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 39 MODULE_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 61 extern "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 82 typedef struct 83 { 84 regex_t *pRegExp; 85 unsigned long *refCount; 86 } 87 RegExp_Arg; 88 89 #elif HAVE_REGEXP_H_FUNCS | HAVE_REGEXPR_H_FUNCS 90 #undef RETURN 91 static int reg_errno; 92 93 static char * 94 RegEx_Init(char *instring) 95 { 96 reg_errno = 0; 97 return instring; 98 } 99 100 static char * 101 RegEx_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 120 typedef struct 121 { 122 char *compiled_expression; 123 unsigned long *refCount; 124 } 125 RegExp_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 +--------------------------------------------------------------------------*/ 147 static void * 148 Generic_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 +--------------------------------------------------------------------------*/ 249 static void * 250 Make_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 +--------------------------------------------------------------------------*/ 266 static void * 267 Copy_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 +--------------------------------------------------------------------------*/ 292 static void 293 Free_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 +--------------------------------------------------------------------------*/ 333 static bool 334 Check_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 357 static 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 375 FORM_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 */ 382 FORM_EXPORT(FIELDTYPE *) 383 _nc_TYPE_REGEXP(void) 384 { 385 return TYPE_REGEXP; 386 } 387 #endif 388 389 /* fty_regex.c ends here */ 390