blob: c8fa5ba2bcdd0c239919c9b4e18c4ab2a1b4b822 [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
static void *njs_pcre_malloc(size_t size);
static void njs_pcre_free(void *p);
static void *njs_pcre_default_malloc(size_t size, void *memory_data);
static void njs_pcre_default_free(void *p, void *memory_data);
static njs_regex_context_t *regex_context;
njs_regex_context_t *
njs_regex_context_create(njs_pcre_malloc_t private_malloc,
njs_pcre_free_t private_free, void *memory_data)
{
njs_regex_context_t *ctx;
if (private_malloc == NULL) {
private_malloc = njs_pcre_default_malloc;
private_free = njs_pcre_default_free;
}
ctx = private_malloc(sizeof(njs_regex_context_t), memory_data);
if (njs_fast_path(ctx != NULL)) {
ctx->private_malloc = private_malloc;
ctx->private_free = private_free;
ctx->memory_data = memory_data;
}
return ctx;
}
njs_int_t
njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len,
njs_uint_t options, njs_regex_context_t *ctx)
{
int ret, err, erroff;
char *pattern, *error;
void *(*saved_malloc)(size_t size);
void (*saved_free)(void *p);
const char *errstr;
ret = NJS_ERROR;
saved_malloc = pcre_malloc;
pcre_malloc = njs_pcre_malloc;
saved_free = pcre_free;
pcre_free = njs_pcre_free;
regex_context = ctx;
if (len == 0) {
pattern = (char *) source;
} else {
pattern = ctx->private_malloc(len + 1, ctx->memory_data);
if (njs_slow_path(pattern == NULL)) {
goto done;
}
memcpy(pattern, source, len);
pattern[len] = '\0';
}
regex->code = pcre_compile(pattern, options, &errstr, &erroff, NULL);
if (njs_slow_path(regex->code == NULL)) {
error = pattern + erroff;
if (*error != '\0') {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_compile(\"%s\") failed: %s at \"%s\"",
pattern, errstr, error);
} else {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_compile(\"%s\") failed: %s", pattern, errstr);
}
ret = NJS_DECLINED;
goto done;
}
regex->extra = pcre_study(regex->code, 0, &errstr);
if (njs_slow_path(errstr != NULL)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_study(\"%s\") failed: %s", pattern, errstr);
goto done;
}
err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_CAPTURECOUNT,
&regex->ncaptures);
if (njs_slow_path(err < 0)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_fullinfo(\"%s\", PCRE_INFO_CAPTURECOUNT) failed: %d",
pattern, err);
goto done;
}
err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_BACKREFMAX,
&regex->backrefmax);
if (njs_slow_path(err < 0)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_fullinfo(\"%s\", PCRE_INFO_BACKREFMAX) failed: %d",
pattern, err);
goto done;
}
/* Reserve additional elements for the first "$0" capture. */
regex->ncaptures++;
if (regex->ncaptures > 1) {
err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMECOUNT,
&regex->nentries);
if (njs_slow_path(err < 0)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR,
"pcre_fullinfo(\"%s\", PCRE_INFO_NAMECOUNT) failed: %d",
pattern, err);
goto done;
}
if (regex->nentries != 0) {
err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMEENTRYSIZE,
&regex->entry_size);
if (njs_slow_path(err < 0)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", "
"PCRE_INFO_NAMEENTRYSIZE) failed: %d", pattern, err);
goto done;
}
err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMETABLE,
&regex->entries);
if (njs_slow_path(err < 0)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", "
"PCRE_INFO_NAMETABLE) failed: %d", pattern, err);
goto done;
}
}
}
ret = NJS_OK;
done:
pcre_malloc = saved_malloc;
pcre_free = saved_free;
regex_context = NULL;
return ret;
}
njs_bool_t
njs_regex_is_valid(njs_regex_t *regex)
{
return (regex->code != NULL);
}
njs_uint_t
njs_regex_ncaptures(njs_regex_t *regex)
{
return regex->ncaptures;
}
njs_uint_t
njs_regex_backrefs(njs_regex_t *regex)
{
return regex->backrefmax;
}
njs_int_t
njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n)
{
char *entry;
if (name == NULL) {
return regex->nentries;
}
if (n >= regex->nentries) {
return NJS_ERROR;
}
entry = regex->entries + regex->entry_size * n;
name->start = (u_char *) entry + 2;
name->length = njs_strlen(name->start);
return (entry[0] << 8) + entry[1];
}
njs_regex_match_data_t *
njs_regex_match_data(njs_regex_t *regex, njs_regex_context_t *ctx)
{
size_t size;
njs_uint_t ncaptures;
njs_regex_match_data_t *match_data;
if (regex != NULL) {
ncaptures = regex->ncaptures - 1;
} else {
ncaptures = 0;
}
/* Each capture is stored in 3 "int" vector elements. */
ncaptures *= 3;
size = sizeof(njs_regex_match_data_t) + ncaptures * sizeof(int);
match_data = ctx->private_malloc(size, ctx->memory_data);
if (njs_fast_path(match_data != NULL)) {
match_data->ncaptures = ncaptures + 3;
}
return match_data;
}
void
njs_regex_match_data_free(njs_regex_match_data_t *match_data,
njs_regex_context_t *ctx)
{
ctx->private_free(match_data, ctx->memory_data);
}
static void *
njs_pcre_malloc(size_t size)
{
return regex_context->private_malloc(size, regex_context->memory_data);
}
static void
njs_pcre_free(void *p)
{
regex_context->private_free(p, regex_context->memory_data);
}
static void *
njs_pcre_default_malloc(size_t size, void *memory_data)
{
return malloc(size);
}
static void
njs_pcre_default_free(void *p, void *memory_data)
{
free(p);
}
njs_int_t
njs_regex_match(njs_regex_t *regex, const u_char *subject, size_t off,
size_t len, njs_regex_match_data_t *match_data, njs_regex_context_t *ctx)
{
int ret;
ret = pcre_exec(regex->code, regex->extra, (const char *) subject, len,
off, 0, match_data->captures, match_data->ncaptures);
/* PCRE_ERROR_NOMATCH is -1. */
if (njs_slow_path(ret < PCRE_ERROR_NOMATCH)) {
njs_alert(ctx->trace, NJS_LEVEL_ERROR, "pcre_exec() failed: %d", ret);
}
return ret;
}
int *
njs_regex_captures(njs_regex_match_data_t *match_data)
{
return match_data->captures;
}