mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-02-02 15:07:36 -05:00
Merge branch 'ph3-resourcematch'
This commit is contained in:
commit
cf093b11d7
@ -7,6 +7,7 @@ bin_PROGRAMS = icecast
|
|||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
icecasttypes.h \
|
icecasttypes.h \
|
||||||
admin.h \
|
admin.h \
|
||||||
|
resourcematch.h \
|
||||||
main.h \
|
main.h \
|
||||||
cfgfile.h \
|
cfgfile.h \
|
||||||
logging.h \
|
logging.h \
|
||||||
@ -69,6 +70,7 @@ icecast_SOURCES = \
|
|||||||
xslt.c \
|
xslt.c \
|
||||||
fserve.c \
|
fserve.c \
|
||||||
admin.c \
|
admin.c \
|
||||||
|
resourcematch.c \
|
||||||
md5.c \
|
md5.c \
|
||||||
matchfile.c \
|
matchfile.c \
|
||||||
tls.c \
|
tls.c \
|
||||||
|
86
src/admin.c
86
src/admin.c
@ -118,37 +118,37 @@ static void command_updatemetadata (client_t *client, source_t *source, adm
|
|||||||
static void command_buildm3u (client_t *client, source_t *source, admin_format_t response);
|
static void command_buildm3u (client_t *client, source_t *source, admin_format_t response);
|
||||||
|
|
||||||
static const admin_command_handler_t handlers[] = {
|
static const admin_command_handler_t handlers[] = {
|
||||||
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL }, /* for ACL framework */
|
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL, NULL}, /* for ACL framework */
|
||||||
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback },
|
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback, NULL},
|
||||||
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback },
|
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback, NULL},
|
||||||
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata },
|
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata, NULL},
|
||||||
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata },
|
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata, NULL},
|
||||||
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata },
|
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata, NULL},
|
||||||
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners },
|
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners, NULL},
|
||||||
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners },
|
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners, NULL},
|
||||||
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
|
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
|
||||||
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
|
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
|
||||||
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
|
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
|
||||||
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload },
|
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload, NULL},
|
||||||
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload },
|
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload, NULL},
|
||||||
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
|
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
|
||||||
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
|
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
|
||||||
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
|
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
|
||||||
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts },
|
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts, NULL},
|
||||||
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
|
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
|
||||||
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients },
|
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients, NULL},
|
||||||
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients },
|
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients, NULL},
|
||||||
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client },
|
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client, NULL},
|
||||||
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client },
|
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client, NULL},
|
||||||
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source },
|
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source, NULL},
|
||||||
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source },
|
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source, NULL},
|
||||||
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth },
|
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth, NULL},
|
||||||
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth },
|
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth, NULL},
|
||||||
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata },
|
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata, NULL},
|
||||||
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata },
|
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata, NULL},
|
||||||
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u },
|
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u, NULL},
|
||||||
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
|
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
|
||||||
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats }
|
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
|
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
|
||||||
@ -247,7 +247,7 @@ admin_command_id_t admin_get_command(const char *command)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < table->length; i++)
|
for (i = 0; i < table->length; i++)
|
||||||
if (strcmp(table->handlers[i].route, suffix) == 0)
|
if (resourcematch_match(table->handlers[i].route, suffix, NULL) == RESOURCEMATCH_MATCH)
|
||||||
return admin_get_command_by_table_and_index(table, i);
|
return admin_get_command_by_table_and_index(table, i);
|
||||||
|
|
||||||
return COMMAND_ERROR;
|
return COMMAND_ERROR;
|
||||||
@ -480,7 +480,7 @@ void admin_handle_request(client_t *client, const char *uri)
|
|||||||
handler = admin_get_handler(client->admin_command);
|
handler = admin_get_handler(client->admin_command);
|
||||||
|
|
||||||
/* Check if admin command is valid */
|
/* Check if admin command is valid */
|
||||||
if (handler == NULL || handler->function == NULL) {
|
if (handler == NULL || (handler->function == NULL && handler->function_with_parameters == NULL)) {
|
||||||
ICECAST_LOG_ERROR("Error parsing command string or unrecognised command: %H",
|
ICECAST_LOG_ERROR("Error parsing command string or unrecognised command: %H",
|
||||||
uri);
|
uri);
|
||||||
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||||
@ -545,7 +545,25 @@ void admin_handle_request(client_t *client, const char *uri)
|
|||||||
switch (client->parser->req_type) {
|
switch (client->parser->req_type) {
|
||||||
case httpp_req_get:
|
case httpp_req_get:
|
||||||
case httpp_req_post:
|
case httpp_req_post:
|
||||||
handler->function(client, source, format);
|
if (handler->function) {
|
||||||
|
handler->function(client, source, format);
|
||||||
|
} else {
|
||||||
|
resourcematch_extract_t *extract = NULL;
|
||||||
|
const char *suffix = strchr(uri, '/');
|
||||||
|
|
||||||
|
if (!suffix) {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||||
|
} else {
|
||||||
|
suffix++;
|
||||||
|
|
||||||
|
if (resourcematch_match(handler->route, suffix, &extract) == RESOURCEMATCH_MATCH) {
|
||||||
|
handler->function_with_parameters(client, source, format, extract);
|
||||||
|
resourcematch_extract_free(extract);
|
||||||
|
} else {
|
||||||
|
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case httpp_req_options:
|
case httpp_req_options:
|
||||||
client_send_204(client);
|
client_send_204(client);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "icecasttypes.h"
|
#include "icecasttypes.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "resourcematch.h"
|
||||||
|
|
||||||
/* types */
|
/* types */
|
||||||
#define ADMINTYPE_ERROR (-1)
|
#define ADMINTYPE_ERROR (-1)
|
||||||
@ -31,12 +32,14 @@
|
|||||||
#define ADMIN_COMMAND_ANY ((admin_command_id_t)0) /* for ACL framework */
|
#define ADMIN_COMMAND_ANY ((admin_command_id_t)0) /* for ACL framework */
|
||||||
|
|
||||||
typedef void (*admin_request_function_ptr)(client_t * client, source_t * source, admin_format_t format);
|
typedef void (*admin_request_function_ptr)(client_t * client, source_t * source, admin_format_t format);
|
||||||
|
typedef void (*admin_request_function_with_parameters_ptr)(client_t * client, source_t * source, admin_format_t format, resourcematch_extract_t *parameters);
|
||||||
|
|
||||||
typedef struct admin_command_handler {
|
typedef struct admin_command_handler {
|
||||||
const char *route;
|
const char *route;
|
||||||
const int type;
|
const int type;
|
||||||
const int format;
|
const int format;
|
||||||
const admin_request_function_ptr function;
|
const admin_request_function_ptr function;
|
||||||
|
const admin_request_function_with_parameters_ptr function_with_parameters;
|
||||||
} admin_command_handler_t;
|
} admin_command_handler_t;
|
||||||
|
|
||||||
void admin_handle_request(client_t *client, const char *uri);
|
void admin_handle_request(client_t *client, const char *uri);
|
||||||
|
191
src/resourcematch.c
Normal file
191
src/resourcematch.c
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "resourcematch.h"
|
||||||
|
|
||||||
|
static size_t count_groups(const char *pattern)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
|
||||||
|
while (*pattern) {
|
||||||
|
for (; *pattern && *pattern != '%'; pattern++);
|
||||||
|
|
||||||
|
if (!*pattern) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern++;
|
||||||
|
|
||||||
|
if (!*pattern)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (*pattern != '%')
|
||||||
|
ret++;
|
||||||
|
|
||||||
|
pattern++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static resourcematch_extract_t * allocate_extract(const char *pattern)
|
||||||
|
{
|
||||||
|
size_t groups = count_groups(pattern);
|
||||||
|
resourcematch_extract_t *ret;
|
||||||
|
|
||||||
|
ret = calloc(1, sizeof(*ret));
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret->groups = groups;
|
||||||
|
ret->group = calloc(groups, sizeof(*ret->group));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strip_common_prefix(const char **pattern, const char **string)
|
||||||
|
{
|
||||||
|
const char *p = *pattern;
|
||||||
|
const char *s = *string;
|
||||||
|
|
||||||
|
for (; *p && *p != '%' && *p == *s; p++, s++);
|
||||||
|
|
||||||
|
*pattern = p;
|
||||||
|
*string = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void setup_group(resourcematch_extract_t *extract, size_t idx, char type)
|
||||||
|
{
|
||||||
|
if (!extract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
extract->group[idx].type = type;
|
||||||
|
extract->group[idx].raw = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline resourcematch_result_t match_lli(const char **string, resourcematch_extract_t *extract, size_t idx, int base)
|
||||||
|
{
|
||||||
|
long long int ret;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
ret = strtoll(*string, &endptr, base);
|
||||||
|
if (errno != 0)
|
||||||
|
return RESOURCEMATCH_ERROR;
|
||||||
|
|
||||||
|
if (extract) {
|
||||||
|
extract->group[idx].result.lli = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*string = endptr;
|
||||||
|
|
||||||
|
return RESOURCEMATCH_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract)
|
||||||
|
{
|
||||||
|
resourcematch_result_t ret;
|
||||||
|
resourcematch_extract_t *matches = NULL;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
if (!pattern || !string)
|
||||||
|
return RESOURCEMATCH_ERROR;
|
||||||
|
|
||||||
|
if (extract) {
|
||||||
|
matches = allocate_extract(pattern);
|
||||||
|
if (!matches)
|
||||||
|
return RESOURCEMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
strip_common_prefix(&pattern, &string);
|
||||||
|
|
||||||
|
if (!*pattern && !*string) {
|
||||||
|
if (extract)
|
||||||
|
*extract = matches;
|
||||||
|
|
||||||
|
return RESOURCEMATCH_MATCH;
|
||||||
|
} else if (!*pattern || !*string) {
|
||||||
|
if (extract)
|
||||||
|
resourcematch_extract_free(matches);
|
||||||
|
|
||||||
|
return RESOURCEMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pattern != '%') {
|
||||||
|
if (extract)
|
||||||
|
resourcematch_extract_free(matches);
|
||||||
|
|
||||||
|
return RESOURCEMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern++;
|
||||||
|
|
||||||
|
switch (*pattern) {
|
||||||
|
case '%':
|
||||||
|
if (*string == '%') {
|
||||||
|
string++;
|
||||||
|
} else {
|
||||||
|
if (extract)
|
||||||
|
resourcematch_extract_free(matches);
|
||||||
|
|
||||||
|
return RESOURCEMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#define _test_int(type,base) \
|
||||||
|
case (type): \
|
||||||
|
setup_group(matches, idx, *pattern); \
|
||||||
|
\
|
||||||
|
ret = match_lli(&string, matches, idx, (base)); \
|
||||||
|
if (ret != RESOURCEMATCH_MATCH) { \
|
||||||
|
if (extract) \
|
||||||
|
resourcematch_extract_free(matches); \
|
||||||
|
\
|
||||||
|
return ret; \
|
||||||
|
} \
|
||||||
|
idx++; \
|
||||||
|
break;
|
||||||
|
|
||||||
|
_test_int('i', 0);
|
||||||
|
_test_int('d', 10);
|
||||||
|
_test_int('x', 16);
|
||||||
|
_test_int('o', 8);
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (extract)
|
||||||
|
resourcematch_extract_free(matches);
|
||||||
|
|
||||||
|
return RESOURCEMATCH_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resourcematch_extract_free(resourcematch_extract_t *extract)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!extract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < extract->groups; i++) {
|
||||||
|
free(extract->group[i].raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(extract->group);
|
||||||
|
free(extract);
|
||||||
|
}
|
37
src/resourcematch.h
Normal file
37
src/resourcematch.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RESOURCEMATCH_H__
|
||||||
|
#define __RESOURCEMATCH_H__
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RESOURCEMATCH_ERROR,
|
||||||
|
RESOURCEMATCH_MATCH,
|
||||||
|
RESOURCEMATCH_NOMATCH
|
||||||
|
} resourcematch_result_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char type;
|
||||||
|
char *raw;
|
||||||
|
union {
|
||||||
|
const char *string;
|
||||||
|
long long int lli;
|
||||||
|
} result;
|
||||||
|
} resourcematch_group_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t groups;
|
||||||
|
resourcematch_group_t *group;
|
||||||
|
} resourcematch_extract_t;
|
||||||
|
|
||||||
|
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract);
|
||||||
|
void resourcematch_extract_free(resourcematch_extract_t *extract);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user