ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
pcre2.c File Reference

Centralized PCRE2 singleton pattern for efficient regex compilation. More...

Go to the source code of this file.

Data Structures

struct  pcre2_singleton
 Represents a thread-safe compiled PCRE2 regex singleton. More...
 

Typedefs

typedef struct pcre2_singleton pcre2_singleton_t
 Represents a thread-safe compiled PCRE2 regex singleton.
 

Functions

pcre2_code * asciichat_pcre2_singleton_get_code (pcre2_singleton_t *singleton)
 Get the compiled pcre2_code from a singleton handle.
 
bool asciichat_pcre2_singleton_is_initialized (pcre2_singleton_t *singleton)
 Check if a singleton was successfully initialized.
 
void asciichat_pcre2_singleton_free (pcre2_singleton_t *singleton)
 Free a PCRE2 singleton and its resources.
 
void asciichat_pcre2_cleanup_all (void)
 Free all PCRE2 singletons in the global registry.
 
char * asciichat_pcre2_extract_named_group (pcre2_code *regex, pcre2_match_data *match_data, const char *group_name, const char *subject)
 Extract named substring from PCRE2 match data.
 
char * asciichat_pcre2_extract_group (pcre2_match_data *match_data, int group_num, const char *subject)
 Extract numbered capture group as allocated string.
 
const char * asciichat_pcre2_extract_group_ptr (pcre2_match_data *match_data, int group_num, const char *subject, size_t *out_len)
 Extract numbered capture group as pointer into subject (non-allocating)
 
bool asciichat_pcre2_extract_group_ulong (pcre2_match_data *match_data, int group_num, const char *subject, unsigned long *out_value)
 Extract numbered capture group and convert to unsigned long.
 

Detailed Description

Centralized PCRE2 singleton pattern for efficient regex compilation.

Definition in file pcre2.c.

Typedef Documentation

◆ pcre2_singleton_t

Represents a thread-safe compiled PCRE2 regex singleton.

Uses atomic flag for thread-safe lazy initialization without mutexes. The compilation flag is set only once, and the compiled code is read-only after that, enabling concurrent access from multiple threads.

Function Documentation

◆ asciichat_pcre2_cleanup_all()

void asciichat_pcre2_cleanup_all ( void  )

Free all PCRE2 singletons in the global registry.

Walks the global registry and frees all singletons. Safe to call multiple times (idempotent). Should be called once during shutdown.

Definition at line 197 of file pcre2.c.

197 {
198 if (!atomic_load(&g_registry_initialized)) {
199 return; /* No singletons were ever created or already cleaned up */
200 }
201
202 /* Check if already cleaned up */
203 if (g_singleton_registry == NULL) {
204 return;
205 }
206
207 pcre2_singleton_t *current = g_singleton_registry;
208
209 /* Clear registry first to prevent re-entry */
210 g_singleton_registry = NULL;
211 atomic_store(&g_registry_initialized, false);
212
213 /* Now free all singletons */
214 while (current) {
215 pcre2_singleton_t *next = current->next;
216
217 /* Free compiled code */
218 pcre2_code *code = atomic_load(&current->code);
219 if (code) {
220 pcre2_code_free(code);
221 atomic_store(&current->code, NULL);
222 }
223
224 /* Free JIT stack */
225 if (current->jit_stack) {
226 pcre2_jit_stack_free(current->jit_stack);
227 current->jit_stack = NULL;
228 }
229
230 /* Free pattern string */
231 SAFE_FREE(current->pattern);
232
233 /* Free singleton structure */
234 SAFE_FREE(current);
235
236 current = next;
237 }
238}
Represents a thread-safe compiled PCRE2 regex singleton.
Definition pcre2.c:21

Referenced by asciichat_shared_destroy().

◆ asciichat_pcre2_extract_group()

char * asciichat_pcre2_extract_group ( pcre2_match_data *  match_data,
int  group_num,
const char *  subject 
)

Extract numbered capture group as allocated string.

Definition at line 291 of file pcre2.c.

291 {
292 if (!match_data || !subject || group_num < 0) {
293 return NULL;
294 }
295
296 /* Get ovector (output vector with match offsets) */
297 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
298 PCRE2_SIZE start = ovector[2 * group_num];
299 PCRE2_SIZE end = ovector[2 * group_num + 1];
300
301 if (start == PCRE2_UNSET || end == PCRE2_UNSET) {
302 return NULL; /* Group not matched */
303 }
304
305 /* Allocate and copy substring */
306 size_t len = end - start;
307 char *result = SAFE_MALLOC(len + 1, char *);
308 if (!result) {
309 log_error("asciichat_pcre2_extract_group: failed to allocate %zu bytes", len + 1);
310 return NULL;
311 }
312
313 memcpy(result, subject + start, len);
314 result[len] = '\0';
315
316 return result;
317}

◆ asciichat_pcre2_extract_group_ptr()

const char * asciichat_pcre2_extract_group_ptr ( pcre2_match_data *  match_data,
int  group_num,
const char *  subject,
size_t *  out_len 
)

Extract numbered capture group as pointer into subject (non-allocating)

Definition at line 322 of file pcre2.c.

323 {
324 if (!match_data || !subject || !out_len || group_num < 0) {
325 return NULL;
326 }
327
328 /* Get ovector (output vector with match offsets) */
329 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
330 PCRE2_SIZE start = ovector[2 * group_num];
331 PCRE2_SIZE end = ovector[2 * group_num + 1];
332
333 if (start == PCRE2_UNSET || end == PCRE2_UNSET) {
334 return NULL; /* Group not matched */
335 }
336
337 *out_len = end - start;
338 return subject + start;
339}

Referenced by stun_servers_parse().

◆ asciichat_pcre2_extract_group_ulong()

bool asciichat_pcre2_extract_group_ulong ( pcre2_match_data *  match_data,
int  group_num,
const char *  subject,
unsigned long *  out_value 
)

Extract numbered capture group and convert to unsigned long.

Definition at line 344 of file pcre2.c.

345 {
346 if (!match_data || !subject || !out_value || group_num < 0) {
347 return false;
348 }
349
350 /* Get ovector (output vector with match offsets) */
351 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
352 PCRE2_SIZE start = ovector[2 * group_num];
353 PCRE2_SIZE end = ovector[2 * group_num + 1];
354
355 if (start == PCRE2_UNSET || end == PCRE2_UNSET) {
356 return false; /* Group not matched */
357 }
358
359 /* Extract substring into temporary buffer */
360 size_t len = end - start;
361 if (len == 0 || len > 63) { /* Sanity check for reasonable number length */
362 return false;
363 }
364
365 char temp[64];
366 memcpy(temp, subject + start, len);
367 temp[len] = '\0';
368
369 /* Parse as unsigned long */
370 char *endptr;
371 errno = 0;
372 unsigned long value = strtoul(temp, &endptr, 10);
373
374 if (errno != 0 || endptr == temp || *endptr != '\0') {
375 return false; /* Parse failed */
376 }
377
378 *out_value = value;
379 return true;
380}

◆ asciichat_pcre2_extract_named_group()

char * asciichat_pcre2_extract_named_group ( pcre2_code *  regex,
pcre2_match_data *  match_data,
const char *  group_name,
const char *  subject 
)

Extract named substring from PCRE2 match data.

Extracts a named capture group from match data and returns an allocated string. The caller is responsible for freeing the returned string with SAFE_FREE().

Parameters
regexCompiled PCRE2 regex with named groups
match_dataMatch data from pcre2_match() or pcre2_jit_match()
group_nameName of the capture group to extract
subjectOriginal subject string that was matched
Returns
Allocated string containing the matched substring, or NULL if group not matched

Definition at line 252 of file pcre2.c.

253 {
254 if (!regex || !match_data || !group_name || !subject) {
255 log_error("pcre2_extract_named_group: invalid parameters");
256 return NULL;
257 }
258
259 /* Get group number from name */
260 int group_number = pcre2_substring_number_from_name(regex, (PCRE2_SPTR)group_name);
261 if (group_number < 0) {
262 return NULL; /* Group doesn't exist */
263 }
264
265 /* Get ovector (output vector with match offsets) */
266 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
267 PCRE2_SIZE start = ovector[2 * group_number];
268 PCRE2_SIZE end = ovector[2 * group_number + 1];
269
270 if (start == PCRE2_UNSET || end == PCRE2_UNSET) {
271 return NULL; /* Group not matched */
272 }
273
274 /* Allocate and copy substring */
275 size_t len = end - start;
276 char *result = SAFE_MALLOC(len + 1, char *);
277 if (!result) {
278 log_error("pcre2_extract_named_group: failed to allocate %zu bytes", len + 1);
279 return NULL;
280 }
281
282 memcpy(result, subject + start, len);
283 result[len] = '\0';
284
285 return result;
286}

Referenced by crypto_regex_extract_gpg_keygrip(), crypto_regex_extract_pem_base64(), crypto_regex_match_known_hosts(), crypto_regex_match_public_key(), and url_parse().

◆ asciichat_pcre2_singleton_free()

void asciichat_pcre2_singleton_free ( pcre2_singleton_t singleton)

Free a PCRE2 singleton and its resources.

Frees the compiled regex code, JIT stack, and singleton structure. After calling this, the singleton pointer is invalid and should not be used.

Parameters
singletonHandle returned by asciichat_pcre2_singleton_compile()

Definition at line 168 of file pcre2.c.

168 {
169 if (!singleton) {
170 return;
171 }
172
173 /* Free compiled code if it exists */
174 pcre2_code *code = atomic_load(&singleton->code);
175 if (code) {
176 pcre2_code_free(code);
177 }
178
179 /* Free JIT stack if it exists */
180 if (singleton->jit_stack) {
181 pcre2_jit_stack_free(singleton->jit_stack);
182 }
183
184 /* Free pattern string */
185 SAFE_FREE(singleton->pattern);
186
187 /* Free the singleton structure itself */
188 SAFE_FREE(singleton);
189}

◆ asciichat_pcre2_singleton_get_code()

pcre2_code * asciichat_pcre2_singleton_get_code ( pcre2_singleton_t singleton)

Get the compiled pcre2_code from a singleton handle.

Lazily compiles the regex on first call. Fast path (subsequent calls) only reads a single atomic variable with no synchronization overhead.

Thread-safe: If multiple threads call this concurrently on first use, only one will compile; others will wait for the code to become non-NULL.

Parameters
singletonHandle returned by asciichat_pcre2_singleton_compile()
Returns
Compiled regex code (read-only), or NULL if compilation failed

Definition at line 95 of file pcre2.c.

95 {
96 if (!singleton) {
97 return NULL;
98 }
99
100 /* Fast path: check if already compiled (lock-free atomic load, no mutex) */
101 pcre2_code *code = atomic_load(&singleton->code);
102 if (code != NULL) {
103 return code;
104 }
105
106 /* If compilation was already attempted (code freed during cleanup, or compilation
107 * failed), don't recompile. Prevents re-entrant allocation during shutdown. */
108 if (atomic_load(&singleton->compiled)) {
109 return NULL;
110 }
111
112 /* Slow path: first call, need to compile. */
113 int errornumber;
114 PCRE2_SIZE erroroffset;
115
116 code = pcre2_compile((PCRE2_SPTR8)singleton->pattern, PCRE2_ZERO_TERMINATED, singleton->flags, &errornumber,
117 &erroroffset, NULL);
118
119 if (!code) {
120 PCRE2_UCHAR error_buf[256];
121 pcre2_get_error_message(errornumber, error_buf, sizeof(error_buf));
122 log_warn("Failed to compile PCRE2 regex at offset %zu: %s", erroroffset, (const char *)error_buf);
123 /* Mark as "attempted" by setting a sentinel value (store NULL explicitly) */
124 atomic_store(&singleton->compiled, true);
125 return NULL;
126 }
127
128 /* Attempt JIT compilation (non-fatal if fails) */
129 int jit_rc = pcre2_jit_compile(code, PCRE2_JIT_COMPLETE);
130 if (jit_rc < 0) {
131 log_debug("PCRE2 JIT compilation not available (code %d), using interpreted mode", jit_rc);
132 } else {
133 /* Create JIT stack for pattern execution */
134 singleton->jit_stack = pcre2_jit_stack_create(32 * 1024, 512 * 1024, NULL);
135 if (!singleton->jit_stack) {
136 log_warn("Failed to create JIT stack for PCRE2 regex");
137 }
138 }
139
140 /* Store compiled code (atomic, so subsequent calls see it immediately) */
141 atomic_store(&singleton->code, code);
142 atomic_store(&singleton->compiled, true);
143
144 return code;
145}

Referenced by grep_highlight(), grep_highlight_colored(), grep_init(), grep_should_output(), interactive_grep_enter_mode(), interactive_grep_exit_mode(), interactive_grep_gather_and_filter_logs(), interactive_grep_get_match_info(), and interactive_grep_handle_key().

◆ asciichat_pcre2_singleton_is_initialized()

bool asciichat_pcre2_singleton_is_initialized ( pcre2_singleton_t singleton)

Check if a singleton was successfully initialized.

Parameters
singletonHandle returned by asciichat_pcre2_singleton_compile()
Returns
true if regex compiled successfully, false otherwise

Definition at line 153 of file pcre2.c.

153 {
154 if (!singleton) {
155 return false;
156 }
157 return atomic_load(&singleton->code) != NULL;
158}