1 /**************************************************************************** 2 * Copyright 2020,2021 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.33 2021/06/17 21:11:08 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) != 0) 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) != 0) 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 } 213 free(argp); 214 } 215 } 216 217 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++ 218 #define NOMATCH 0 219 #define PARTIAL 1 220 #define EXACT 2 221 222 /*--------------------------------------------------------------------------- 223 | Facility : libnform 224 | Function : static int Compare(const unsigned char * s, 225 | const unsigned char * buf, 226 | bool ccase ) 227 | 228 | Description : Check whether or not the text in 'buf' matches the 229 | text in 's', at least partial. 230 | 231 | Return Values : NOMATCH - buffer doesn't match 232 | PARTIAL - buffer matches partially 233 | EXACT - buffer matches exactly 234 +--------------------------------------------------------------------------*/ 235 static int 236 Compare(const unsigned char *s, const unsigned char *buf, 237 bool ccase) 238 { 239 SKIP_SPACE(buf); /* Skip leading spaces in both texts */ 240 SKIP_SPACE(s); 241 242 if (*buf == '\0') 243 { 244 return (((*s) != '\0') ? NOMATCH : EXACT); 245 } 246 else 247 { 248 if (ccase) 249 { 250 while (*s++ == *buf) 251 { 252 if (*buf++ == '\0') 253 return EXACT; 254 } 255 } 256 else 257 { 258 while (toupper(*s++) == toupper(*buf)) 259 { 260 if (*buf++ == '\0') 261 return EXACT; 262 } 263 } 264 } 265 /* At this location buf points to the first character where it no longer 266 matches with s. So if only blanks are following, we have a partial 267 match otherwise there is no match */ 268 SKIP_SPACE(buf); 269 if (*buf) 270 return NOMATCH; 271 272 /* If it happens that the reference buffer is at its end, the partial 273 match is actually an exact match. */ 274 return ((s[-1] != '\0') ? PARTIAL : EXACT); 275 } 276 277 /*--------------------------------------------------------------------------- 278 | Facility : libnform 279 | Function : static bool Check_Enum_Field( 280 | FIELD * field, 281 | const void * argp) 282 | 283 | Description : Validate buffer content to be a valid enumeration value 284 | 285 | Return Values : TRUE - field is valid 286 | FALSE - field is invalid 287 +--------------------------------------------------------------------------*/ 288 static bool 289 Check_Enum_Field(FIELD *field, const void *argp) 290 { 291 char **kwds = ((const enumARG *)argp)->kwds; 292 bool ccase = ((const enumARG *)argp)->checkcase; 293 bool unique = ((const enumARG *)argp)->checkunique; 294 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 295 char *s, *t, *p; 296 297 while (kwds && (s = (*kwds++))) 298 { 299 int res; 300 301 if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH) 302 { 303 p = t = s; /* t is at least a partial match */ 304 if ((unique && res != EXACT)) 305 { 306 while (kwds && (p = *kwds++)) 307 { 308 if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH) 309 { 310 if (res == EXACT) 311 { 312 t = p; 313 break; 314 } 315 else 316 t = (char *)0; 317 } 318 } 319 } 320 if (t) 321 { 322 set_field_buffer(field, 0, t); 323 return TRUE; 324 } 325 if (!p) 326 break; 327 } 328 } 329 return FALSE; 330 } 331 332 static const char *dummy[] = 333 {(char *)0}; 334 335 /*--------------------------------------------------------------------------- 336 | Facility : libnform 337 | Function : static bool Next_Enum(FIELD * field, 338 | const void * argp) 339 | 340 | Description : Check for the next enumeration value 341 | 342 | Return Values : TRUE - next value found and loaded 343 | FALSE - no next value loaded 344 +--------------------------------------------------------------------------*/ 345 static bool 346 Next_Enum(FIELD *field, const void *argp) 347 { 348 const enumARG *args = (const enumARG *)argp; 349 char **kwds = args->kwds; 350 bool ccase = args->checkcase; 351 int cnt = args->count; 352 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 353 354 if (kwds) 355 { 356 while (cnt--) 357 { 358 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT) 359 break; 360 } 361 if (cnt <= 0) 362 kwds = args->kwds; 363 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 364 { 365 set_field_buffer(field, 0, *kwds); 366 return TRUE; 367 } 368 } 369 return FALSE; 370 } 371 372 /*--------------------------------------------------------------------------- 373 | Facility : libnform 374 | Function : static bool Previous_Enum( 375 | FIELD * field, 376 | const void * argp) 377 | 378 | Description : Check for the previous enumeration value 379 | 380 | Return Values : TRUE - previous value found and loaded 381 | FALSE - no previous value loaded 382 +--------------------------------------------------------------------------*/ 383 static bool 384 Previous_Enum(FIELD *field, const void *argp) 385 { 386 const enumARG *args = (const enumARG *)argp; 387 int cnt = args->count; 388 char **kwds = &args->kwds[cnt - 1]; 389 bool ccase = args->checkcase; 390 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 391 392 if (kwds) 393 { 394 while (cnt--) 395 { 396 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT) 397 break; 398 } 399 400 if (cnt <= 0) 401 kwds = &args->kwds[args->count - 1]; 402 403 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 404 { 405 set_field_buffer(field, 0, *kwds); 406 return TRUE; 407 } 408 } 409 return FALSE; 410 } 411 412 static FIELDTYPE typeENUM = 413 { 414 _HAS_ARGS | _HAS_CHOICE | _RESIDENT, 415 1, /* this is mutable, so we can't be const */ 416 (FIELDTYPE *)0, 417 (FIELDTYPE *)0, 418 Make_Enum_Type, 419 Copy_Enum_Type, 420 Free_Enum_Type, 421 INIT_FT_FUNC(Check_Enum_Field), 422 INIT_FT_FUNC(NULL), 423 INIT_FT_FUNC(Next_Enum), 424 INIT_FT_FUNC(Previous_Enum), 425 #if NCURSES_INTEROP_FUNCS 426 Generic_Enum_Type 427 #endif 428 }; 429 430 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM; 431 432 #if NCURSES_INTEROP_FUNCS 433 /* The next routines are to simplify the use of ncurses from 434 programming languages with restrictions on interop with C level 435 constructs (e.g. variable access or va_list + ellipsis constructs) 436 */ 437 FORM_EXPORT(FIELDTYPE *) 438 _nc_TYPE_ENUM(void) 439 { 440 return TYPE_ENUM; 441 } 442 #endif 443 444 /* fty_enum.c ends here */ 445