1/* $OpenBSD: fty_regex.c,v 1.10 2023/10/17 09:52:10 nicm Exp $ */ 2/**************************************************************************** 3 * Copyright 2018-2020,2021 Thomas E. Dickey * 4 * Copyright 1998-2012,2015 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31/*************************************************************************** 32* * 33* Author : Juergen Pfeifer * 34* * 35***************************************************************************/ 36 37#include "form.priv.h" 38 39MODULE_ID("$Id: fty_regex.c,v 1.10 2023/10/17 09:52:10 nicm Exp $") 40 41#if HAVE_REGEX_H_FUNCS || HAVE_LIB_PCRE2 /* We prefer POSIX regex */ 42 43#if HAVE_PCRE2POSIX_H 44#include <pcre2posix.h> 45 46/* pcre2 used to provide its "POSIX" entrypoints using the same names as the 47 * standard ones in the C runtime, but that never worked because the linker 48 * would use the C runtime. Debian patched the library to fix this symbol 49 * conflict, but overlooked the header file, and Debian's patch was made 50 * obsolete when pcre2 was changed early in 2019 to provide different names. 51 * 52 * Here is a workaround to make the older version of Debian's package work. 53 */ 54#if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP) 55 56#undef regcomp 57#undef regexec 58#undef regfree 59 60#ifdef __cplusplus 61extern "C" 62{ 63#endif 64 PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int); 65 PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t, 66 regmatch_t *, int); 67 PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *); 68#ifdef __cplusplus 69} /* extern "C" */ 70#endif 71#define regcomp(r,s,n) PCRE2regcomp(r,s,n) 72#define regexec(r,s,n,m,x) PCRE2regexec(r,s,n,m,x) 73#define regfree(r) PCRE2regfree(r) 74#endif 75/* end workaround... */ 76#elif HAVE_PCREPOSIX_H 77#include <pcreposix.h> 78#else 79#include <regex.h> 80#endif 81 82typedef struct 83 { 84 regex_t *pRegExp; 85 unsigned long *refCount; 86 } 87RegExp_Arg; 88 89#elif HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 90#undef RETURN 91static int reg_errno; 92 93static char * 94RegEx_Init(char *instring) 95{ 96 reg_errno = 0; 97 return instring; 98} 99 100static char * 101RegEx_Error(int code) 102{ 103 reg_errno = code; 104 return 0; 105} 106 107#define INIT register char *sp = RegEx_Init(instring); 108#define GETC() (*sp++) 109#define PEEKC() (*sp) 110#define UNGETC(c) (--sp) 111#define RETURN(c) return(c) 112#define ERROR(c) return RegEx_Error(c) 113 114#if HAVE_REGEXP_H_FUNCS 115#include <regexp.h> 116#else 117#include <regexpr.h> 118#endif 119 120typedef struct 121{ 122 char *compiled_expression; 123 unsigned long *refCount; 124} 125RegExp_Arg; 126 127/* Maximum Length we allow for a compiled regular expression */ 128#define MAX_RX_LEN (2048) 129#define RX_INCREMENT (256) 130 131#endif 132 133#if HAVE_REGEX_H_FUNCS | HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 134# define MAYBE_UNUSED 135#else 136# define MAYBE_UNUSED GCC_UNUSED 137#endif 138 139/*--------------------------------------------------------------------------- 140| Facility : libnform 141| Function : static void *Generic_RegularExpression_Type(void * arg) 142| 143| Description : Allocate structure for regex type argument. 144| 145| Return Values : Pointer to argument structure or NULL on error 146+--------------------------------------------------------------------------*/ 147static void * 148Generic_RegularExpression_Type(void *arg MAYBE_UNUSED) 149{ 150#if HAVE_REGEX_H_FUNCS 151 char *rx = (char *)arg; 152 RegExp_Arg *preg = (RegExp_Arg *)0; 153 154 if (rx) 155 { 156 preg = typeCalloc(RegExp_Arg, 1); 157 158 if (preg) 159 { 160 T((T_CREATE("RegExp_Arg %p"), (void *)preg)); 161 if (((preg->pRegExp = typeMalloc(regex_t, 1)) != 0) 162 && !regcomp(preg->pRegExp, rx, 163 (REG_EXTENDED | REG_NOSUB | REG_NEWLINE))) 164 { 165 T((T_CREATE("regex_t %p"), (void *)preg->pRegExp)); 166 if ((preg->refCount = typeMalloc(unsigned long, 1)) != 0) 167 *(preg->refCount) = 1; 168 } 169 else 170 { 171 if (preg->pRegExp) 172 free(preg->pRegExp); 173 free(preg); 174 preg = (RegExp_Arg *)0; 175 } 176 } 177 } 178 return ((void *)preg); 179#elif HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 180 char *rx = (char *)arg; 181 RegExp_Arg *pArg = (RegExp_Arg *)0; 182 183 if (rx) 184 { 185 pArg = typeMalloc(RegExp_Arg, 1); 186 187 if (pArg) 188 { 189 int blen = RX_INCREMENT; 190 191 T((T_CREATE("RegExp_Arg %p"), pArg)); 192 pArg->compiled_expression = NULL; 193 if ((pArg->refCount = typeMalloc(unsigned long, 1)) != 0) 194 *(pArg->refCount) = 1; 195 196 do 197 { 198 char *buf = typeMalloc(char, blen); 199 200 if (buf) 201 { 202#if HAVE_REGEXP_H_FUNCS 203 char *last_pos = compile(rx, buf, &buf[blen], '\0'); 204 205#else /* HAVE_REGEXPR_H_FUNCS */ 206 char *last_pos = compile(rx, buf, &buf[blen]); 207#endif 208 if (reg_errno) 209 { 210 free(buf); 211 if (reg_errno == 50) 212 blen += RX_INCREMENT; 213 else 214 { 215 free(pArg); 216 pArg = NULL; 217 break; 218 } 219 } 220 else 221 { 222 pArg->compiled_expression = buf; 223 break; 224 } 225 } 226 } 227 while (blen <= MAX_RX_LEN); 228 } 229 if (pArg && !pArg->compiled_expression) 230 { 231 free(pArg); 232 pArg = NULL; 233 } 234 } 235 return (void *)pArg; 236#else 237 return 0; 238#endif 239} 240 241/*--------------------------------------------------------------------------- 242| Facility : libnform 243| Function : static void *Make_RegularExpression_Type(va_list * ap) 244| 245| Description : Allocate structure for regex type argument. 246| 247| Return Values : Pointer to argument structure or NULL on error 248+--------------------------------------------------------------------------*/ 249static void * 250Make_RegularExpression_Type(va_list *ap) 251{ 252 char *rx = va_arg(*ap, char *); 253 254 return Generic_RegularExpression_Type((void *)rx); 255} 256 257/*--------------------------------------------------------------------------- 258| Facility : libnform 259| Function : static void *Copy_RegularExpression_Type( 260| const void * argp) 261| 262| Description : Copy structure for regex type argument. 263| 264| Return Values : Pointer to argument structure or NULL on error. 265+--------------------------------------------------------------------------*/ 266static void * 267Copy_RegularExpression_Type(const void *argp MAYBE_UNUSED) 268{ 269#if (HAVE_REGEX_H_FUNCS | HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS) 270 const RegExp_Arg *ap = (const RegExp_Arg *)argp; 271 const RegExp_Arg *result = (const RegExp_Arg *)0; 272 273 if (ap) 274 { 275 *(ap->refCount) += 1; 276 result = ap; 277 } 278 return (void *)result; 279#else 280 return 0; 281#endif 282} 283 284/*--------------------------------------------------------------------------- 285| Facility : libnform 286| Function : static void Free_RegularExpression_Type(void * argp) 287| 288| Description : Free structure for regex type argument. 289| 290| Return Values : - 291+--------------------------------------------------------------------------*/ 292static void 293Free_RegularExpression_Type(void *argp MAYBE_UNUSED) 294{ 295#if HAVE_REGEX_H_FUNCS | HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 296 RegExp_Arg *ap = (RegExp_Arg *)argp; 297 298 if (ap) 299 { 300 if (--(*(ap->refCount)) == 0) 301 { 302#if HAVE_REGEX_H_FUNCS 303 if (ap->pRegExp) 304 { 305 free(ap->refCount); 306 regfree(ap->pRegExp); 307 free(ap->pRegExp); 308 } 309#elif HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 310 if (ap->compiled_expression) 311 { 312 free(ap->refCount); 313 free(ap->compiled_expression); 314 } 315#endif 316 free(ap); 317 } 318 } 319#endif 320} 321 322/*--------------------------------------------------------------------------- 323| Facility : libnform 324| Function : static bool Check_RegularExpression_Field( 325| FIELD * field, 326| const void * argp) 327| 328| Description : Validate buffer content to be a valid regular expression 329| 330| Return Values : TRUE - field is valid 331| FALSE - field is invalid 332+--------------------------------------------------------------------------*/ 333static bool 334Check_RegularExpression_Field(FIELD *field MAYBE_UNUSED, 335 const void *argp MAYBE_UNUSED) 336{ 337 bool match = FALSE; 338 339#if HAVE_REGEX_H_FUNCS 340 const RegExp_Arg *ap = (const RegExp_Arg *)argp; 341 342 if (ap && ap->pRegExp) 343 match = (regexec(ap->pRegExp, field_buffer(field, 0), 0, NULL, 0) 344 ? FALSE 345 : TRUE); 346#elif HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 347 RegExp_Arg *ap = (RegExp_Arg *)argp; 348 349 if (ap && ap->compiled_expression) 350 match = (step(field_buffer(field, 0), ap->compiled_expression) 351 ? TRUE 352 : FALSE); 353#endif 354 return match; 355} 356 357static FIELDTYPE typeREGEXP = 358{ 359 _HAS_ARGS | _RESIDENT, 360 1, /* this is mutable, so we can't be const */ 361 (FIELDTYPE *)0, 362 (FIELDTYPE *)0, 363 Make_RegularExpression_Type, 364 Copy_RegularExpression_Type, 365 Free_RegularExpression_Type, 366 INIT_FT_FUNC(Check_RegularExpression_Field), 367 INIT_FT_FUNC(NULL), 368 INIT_FT_FUNC(NULL), 369 INIT_FT_FUNC(NULL), 370#if NCURSES_INTEROP_FUNCS 371 Generic_RegularExpression_Type 372#endif 373}; 374 375FORM_EXPORT_VAR(FIELDTYPE *) TYPE_REGEXP = &typeREGEXP; 376 377#if NCURSES_INTEROP_FUNCS 378/* The next routines are to simplify the use of ncurses from 379 programming languages with restrictions on interop with C level 380 constructs (e.g. variable access or va_list + ellipsis constructs) 381*/ 382FORM_EXPORT(FIELDTYPE *) 383_nc_TYPE_REGEXP(void) 384{ 385 return TYPE_REGEXP; 386} 387#endif 388 389/* fty_regex.c ends here */ 390