blob: d483f1f4de0be687b6d873fc2ab8e5f8d5f828b3 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include "log.h"
#include "rgx.h"
#include "rgx_internal.h"
#include "strfmt.h"
static int savedErrorCode = 0;
static const char *savedErrorMessage = NULL;
static void
saveErrorMessage (int error, const char *message) {
if (message && *message) {
savedErrorCode = error;
savedErrorMessage = message;
}
}
static const char *
getErrorMessage (int error) {
if (error == savedErrorCode) return savedErrorMessage;
return NULL;
}
RGX_CodeType *
rgxCompilePattern (
const RGX_CharacterType *characters, size_t length,
RGX_OptionsType options, RGX_OffsetType *offset,
int *error
) {
const char *message;
RGX_CodeType *code = pcre32_compile2(
characters, options, error, &message, offset, NULL
);
if (!code) saveErrorMessage(*error, message);
return code;
}
void
rgxDeallocateCode (RGX_CodeType *code) {
pcre32_free(code);
}
RGX_DataType *
rgxAllocateData (RGX_CodeType *code) {
RGX_DataType *data;
size_t size = sizeof(*data);
size_t matches = 10;
size_t count = matches * 3;
size += count * sizeof(data->offsets[0]);
if (!(data = malloc(size))) return NULL;
memset(data, 0, size);
data->matches = matches;
data->count = count;
{
const char *message = NULL;
data->study = pcre32_study(code, 0, &message);
if (message) {
logMessage(LOG_WARNING, "pcre study error: %s", message);
if (data->study) {
pcre32_free_study(data->study);
data->study = NULL;
}
}
}
return data;
}
void
rgxDeallocateData (RGX_DataType *data) {
if (data->study) pcre32_free_study(data->study);
free(data);
}
int
rgxMatchText (
const RGX_CharacterType *characters, size_t length,
RGX_CodeType *code, RGX_DataType *data,
RGX_OptionsType options, size_t *count, int *error
) {
int result = pcre32_exec(
code, data->study,
characters, length,
0, options,
data->offsets, data->count
);
if (result < 0) {
*error = result;
return 0;
}
if (!result) result = data->matches;
*count = result - 1;
return 1;
}
int
rgxNameNumber (
RGX_CodeType *code, const RGX_CharacterType *name,
size_t *number, int *error
) {
int result = pcre32_get_stringnumber(code, name);
if (result > 0) {
*number = result;
return 1;
} else {
*error = result;
return 0;
}
}
int
rgxCaptureBounds (
RGX_DataType *data, size_t number, size_t *from, size_t *to
) {
const RGX_OffsetType *offsets = data->offsets;
offsets += number * 2;
if (offsets[0] == -1) return 0;
if (offsets[1] == -1) return 0;
*from = offsets[0];
*to = offsets[1];
return 1;
}
static const char *const rgxNegativeErrors[] = {
[0] = "no error",
[-PCRE_ERROR_NOMATCH] = "no match",
[-PCRE_ERROR_NULL] = "required pointer argument is null",
[-PCRE_ERROR_BADOPTION] = "unrecognized option",
[-PCRE_ERROR_BADMAGIC] = "magic number not found",
[-PCRE_ERROR_UNKNOWN_OPCODE] = "invalid item in compiled pattern",
[-PCRE_ERROR_NOMEMORY] = "insufficient memory",
[-PCRE_ERROR_NOSUBSTRING] = "no capture with specified number or name",
[-PCRE_ERROR_MATCHLIMIT] = "match limit exceeded",
[-PCRE_ERROR_CALLOUT] = "error in callout",
[-PCRE_ERROR_BADUTF32] = "invalid UTF-32 character",
[-PCRE_ERROR_BADUTF16_OFFSET] = "start offset is within a multibyte character",
[-PCRE_ERROR_PARTIAL] = "partial match",
[-PCRE_ERROR_BADPARTIAL] = "pattern contains item not supported for partial match",
[-PCRE_ERROR_INTERNAL] = "internal error",
[-PCRE_ERROR_BADCOUNT] = "size of offsets vector is negative",
[-PCRE_ERROR_DFA_UITEM] = "pattern contains item not supported for DFA match",
[-PCRE_ERROR_DFA_UCOND] = "DFA match uses back reference for condition or test for recursion in specific group",
[-PCRE_ERROR_DFA_UMLIMIT] = "match or recursion limit specified for DFA match",
[-PCRE_ERROR_DFA_WSSIZE] = "DFA workspace overflow",
[-PCRE_ERROR_DFA_RECURSE] = "DFA recursion offsets vector too small",
[-PCRE_ERROR_RECURSIONLIMIT] = "recursion limit exceeded",
[-PCRE_ERROR_NULLWSLIMIT] = "",
[-PCRE_ERROR_BADNEWLINE] = "invalid newline option combination",
[-PCRE_ERROR_BADOFFSET] = "start offset out of bounds",
[-PCRE_ERROR_SHORTUTF16] = "truncated multibyte character",
[-PCRE_ERROR_RECURSELOOP] = "recursion loop detected",
[-PCRE_ERROR_JIT_STACKLIMIT] = "JIT stack too small",
[-PCRE_ERROR_BADMODE] = "pattern compiled for different character size",
[-PCRE_ERROR_BADENDIANNESS] = "pattern compiled for different host endianness",
[-PCRE_ERROR_DFA_BADRESTART] = "unable to resume partial DFA match",
[-PCRE_ERROR_JIT_BADOPTION] = "invalid JIT option",
[-PCRE_ERROR_BADLENGTH] = "text length is negative",
[-PCRE_ERROR_UNSET] = "required value not set",
};
STR_BEGIN_FORMATTER(rgxFormatErrorMessage, int error)
const char *message = getErrorMessage(error);
if (!message) {
if (error <= 0) {
if ((error = -error) < ARRAY_COUNT(rgxNegativeErrors)) {
message = rgxNegativeErrors[error];
}
}
}
if (message && *message) STR_PRINTF("%s", message);
STR_END_FORMATTER
RGX_BEGIN_OPTION_MAP(rgxCompileOptions)
[RGX_COMPILE_ANCHOR_START] = PCRE_ANCHORED,
[RGX_COMPILE_IGNORE_CASE] = PCRE_CASELESS,
[RGX_COMPILE_UNICODE_PROPERTIES] = PCRE_UCP,
RGX_END_OPTION_MAP(rgxCompileOptions)
RGX_BEGIN_OPTION_MAP(rgxMatchOptions)
[RGX_MATCH_ANCHOR_START] = PCRE_ANCHORED,
RGX_END_OPTION_MAP(rgxMatchOptions)