Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_pcre.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#include "nilorea/n_pcre.h"
28#include "nilorea/n_log.h"
29
30#include <string.h>
31
32/*
33 * This module was originally written for legacy PCRE (pcre.h).
34 * It has been migrated to PCRE2 (pcre2.h) using the 8-bit API.
35 */
36
80N_PCRE* npcre_new(char* str, int flags) {
81 N_PCRE* pcre = NULL;
82 __n_assert(str, return NULL);
83
84 Malloc(pcre, N_PCRE, 1);
85 __n_assert(pcre, return NULL);
86
87 pcre->captured = 0;
88 pcre->match_list = NULL;
89 init_lock(pcre->rwlock);
90
91 pcre->regexp_str = strdup(str);
92 __n_assert(pcre->regexp_str, npcre_delete(&pcre); return NULL);
93
94 int errorcode = 0;
95 PCRE2_SIZE erroroffset = 0;
96
97 pcre->regexp = pcre2_compile(
98 (PCRE2_SPTR)str,
99 PCRE2_ZERO_TERMINATED,
100 (uint32_t)flags,
101 &errorcode,
102 &erroroffset,
103 NULL);
104
105 if (!pcre->regexp) {
106 PCRE2_UCHAR errmsg[256];
107 (void)pcre2_get_error_message(errorcode, errmsg, sizeof(errmsg));
108 n_log(LOG_ERR, "pcre2 compilation of %s failed at offset %zu : %s", str, (size_t)erroroffset, (char*)errmsg);
109 npcre_delete(&pcre);
110 return NULL;
111 }
112
113 /* Allocate match_data sized for this pattern (captures, etc.) */
114 pcre->match_data = pcre2_match_data_create_from_pattern(pcre->regexp, NULL);
115 if (!pcre->match_data) {
116 n_log(LOG_ERR, "pcre2_match_data_create_from_pattern failed (OOM?)");
117 npcre_delete(&pcre);
118 return NULL;
119 }
120
121 /* Best-effort JIT compilation (optional). Ignore failures. */
122 (void)pcre2_jit_compile(pcre->regexp, PCRE2_JIT_COMPLETE);
123
124 return pcre;
125} /* npcre_new(...) */
126
134int npcre_delete(N_PCRE** pcre) {
135 __n_assert(pcre && (*pcre), return FALSE);
136
137 write_lock((*pcre)->rwlock);
138 if ((*pcre)->captured > 0 && (*pcre)->match_list) {
139 pcre2_substring_list_free(PCRE2_LIST_FREE_CAST((*pcre)->match_list));
140 (*pcre)->match_list = NULL;
141 (*pcre)->captured = 0;
142 }
143
144 if ((*pcre)->match_data) {
145 pcre2_match_data_free((*pcre)->match_data);
146 (*pcre)->match_data = NULL;
147 }
148 if ((*pcre)->regexp) {
149 pcre2_code_free((*pcre)->regexp);
150 (*pcre)->regexp = NULL;
151 }
152 FreeNoLog((*pcre)->regexp_str);
153 unlock((*pcre)->rwlock);
154 rw_lock_destroy((*pcre)->rwlock);
155 FreeNoLog((*pcre));
156 return TRUE;
157} /* npcre_delete (...) */
158
165 __n_assert(pcre, return FALSE);
166
167 write_lock(pcre->rwlock);
168 if (pcre->match_list) {
169 pcre->captured = 0;
170 pcre2_substring_list_free(PCRE2_LIST_FREE_CAST(pcre->match_list));
171 pcre->match_list = NULL;
172 }
173 unlock(pcre->rwlock);
174
175 return TRUE;
176} /* npcre_clean_match */
177
187int _npcre_print_error(int LOG_LEVEL, const char* file, const char* func, int line, int rc) {
188 /*
189 * PCRE2 error code names differ from legacy PCRE, and not all historical
190 * constants exist in all packaged versions.
191 *
192 * The portable way is to ask PCRE2 for the human-readable message.
193 */
194
195 if (rc > 0) {
196 _n_log(LOG_DEBUG, file, func, line, "no pcre error, match: %d elements in ovector", rc);
197 return TRUE;
198 }
199
200 if (rc == 0) {
201 _n_log(LOG_LEVEL, file, func, line, "match OK, but not enough space in ovector to store all elements");
202 return FALSE;
203 }
204
205 PCRE2_UCHAR errmsg[256];
206 PCRE2_SIZE cap = sizeof(errmsg) / sizeof(errmsg[0]);
207
208 int mrc = pcre2_get_error_message(rc, errmsg, cap);
209 if (mrc >= 0) {
210 _n_log(LOG_LEVEL, file, func, line, "PCRE2 error %d: %s", rc, (const char*)errmsg);
211 } else {
212 _n_log(LOG_LEVEL, file, func, line, "Unknown PCRE2 error code: %d", rc);
213 }
214
215 return FALSE;
216}
217
226int npcre_match(char* str, N_PCRE* pcre) {
227 __n_assert(str, return FALSE);
228 __n_assert(pcre, return FALSE);
229 __n_assert(pcre->regexp, return FALSE);
230
231 pcre2_match_data* local_match_data = pcre2_match_data_create_from_pattern(pcre->regexp, NULL);
232 if (!local_match_data) {
233 n_log(LOG_ERR, "npcre_match: pcre2_match_data_create_from_pattern failed");
234 return FALSE;
235 }
236
237 int rc = (int)pcre2_match(
238 pcre->regexp,
239 (PCRE2_SPTR)str,
240 (PCRE2_SIZE)strlen(str),
241 0,
242 0,
243 local_match_data,
244 NULL);
245
246 pcre2_match_data_free(local_match_data);
247
248 return (rc > 0) ? TRUE : FALSE;
249} /* npcre_match(...) */
250
261int npcre_match_capture(char* str, N_PCRE* pcre) {
262 __n_assert(str, return FALSE);
263 __n_assert(pcre, return FALSE);
264 __n_assert(pcre->regexp_str, return FALSE);
265 __n_assert(pcre->regexp, return FALSE);
266
267 write_lock(pcre->rwlock);
268
269 int rc = 0;
270 size_t len = strlen(str);
271
272 rc = (int)pcre2_match(
273 pcre->regexp,
274 (PCRE2_SPTR)str,
275 (PCRE2_SIZE)len,
276 0,
277 0,
278 pcre->match_data,
279 NULL);
280
281 if (rc < 0) {
282 unlock(pcre->rwlock);
283 switch (rc) {
284 case PCRE2_ERROR_NOMATCH: /* n_log( LOG_DEBUG , "String did not match the pattern");*/
285 break;
286 case PCRE2_ERROR_NULL:
287 n_log(LOG_DEBUG, "Something was null");
288 break;
289 case PCRE2_ERROR_BADOPTION:
290 n_log(LOG_DEBUG, "A bad option was passed");
291 break;
292 case PCRE2_ERROR_BADMAGIC:
293 n_log(LOG_DEBUG, "Magic number bad (compiled re corrupt?)");
294 break;
295 case PCRE2_ERROR_NOMEMORY:
296 n_log(LOG_DEBUG, "Ran out of memory");
297 break;
298 default:
299 n_log(LOG_DEBUG, "Unknown error");
300 break;
301 }
302 return FALSE;
303 }
304
305 if (rc > 0) {
306 /* clean previous match results (already under lock, call internal logic directly) */
307 if (pcre->match_list) {
308 pcre->captured = 0;
309 pcre2_substring_list_free(PCRE2_LIST_FREE_CAST(pcre->match_list));
310 pcre->match_list = NULL;
311 }
312 /* lengthsptr Where to put a pointer to the lengths, or NULL */
313 PCRE2_SIZE* lens = NULL;
314 /* list is NULL-terminated and must be freed with pcre2_substring_list_free() */
315 int lrc = pcre2_substring_list_get(pcre->match_data, &pcre->match_list, &lens);
316 if (lrc != 0) {
317 _npcre_print_error(LOG_ERR, __FILE__, __func__, __LINE__, lrc);
318 pcre->match_list = NULL;
319 pcre->captured = 0;
320 unlock(pcre->rwlock);
321 return FALSE;
322 }
323 pcre->captured = rc;
324 }
325 unlock(pcre->rwlock);
326 return TRUE;
327} /* npcre_match_capture(...) */
#define init_lock(__rwlock_mutex)
Macro for initializing a rwlock.
Definition n_common.h:349
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:271
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:203
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:278
#define rw_lock_destroy(__rwlock_mutex)
Macro to destroy rwlock mutex.
Definition n_common.h:408
#define unlock(__rwlock_mutex)
Macro for releasing read/write lock a rwlock mutex.
Definition n_common.h:395
#define write_lock(__rwlock_mutex)
Macro for acquiring a write lock on a rwlock mutex.
Definition n_common.h:381
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
void _n_log(int level, const char *file, const char *func, int line, const char *format,...)
Logging function.
Definition n_log.c:267
PCRE2_UCHAR8 ** match_list
populated match list (NULL-terminated) after npcre_match_capture() Allocated by PCRE2 via pcre2_subst...
Definition n_pcre.h:104
pcre2_code * regexp
regexp
Definition n_pcre.h:97
pcre2_match_data * match_data
match data storage (ovector, etc.)
Definition n_pcre.h:99
char * regexp_str
original regexp string
Definition n_pcre.h:95
pthread_rwlock_t rwlock
rwlock protecting match_data, match_list, and captured
Definition n_pcre.h:108
int captured
flag for match_list cleaning
Definition n_pcre.h:106
N_PCRE * npcre_new(char *str, int flags)
From pcre doc, the flag bits are: PCRE_ANCHORED Force pattern anchoring PCRE_AUTO_CALLOUT Compile aut...
Definition n_pcre.c:80
int _npcre_print_error(int LOG_LEVEL, const char *file, const char *func, int line, int rc)
print error associated to pcre error code
Definition n_pcre.c:187
int npcre_match_capture(char *str, N_PCRE *pcre)
Return TRUE if str matches regexp, and make captures.
Definition n_pcre.c:261
int npcre_match(char *str, N_PCRE *pcre)
Thread-safe match: returns TRUE/FALSE without modifying the N_PCRE struct.
Definition n_pcre.c:226
int npcre_clean_match(N_PCRE *pcre)
clean the match list of the last capture, if any
Definition n_pcre.c:164
int npcre_delete(N_PCRE **pcre)
Free a N_PCRE pointer.
Definition n_pcre.c:134
N_PCRE structure.
Definition n_pcre.h:93
static int LOG_LEVEL
static global maximum wanted log level value
Definition n_log.c:77
Generic log system.
PCRE helpers for regex matching.
#define PCRE2_LIST_FREE_CAST(x)
Definition n_pcre.h:78