jcs's openbsd hax
openbsd
at jcs 445 lines 13 kB view raw
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 39MODULE_ID("$Id: fty_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $") 40 41typedef struct 42 { 43 char **kwds; 44 int count; 45 bool checkcase; 46 bool checkunique; 47 } 48enumARG; 49 50typedef struct 51 { 52 char **kwds; 53 int ccase; 54 int cunique; 55 } 56enumParams; 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+--------------------------------------------------------------------------*/ 66static void * 67Generic_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+--------------------------------------------------------------------------*/ 130static void * 131Make_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 *)&params); 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+--------------------------------------------------------------------------*/ 150static void * 151Copy_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+--------------------------------------------------------------------------*/ 194static void 195Free_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+--------------------------------------------------------------------------*/ 236static int 237Compare(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+--------------------------------------------------------------------------*/ 289static bool 290Check_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 333static 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+--------------------------------------------------------------------------*/ 346static bool 347Next_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+--------------------------------------------------------------------------*/ 384static bool 385Previous_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 413static 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 431FORM_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*/ 438FORM_EXPORT(FIELDTYPE *) 439_nc_TYPE_ENUM(void) 440{ 441 return TYPE_ENUM; 442} 443#endif 444 445/* fty_enum.c ends here */