summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/pattern.cc
blob: bcab95a97aacb94f22ad486a9d7b2db0a9c3f0b0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "AppHdr.h"

#ifdef REGEX_PCRE
    // Statically link pcre on Windows
    #if defined(TARGET_OS_WINDOWS)
        #define PCRE_STATIC
    #endif

    #include <pcre.h>
#endif

#ifdef REGEX_POSIX
    #include <regex.h>
#endif

#include "pattern.h"

#if defined(REGEX_PCRE)
////////////////////////////////////////////////////////////////////
// Perl Compatible Regular Expressions

static void *_compile_pattern(const char *pattern, bool icase)
{
    const char *error;
    int erroffset;
    int flags = icase ? PCRE_CASELESS : 0;
    return pcre_compile(pattern,
                        flags,
                        &error,
                        &erroffset,
                        NULL);
}

static void _free_compiled_pattern(void *cp)
{
    if (cp)
        pcre_free(cp);
}

static bool _pattern_match(void *compiled_pattern, const char *text, int length)
{
    int ovector[42];
    int pcre_rc = pcre_exec(static_cast<pcre *>(compiled_pattern),
                            NULL,
                            text, length, 0, 0,
                            ovector, sizeof(ovector) / sizeof(*ovector));
    return pcre_rc >= 0;
}

////////////////////////////////////////////////////////////////////
#else
////////////////////////////////////////////////////////////////////
// POSIX regular expressions

static void *_compile_pattern(const char *pattern, bool icase)
{
    regex_t *re = new regex_t;
    if (!re)
        return NULL;

    int flags = REG_EXTENDED | REG_NOSUB;
    if (icase)
        flags |= REG_ICASE;
    int rc = regcomp(re, pattern, flags);
    // Nonzero return code == failure
    if (rc)
    {
        delete re;
        return NULL;
    }
    return re;
}

static void _free_compiled_pattern(void *cp)
{
    if (cp)
    {
        regex_t *re = static_cast<regex_t *>(cp);
        regfree(re);
        delete re;
    }
}

static bool _pattern_match(void *compiled_pattern, const char *text, int length)
{
    regex_t *re = static_cast<regex_t *>(compiled_pattern);
    return !regexec(re, text, 0, NULL, 0);
}

////////////////////////////////////////////////////////////////////
#endif

text_pattern::~text_pattern()
{
    if (compiled_pattern)
        _free_compiled_pattern(compiled_pattern);
}

const text_pattern &text_pattern::operator= (const text_pattern &tp)
{
    if (this == &tp)
        return tp;

    if (compiled_pattern)
        _free_compiled_pattern(compiled_pattern);
    pattern = tp.pattern;
    compiled_pattern = NULL;
    isvalid      = tp.isvalid;
    ignore_case  = tp.ignore_case;
    return *this;
}

const text_pattern &text_pattern::operator= (const string &spattern)
{
    if (pattern == spattern)
        return *this;

    if (compiled_pattern)
        _free_compiled_pattern(compiled_pattern);
    pattern = spattern;
    compiled_pattern = NULL;
    isvalid = true;
    // We don't change ignore_case
    return *this;
}

bool text_pattern::operator== (const text_pattern &tp) const
{
    if (this == &tp)
        return true;

    return pattern == tp.pattern && ignore_case == tp.ignore_case;
}

bool text_pattern::compile() const
{
    return !empty()?
        !!(compiled_pattern = _compile_pattern(pattern.c_str(), ignore_case))
      : false;
}

bool text_pattern::matches(const char *s, int length) const
{
    return valid() && _pattern_match(compiled_pattern, s, length);
}