1 /**************************************************************************** 2 * Copyright 2020-2024,2025 Thomas E. Dickey * 3 * Copyright 1998-2009,2010 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /*************************************************************************** 31 * * 32 * Author : Juergen Pfeifer * 33 * * 34 ***************************************************************************/ 35 36 #include "form.priv.h" 37 38 MODULE_ID("$Id: fty_enum.c,v 1.36 2025/11/15 18:12:38 tom Exp $") 39 40 typedef struct 41 { 42 char **kwds; 43 int count; 44 bool checkcase; 45 bool checkunique; 46 } 47 enumARG; 48 49 typedef struct 50 { 51 char **kwds; 52 int ccase; 53 int cunique; 54 } 55 enumParams; 56 57 /*--------------------------------------------------------------------------- 58 | Facility : libnform 59 | Function : static void *Generic_Enum_Type(void * arg) 60 | 61 | Description : Allocate structure for enumeration type argument. 62 | 63 | Return Values : Pointer to argument structure or NULL on error 64 +--------------------------------------------------------------------------*/ 65 static void * 66 Generic_Enum_Type(void *arg) 67 { 68 enumARG *argp = (enumARG *)0; 69 enumParams *params = (enumParams *)arg; 70 71 if (params) 72 { 73 argp = typeMalloc(enumARG, 1); 74 75 if (argp) 76 { 77 int cnt = 0; 78 char **kp = (char **)0; 79 char **kwds = (char **)0; 80 int ccase, cunique; 81 82 T((T_CREATE("enumARG %p"), (void *)argp)); 83 kwds = params->kwds; 84 ccase = params->ccase; 85 cunique = params->cunique; 86 87 argp->checkcase = ccase ? TRUE : FALSE; 88 argp->checkunique = cunique ? TRUE : FALSE; 89 argp->kwds = (char **)0; 90 91 kp = kwds; 92 while (kp && (*kp++)) 93 cnt++; 94 argp->count = cnt; 95 96 if (cnt > 0) 97 { 98 char **kptarget; 99 100 /* We copy the keywords, because we can't rely on the fact 101 that the caller doesn't relocate or free the memory used 102 for the keywords (maybe he has GC) 103 */ 104 argp->kwds = typeMalloc(char *, cnt + 1); 105 106 kp = kwds; 107 if ((kptarget = argp->kwds) != NULL) 108 { 109 while (kp && (*kp)) 110 { 111 (*kptarget++) = strdup(*kp++); 112 } 113 *kptarget = (char *)0; 114 } 115 } 116 } 117 } 118 return (void *)argp; 119 } 120 121 /*--------------------------------------------------------------------------- 122 | Facility : libnform 123 | Function : static void *Make_Enum_Type( va_list * ap ) 124 | 125 | Description : Allocate structure for enumeration type argument. 126 | 127 | Return Values : Pointer to argument structure or NULL on error 128 +--------------------------------------------------------------------------*/ 129 static void * 130 Make_Enum_Type(va_list *ap) 131 { 132 enumParams params; 133 134 params.kwds = va_arg(*ap, char **); 135 params.ccase = va_arg(*ap, int); 136 params.cunique = va_arg(*ap, int); 137 138 return Generic_Enum_Type((void *)¶ms); 139 } 140 141 /*--------------------------------------------------------------------------- 142 | Facility : libnform 143 | Function : static void *Copy_Enum_Type( const void * argp ) 144 | 145 | Description : Copy structure for enumeration type argument. 146 | 147 | Return Values : Pointer to argument structure or NULL on error. 148 +--------------------------------------------------------------------------*/ 149 static void * 150 Copy_Enum_Type(const void *argp) 151 { 152 enumARG *result = (enumARG *)0; 153 154 if (argp) 155 { 156 const enumARG *ap = (const enumARG *)argp; 157 158 result = typeMalloc(enumARG, 1); 159 160 if (result) 161 { 162 T((T_CREATE("enumARG %p"), (void *)result)); 163 *result = *ap; 164 165 if (ap->count > 0) 166 { 167 char **kptarget; 168 char **kp = ap->kwds; 169 result->kwds = typeMalloc(char *, 1 + ap->count); 170 171 if ((kptarget = result->kwds) != NULL) 172 { 173 while (kp && (*kp)) 174 { 175 (*kptarget++) = strdup(*kp++); 176 } 177 *kptarget = (char *)0; 178 } 179 } 180 } 181 } 182 return (void *)result; 183 } 184 185 /*--------------------------------------------------------------------------- 186 | Facility : libnform 187 | Function : static void Free_Enum_Type( void * argp ) 188 | 189 | Description : Free structure for enumeration type argument. 190 | 191 | Return Values : - 192 +--------------------------------------------------------------------------*/ 193 static void 194 Free_Enum_Type(void *argp) 195 { 196 if (argp) 197 { 198 const enumARG *ap = (const enumARG *)argp; 199 200 if (ap->kwds && ap->count > 0) 201 { 202 char **kp = ap->kwds; 203 int cnt = 0; 204 205 while (kp && (*kp)) 206 { 207 free(*kp++); 208 cnt++; 209 } 210 assert(cnt == ap->count); 211 free(ap->kwds); 212 (void) cnt; 213 } 214 free(argp); 215 } 216 } 217 218 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++ 219 #define NOMATCH 0 220 #define PARTIAL 1 221 #define EXACT 2 222 223 /*--------------------------------------------------------------------------- 224 | Facility : libnform 225 | Function : static int Compare(const unsigned char * s, 226 | const unsigned char * buf, 227 | bool ccase ) 228 | 229 | Description : Check whether or not the text in 'buf' matches the 230 | text in 's', at least partial. 231 | 232 | Return Values : NOMATCH - buffer doesn't match 233 | PARTIAL - buffer matches partially 234 | EXACT - buffer matches exactly 235 +--------------------------------------------------------------------------*/ 236 static int 237 Compare(const unsigned char *s, const unsigned char *buf, 238 bool ccase) 239 { 240 SKIP_SPACE(buf); /* Skip leading spaces in both texts */ 241 SKIP_SPACE(s); 242 243 if (*buf == '\0') 244 { 245 return (((*s) != '\0') ? NOMATCH : EXACT); 246 } 247 else 248 { 249 if (ccase) 250 { 251 while (*s++ == *buf) 252 { 253 if (*buf++ == '\0') 254 return EXACT; 255 } 256 } 257 else 258 { 259 while (toupper(*s++) == toupper(*buf)) 260 { 261 if (*buf++ == '\0') 262 return EXACT; 263 } 264 } 265 } 266 /* At this location buf points to the first character where it no longer 267 matches with s. So if only blanks are following, we have a partial 268 match otherwise there is no match */ 269 SKIP_SPACE(buf); 270 if (*buf) 271 return NOMATCH; 272 273 /* If it happens that the reference buffer is at its end, the partial 274 match is actually an exact match. */ 275 return ((s[-1] != '\0') ? PARTIAL : EXACT); 276 } 277 278 /*--------------------------------------------------------------------------- 279 | Facility : libnform 280 | Function : static bool Check_Enum_Field( 281 | FIELD * field, 282 | const void * argp) 283 | 284 | Description : Validate buffer content to be a valid enumeration value 285 | 286 | Return Values : TRUE - field is valid 287 | FALSE - field is invalid 288 +--------------------------------------------------------------------------*/ 289 static bool 290 Check_Enum_Field(FIELD *field, const void *argp) 291 { 292 char **kwds = ((const enumARG *)argp)->kwds; 293 bool ccase = ((const enumARG *)argp)->checkcase; 294 bool unique = ((const enumARG *)argp)->checkunique; 295 const unsigned char *bp = (unsigned char *)field_buffer(field, 0); 296 const char *s, *t, *p; 297 298 while (kwds && (s = (*kwds++))) 299 { 300 int res; 301 302 if ((res = Compare((const unsigned char *)s, bp, ccase)) != NOMATCH) 303 { 304 p = t = s; /* t is at least a partial match */ 305 if ((unique && res != EXACT)) 306 { 307 while (kwds && (p = *kwds++)) 308 { 309 if ((res = Compare((const unsigned char *)p, bp, ccase)) != NOMATCH) 310 { 311 if (res == EXACT) 312 { 313 t = p; 314 break; 315 } 316 else 317 t = NULL; 318 } 319 } 320 } 321 if (t) 322 { 323 set_field_buffer(field, 0, t); 324 return TRUE; 325 } 326 if (!p) 327 break; 328 } 329 } 330 return FALSE; 331 } 332 333 static const char *dummy[] = 334 {(char *)0}; 335 336 /*--------------------------------------------------------------------------- 337 | Facility : libnform 338 | Function : static bool Next_Enum(FIELD * field, 339 | const void * argp) 340 | 341 | Description : Check for the next enumeration value 342 | 343 | Return Values : TRUE - next value found and loaded 344 | FALSE - no next value loaded 345 +--------------------------------------------------------------------------*/ 346 static bool 347 Next_Enum(FIELD *field, const void *argp) 348 { 349 const enumARG *args = (const enumARG *)argp; 350 char **kwds = args->kwds; 351 const unsigned char *bp = (const unsigned char *)field_buffer(field, 0); 352 353 if (kwds) 354 { 355 int cnt = args->count; 356 bool ccase = args->checkcase; 357 358 while (cnt--) 359 { 360 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT) 361 break; 362 } 363 if (cnt <= 0) 364 kwds = args->kwds; 365 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 366 { 367 set_field_buffer(field, 0, *kwds); 368 return TRUE; 369 } 370 } 371 return FALSE; 372 } 373 374 /*--------------------------------------------------------------------------- 375 | Facility : libnform 376 | Function : static bool Previous_Enum( 377 | FIELD * field, 378 | const void * argp) 379 | 380 | Description : Check for the previous enumeration value 381 | 382 | Return Values : TRUE - previous value found and loaded 383 | FALSE - no previous value loaded 384 +--------------------------------------------------------------------------*/ 385 static bool 386 Previous_Enum(FIELD *field, const void *argp) 387 { 388 const enumARG *args = (const enumARG *)argp; 389 int cnt = args->count; 390 char **kwds = &args->kwds[cnt - 1]; 391 const unsigned char *bp = (const unsigned char *)field_buffer(field, 0); 392 393 bool ccase = args->checkcase; 394 395 while (cnt--) 396 { 397 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT) 398 break; 399 } 400 401 if (cnt <= 0) 402 kwds = &args->kwds[args->count - 1]; 403 404 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 405 { 406 set_field_buffer(field, 0, *kwds); 407 return TRUE; 408 } 409 410 return FALSE; 411 } 412 413 static FIELDTYPE typeENUM = 414 { 415 _HAS_ARGS | _HAS_CHOICE | _RESIDENT, 416 1, /* this is mutable, so we can't be const */ 417 (FIELDTYPE *)0, 418 (FIELDTYPE *)0, 419 Make_Enum_Type, 420 Copy_Enum_Type, 421 Free_Enum_Type, 422 INIT_FT_FUNC(Check_Enum_Field), 423 INIT_FT_FUNC(NULL), 424 INIT_FT_FUNC(Next_Enum), 425 INIT_FT_FUNC(Previous_Enum), 426 #if NCURSES_INTEROP_FUNCS 427 Generic_Enum_Type 428 #endif 429 }; 430 431 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM; 432 433 #if NCURSES_INTEROP_FUNCS 434 /* The next routines are to simplify the use of ncurses from 435 programming languages with restrictions on interop with C level 436 constructs (e.g. variable access or va_list + ellipsis constructs) 437 */ 438 FORM_EXPORT(FIELDTYPE *) 439 _nc_TYPE_ENUM(void) 440 { 441 return TYPE_ENUM; 442 } 443 #endif 444 445 /* fty_enum.c ends here */ 446