1 /**************************************************************************** 2 * Copyright 2020 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.31 2020/12/12 01:15:37 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 char **kptarget; 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 /* We copy the keywords, because we can't rely on the fact 100 that the caller doesn't relocate or free the memory used 101 for the keywords (maybe he has GC) 102 */ 103 argp->kwds = typeMalloc(char *, cnt + 1); 104 105 kp = kwds; 106 if ((kptarget = argp->kwds) != 0) 107 { 108 while (kp && (*kp)) 109 { 110 (*kptarget++) = strdup(*kp++); 111 } 112 *kptarget = (char *)0; 113 } 114 } 115 } 116 } 117 return (void *)argp; 118 } 119 120 /*--------------------------------------------------------------------------- 121 | Facility : libnform 122 | Function : static void *Make_Enum_Type( va_list * ap ) 123 | 124 | Description : Allocate structure for enumeration type argument. 125 | 126 | Return Values : Pointer to argument structure or NULL on error 127 +--------------------------------------------------------------------------*/ 128 static void * 129 Make_Enum_Type(va_list *ap) 130 { 131 enumParams params; 132 133 params.kwds = va_arg(*ap, char **); 134 params.ccase = va_arg(*ap, int); 135 params.cunique = va_arg(*ap, int); 136 137 return Generic_Enum_Type((void *)¶ms); 138 } 139 140 /*--------------------------------------------------------------------------- 141 | Facility : libnform 142 | Function : static void *Copy_Enum_Type( const void * argp ) 143 | 144 | Description : Copy structure for enumeration type argument. 145 | 146 | Return Values : Pointer to argument structure or NULL on error. 147 +--------------------------------------------------------------------------*/ 148 static void * 149 Copy_Enum_Type(const void *argp) 150 { 151 enumARG *result = (enumARG *)0; 152 153 if (argp) 154 { 155 const enumARG *ap = (const enumARG *)argp; 156 157 result = typeMalloc(enumARG, 1); 158 159 if (result) 160 { 161 T((T_CREATE("enumARG %p"), (void *)result)); 162 *result = *ap; 163 164 if (ap->count > 0) 165 { 166 char **kptarget; 167 char **kp = ap->kwds; 168 result->kwds = typeMalloc(char *, 1 + ap->count); 169 170 if ((kptarget = result->kwds) != 0) 171 { 172 while (kp && (*kp)) 173 { 174 (*kptarget++) = strdup(*kp++); 175 } 176 *kptarget = (char *)0; 177 } 178 } 179 } 180 } 181 return (void *)result; 182 } 183 184 /*--------------------------------------------------------------------------- 185 | Facility : libnform 186 | Function : static void Free_Enum_Type( void * argp ) 187 | 188 | Description : Free structure for enumeration type argument. 189 | 190 | Return Values : - 191 +--------------------------------------------------------------------------*/ 192 static void 193 Free_Enum_Type(void *argp) 194 { 195 if (argp) 196 { 197 const enumARG *ap = (const enumARG *)argp; 198 199 if (ap->kwds && ap->count > 0) 200 { 201 char **kp = ap->kwds; 202 int cnt = 0; 203 204 while (kp && (*kp)) 205 { 206 free(*kp++); 207 cnt++; 208 } 209 assert(cnt == ap->count); 210 free(ap->kwds); 211 } 212 free(argp); 213 } 214 } 215 216 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++ 217 #define NOMATCH 0 218 #define PARTIAL 1 219 #define EXACT 2 220 221 /*--------------------------------------------------------------------------- 222 | Facility : libnform 223 | Function : static int Compare(const unsigned char * s, 224 | const unsigned char * buf, 225 | bool ccase ) 226 | 227 | Description : Check whether or not the text in 'buf' matches the 228 | text in 's', at least partial. 229 | 230 | Return Values : NOMATCH - buffer doesn't match 231 | PARTIAL - buffer matches partially 232 | EXACT - buffer matches exactly 233 +--------------------------------------------------------------------------*/ 234 static int 235 Compare(const unsigned char *s, const unsigned char *buf, 236 bool ccase) 237 { 238 SKIP_SPACE(buf); /* Skip leading spaces in both texts */ 239 SKIP_SPACE(s); 240 241 if (*buf == '\0') 242 { 243 return (((*s) != '\0') ? NOMATCH : EXACT); 244 } 245 else 246 { 247 if (ccase) 248 { 249 while (*s++ == *buf) 250 { 251 if (*buf++ == '\0') 252 return EXACT; 253 } 254 } 255 else 256 { 257 while (toupper(*s++) == toupper(*buf)) 258 { 259 if (*buf++ == '\0') 260 return EXACT; 261 } 262 } 263 } 264 /* At this location buf points to the first character where it no longer 265 matches with s. So if only blanks are following, we have a partial 266 match otherwise there is no match */ 267 SKIP_SPACE(buf); 268 if (*buf) 269 return NOMATCH; 270 271 /* If it happens that the reference buffer is at its end, the partial 272 match is actually an exact match. */ 273 return ((s[-1] != '\0') ? PARTIAL : EXACT); 274 } 275 276 /*--------------------------------------------------------------------------- 277 | Facility : libnform 278 | Function : static bool Check_Enum_Field( 279 | FIELD * field, 280 | const void * argp) 281 | 282 | Description : Validate buffer content to be a valid enumeration value 283 | 284 | Return Values : TRUE - field is valid 285 | FALSE - field is invalid 286 +--------------------------------------------------------------------------*/ 287 static bool 288 Check_Enum_Field(FIELD *field, const void *argp) 289 { 290 char **kwds = ((const enumARG *)argp)->kwds; 291 bool ccase = ((const enumARG *)argp)->checkcase; 292 bool unique = ((const enumARG *)argp)->checkunique; 293 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 294 char *s, *t, *p; 295 int res; 296 297 while (kwds && (s = (*kwds++))) 298 { 299 if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH) 300 { 301 p = t = s; /* t is at least a partial match */ 302 if ((unique && res != EXACT)) 303 { 304 while (kwds && (p = *kwds++)) 305 { 306 if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH) 307 { 308 if (res == EXACT) 309 { 310 t = p; 311 break; 312 } 313 else 314 t = (char *)0; 315 } 316 } 317 } 318 if (t) 319 { 320 set_field_buffer(field, 0, t); 321 return TRUE; 322 } 323 if (!p) 324 break; 325 } 326 } 327 return FALSE; 328 } 329 330 static const char *dummy[] = 331 {(char *)0}; 332 333 /*--------------------------------------------------------------------------- 334 | Facility : libnform 335 | Function : static bool Next_Enum(FIELD * field, 336 | const void * argp) 337 | 338 | Description : Check for the next enumeration value 339 | 340 | Return Values : TRUE - next value found and loaded 341 | FALSE - no next value loaded 342 +--------------------------------------------------------------------------*/ 343 static bool 344 Next_Enum(FIELD *field, const void *argp) 345 { 346 const enumARG *args = (const enumARG *)argp; 347 char **kwds = args->kwds; 348 bool ccase = args->checkcase; 349 int cnt = args->count; 350 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 351 352 if (kwds) 353 { 354 while (cnt--) 355 { 356 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT) 357 break; 358 } 359 if (cnt <= 0) 360 kwds = args->kwds; 361 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 362 { 363 set_field_buffer(field, 0, *kwds); 364 return TRUE; 365 } 366 } 367 return FALSE; 368 } 369 370 /*--------------------------------------------------------------------------- 371 | Facility : libnform 372 | Function : static bool Previous_Enum( 373 | FIELD * field, 374 | const void * argp) 375 | 376 | Description : Check for the previous enumeration value 377 | 378 | Return Values : TRUE - previous value found and loaded 379 | FALSE - no previous value loaded 380 +--------------------------------------------------------------------------*/ 381 static bool 382 Previous_Enum(FIELD *field, const void *argp) 383 { 384 const enumARG *args = (const enumARG *)argp; 385 int cnt = args->count; 386 char **kwds = &args->kwds[cnt - 1]; 387 bool ccase = args->checkcase; 388 unsigned char *bp = (unsigned char *)field_buffer(field, 0); 389 390 if (kwds) 391 { 392 while (cnt--) 393 { 394 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT) 395 break; 396 } 397 398 if (cnt <= 0) 399 kwds = &args->kwds[args->count - 1]; 400 401 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT)) 402 { 403 set_field_buffer(field, 0, *kwds); 404 return TRUE; 405 } 406 } 407 return FALSE; 408 } 409 410 static FIELDTYPE typeENUM = 411 { 412 _HAS_ARGS | _HAS_CHOICE | _RESIDENT, 413 1, /* this is mutable, so we can't be const */ 414 (FIELDTYPE *)0, 415 (FIELDTYPE *)0, 416 Make_Enum_Type, 417 Copy_Enum_Type, 418 Free_Enum_Type, 419 INIT_FT_FUNC(Check_Enum_Field), 420 INIT_FT_FUNC(NULL), 421 INIT_FT_FUNC(Next_Enum), 422 INIT_FT_FUNC(Previous_Enum), 423 #if NCURSES_INTEROP_FUNCS 424 Generic_Enum_Type 425 #endif 426 }; 427 428 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM; 429 430 #if NCURSES_INTEROP_FUNCS 431 /* The next routines are to simplify the use of ncurses from 432 programming languages with restrictions on interop with C level 433 constructs (e.g. variable access or va_list + ellipsis constructs) 434 */ 435 FORM_EXPORT(FIELDTYPE *) 436 _nc_TYPE_ENUM(void) 437 { 438 return TYPE_ENUM; 439 } 440 #endif 441 442 /* fty_enum.c ends here */ 443