1 /* $OpenBSD: fty_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $ */ 2 /**************************************************************************** 3 * Copyright 2020,2021 Thomas E. Dickey * 4 * Copyright 1998-2009,2010 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_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $") 40 41 typedef struct 42 { 43 char **kwds; 44 int count; 45 bool checkcase; 46 bool checkunique; 47 } 48 enumARG; 49 50 typedef struct 51 { 52 char **kwds; 53 int ccase; 54 int cunique; 55 } 56 enumParams; 57 58 /*--------------------------------------------------------------------------- 59 | Facility : libnform 60 | Function : static void *Generic_Enum_Type(void * arg) 61 | 62 | Description : Allocate structure for enumeration type argument. 63 | 64 | Return Values : Pointer to argument structure or NULL on error 65 +--------------------------------------------------------------------------*/ 66 static void * 67 Generic_Enum_Type(void *arg) 68 { 69 enumARG *argp = (enumARG *)0; 70 enumParams *params = (enumParams *)arg; 71 72 if (params) 73 { 74 argp = typeMalloc(enumARG, 1); 75 76 if (argp) 77 { 78 int cnt = 0; 79 char **kp = (char **)0; 80 char **kwds = (char **)0; 81 int ccase, cunique; 82 83 T((T_CREATE("enumARG %p"), (void *)argp)); 84 kwds = params->kwds; 85 ccase = params->ccase; 86 cunique = params->cunique; 87 88 argp->checkcase = ccase ? TRUE : FALSE; 89 argp->checkunique = cunique ? TRUE : FALSE; 90 argp->kwds = (char **)0; 91 92 kp = kwds; 93 while (kp && (*kp++)) 94 cnt++; 95 argp->count = cnt; 96 97 if (cnt > 0) 98 { 99 char **kptarget; 100 101 /* We copy the keywords, because we can't rely on the fact 102 that the caller doesn't relocate or free the memory used 103 for the keywords (maybe he has GC) 104 */ 105 argp->kwds = typeMalloc(char *, cnt + 1); 106 107 kp = kwds; 108 if ((kptarget = argp->kwds) != 0) 109 { 110 while (kp && (*kp)) 111 { 112 (*kptarget++) = strdup(*kp++); 113 } 114 *kptarget = (char *)0; 115 } 116 } 117 } 118 } 119 return (void *)argp; 120 } 121 122 /*--------------------------------------------------------------------------- 123 | Facility : libnform 124 | Function : static void *Make_Enum_Type( va_list * ap ) 125 | 126 | Description : Allocate structure for enumeration type argument. 127 | 128 | Return Values : Pointer to argument structure or NULL on error 129 +--------------------------------------------------------------------------*/ 130 static void * 131 Make_Enum_Type(va_list *ap) 132 { 133 enumParams params; 134 135 params.kwds = va_arg(*ap, char **); 136 params.ccase = va_arg(*ap, int); 137 params.cunique = va_arg(*ap, int); 138 139 return Generic_Enum_Type((void *)¶ms); 140 } 141 142 /*--------------------------------------------------------------------------- 143 | Facility : libnform 144 | Function : static void *Copy_Enum_Type( const void * argp ) 145 | 146 | Description : Copy structure for enumeration type argument. 147 | 148 | Return Values : Pointer to argument structure or NULL on error. 149 +--------------------------------------------------------------------------*/ 150 static void * 151 Copy_Enum_Type(const void *argp) 152 { 153 enumARG *result = (enumARG *)0; 154 155 if (argp) 156 { 157 const enumARG *ap = (const enumARG *)argp; 158 159 result = typeMalloc(enumARG, 1); 160 161 if (result) 162 { 163 T((T_CREATE("enumARG %p"), (void *)result)); 164 *result = *ap; 165 166 if (ap->count > 0) 167 { 168 char **kptarget; 169 char **kp = ap->kwds; 170 result->kwds = typeMalloc(char *, 1 + ap->count); 171 172 if ((kptarget = result->kwds) != 0) 173 { 174 while (kp && (*kp)) 175 { 176 (*kptarget++) = strdup(*kp++); 177 } 178 *kptarget = (char *)0; 179 } 180 } 181 } 182 } 183 return (void *)result; 184 } 185 186 /*--------------------------------------------------------------------------- 187 | Facility : libnform 188 | Function : static void Free_Enum_Type( void * argp ) 189 | 190 | Description : Free structure for enumeration type argument. 191 | 192 | Return Values : - 193 +--------------------------------------------------------------------------*/ 194 static void 195 Free_Enum_Type(void *argp) 196 { 197 if (argp) 198 { 199 const enumARG *ap = (const enumARG *)argp; 200 201 if (ap->kwds && ap->count > 0) 202 { 203 char **kp = ap->kwds; 204 int cnt = 0; 205 206 while (kp && (*kp)) 207 { 208 free(*kp++); 209 cnt++; 210 } 211 assert(cnt == ap->count); 212 free(ap->kwds); 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 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 296 char *s, *t, *p; 297 298 while (kwds && (s = (*kwds++))) 299 { 300 int res; 301 302 if ((res = Compare((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((unsigned char *)p, bp, ccase)) != NOMATCH) 310 { 311 if (res == EXACT) 312 { 313 t = p; 314 break; 315 } 316 else 317 t = (char *)0; 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 bool ccase = args->checkcase; 352 int cnt = args->count; 353 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 354 355 if (kwds) 356 { 357 while (cnt--) 358 { 359 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT) 360 break; 361 } 362 if (cnt <= 0) 363 kwds = args->kwds; 364 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 365 { 366 set_field_buffer(field, 0, *kwds); 367 return TRUE; 368 } 369 } 370 return FALSE; 371 } 372 373 /*--------------------------------------------------------------------------- 374 | Facility : libnform 375 | Function : static bool Previous_Enum( 376 | FIELD * field, 377 | const void * argp) 378 | 379 | Description : Check for the previous enumeration value 380 | 381 | Return Values : TRUE - previous value found and loaded 382 | FALSE - no previous value loaded 383 +--------------------------------------------------------------------------*/ 384 static bool 385 Previous_Enum(FIELD *field, const void *argp) 386 { 387 const enumARG *args = (const enumARG *)argp; 388 int cnt = args->count; 389 char **kwds = &args->kwds[cnt - 1]; 390 bool ccase = args->checkcase; 391 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 392 393 if (kwds) 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