1 /* $NetBSD: type_enum.c,v 1.13 2021/04/13 13:13:04 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1998-1999 Brett Lymn 5 * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au) 6 * All rights reserved. 7 * 8 * This code has been donated to The NetBSD Foundation by the Author. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: type_enum.c,v 1.13 2021/04/13 13:13:04 christos Exp $"); 34 35 #include <ctype.h> 36 #include <stdlib.h> 37 #include <strings.h> 38 #include "form.h" 39 #include "internals.h" 40 41 /* 42 * Prototypes. 43 */ 44 static int 45 trim_blanks(char *field); 46 47 /* 48 * The enum type handling. 49 */ 50 51 typedef struct 52 { 53 char **choices; 54 unsigned num_choices; 55 bool ignore_case; 56 bool exact; 57 } enum_args; 58 59 /* 60 * Find the first non-blank character at the end of a field, return the 61 * index of that character. 62 */ 63 static int 64 trim_blanks(char *field) 65 { 66 int i; 67 68 i = (int) strlen(field); 69 if (i > 0) 70 i--; 71 else 72 return 0; 73 74 while ((i > 0) && isblank((unsigned char)field[i])) 75 i--; 76 77 return i; 78 } 79 80 /* 81 * Create the enum arguments structure from the given args. Return NULL 82 * if the call fails, otherwise return a pointer to the structure allocated. 83 */ 84 static char * 85 create_enum_args(va_list *args) 86 { 87 enum_args *new; 88 char **choices; 89 90 new = malloc(sizeof(*new)); 91 if (new == NULL) 92 return NULL; 93 94 new->choices = va_arg(*args, char **); 95 new->ignore_case = (va_arg(*args, int)) ? TRUE : FALSE; 96 new->exact = (va_arg(*args, int)) ? TRUE : FALSE; 97 98 _formi_dbg_printf("%s: ignore_case %d, no_blanks %d\n", __func__, 99 new->ignore_case, new->exact); 100 101 /* count the choices we have */ 102 choices = new->choices; 103 new->num_choices = 0; 104 while (*choices != NULL) { 105 _formi_dbg_printf("%s: choice[%u] = \'%s\'\n", __func__, 106 new->num_choices, new->choices[new->num_choices]); 107 new->num_choices++; 108 choices++; 109 } 110 _formi_dbg_printf("%s: have %u choices\n", __func__, 111 new->num_choices); 112 113 return (void *) new; 114 } 115 116 /* 117 * Copy the enum argument structure. 118 */ 119 static char * 120 copy_enum_args(char *args) 121 { 122 enum_args *new; 123 124 new = malloc(sizeof(*new)); 125 126 if (new != NULL) 127 memcpy(new, args, sizeof(*new)); 128 129 return (void *) new; 130 } 131 132 /* 133 * Free the allocated storage associated with the type arguments. 134 */ 135 static void 136 free_enum_args(char *args) 137 { 138 if (args != NULL) 139 free(args); 140 } 141 142 /* 143 * Attempt to match the string in this to the choices given. Returns 144 * TRUE if match found otherwise FALSE. 145 * 146 */ 147 static bool 148 match_enum(char **choices, unsigned num_choices, bool ignore_case, 149 bool exact, char *this, unsigned *match_num) 150 { 151 unsigned i, start, end, enum_start, blen, elen, enum_end; 152 bool cur_match; 153 154 start = _formi_skip_blanks(this, 0); 155 end = trim_blanks(this); 156 157 if (end >= start) 158 blen = (unsigned) (strlen(&this[start]) 159 - strlen(&this[end]) + 1); 160 else 161 blen = 0; 162 163 _formi_dbg_printf("%s: start %u, blen %u\n", __func__, start, blen); 164 for (i = 0; i < num_choices; i++) { 165 enum_start = _formi_skip_blanks(choices[i], 0); 166 enum_end = trim_blanks(choices[i]); 167 168 if (enum_end >= enum_start) 169 elen = (unsigned) (strlen(&choices[i][enum_start]) 170 - strlen(&choices[i][enum_end]) + 1); 171 else 172 elen = 0; 173 174 _formi_dbg_printf("%s: checking choice \'%s\'\n", __func__, 175 choices[i]); 176 _formi_dbg_printf("%s: enum_start %u, elen %u\n", __func__, 177 enum_start, elen); 178 179 /* don't bother if we are after an exact match 180 * and the test length is not equal to the enum 181 * in question - it will never match. 182 */ 183 if ((exact == TRUE) && (blen != elen)) 184 continue; 185 186 /* 187 * If the test length is longer than the enum 188 * length then there is no chance of a match 189 * so we skip. 190 */ 191 if ((exact != TRUE) && (blen > elen)) 192 continue; 193 194 if (ignore_case) 195 cur_match = (strncasecmp(&choices[i][enum_start], 196 &this[start], 197 (size_t)blen) == 0) ? 198 TRUE : FALSE; 199 else 200 cur_match = (strncmp(&choices[i][enum_start], 201 &this[start], 202 (size_t) blen) == 0) ? 203 TRUE : FALSE; 204 205 _formi_dbg_printf("%s: curmatch is %s\n", __func__, 206 (cur_match == TRUE)? "TRUE" : "FALSE"); 207 208 if (cur_match == TRUE) { 209 *match_num = i; 210 return TRUE; 211 } 212 213 } 214 215 _formi_dbg_printf("%s: no match found\n", __func__); 216 return FALSE; 217 } 218 219 /* 220 * Check the contents of the field buffer match one of the enum strings only. 221 */ 222 static int 223 enum_check_field(FIELD *field, char *args) 224 { 225 enum_args *ta; 226 unsigned match_num; 227 228 if (args == NULL) 229 return FALSE; 230 231 ta = (enum_args *) (void *) field->args; 232 233 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 234 ta->exact, args, &match_num) == TRUE) { 235 _formi_dbg_printf("%s: We matched, match_num %u\n", __func__, 236 match_num); 237 _formi_dbg_printf("%s: buffer is \'%s\'\n", __func__, 238 ta->choices[match_num]); 239 set_field_buffer(field, 0, ta->choices[match_num]); 240 return TRUE; 241 } 242 243 return FALSE; 244 } 245 246 /* 247 * Get the next enum in the list of choices. 248 */ 249 static int 250 next_enum(FIELD *field, char *args) 251 { 252 enum_args *ta; 253 unsigned cur_choice; 254 255 if (args == NULL) 256 return FALSE; 257 258 ta = (enum_args *) (void *) field->args; 259 260 _formi_dbg_printf("%s: attempt to match \'%s\'\n", __func__, args); 261 262 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 263 ta->exact, args, &cur_choice) == FALSE) { 264 _formi_dbg_printf("%s: match failed\n", __func__); 265 return FALSE; 266 } 267 268 _formi_dbg_printf("%s: cur_choice is %u\n", __func__, cur_choice); 269 270 cur_choice++; 271 272 if (cur_choice >= ta->num_choices) 273 cur_choice = 0; 274 275 _formi_dbg_printf("%s: cur_choice is %u on exit\n", __func__, 276 cur_choice); 277 278 set_field_buffer(field, 0, ta->choices[cur_choice]); 279 return TRUE; 280 } 281 282 /* 283 * Get the previous enum in the list of choices. 284 */ 285 static int 286 prev_enum(FIELD *field, char *args) 287 { 288 enum_args *ta; 289 unsigned cur_choice; 290 291 if (args == NULL) 292 return FALSE; 293 294 ta = (enum_args *) (void *) field->args; 295 296 _formi_dbg_printf("%s: attempt to match \'%s\'\n", __func__, args); 297 298 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 299 ta->exact, args, &cur_choice) == FALSE) { 300 _formi_dbg_printf("%s: match failed\n", __func__); 301 return FALSE; 302 } 303 304 _formi_dbg_printf("%s: cur_choice is %u\n", __func__, cur_choice); 305 if (cur_choice == 0) 306 cur_choice = ta->num_choices - 1; 307 else 308 cur_choice--; 309 310 _formi_dbg_printf("%s: cur_choice is %u on exit\n", 311 __func__, cur_choice); 312 313 set_field_buffer(field, 0, ta->choices[cur_choice]); 314 return TRUE; 315 } 316 317 318 static FIELDTYPE builtin_enum = { 319 _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 320 0, /* refcount */ 321 NULL, /* link */ 322 create_enum_args, /* make_args */ 323 copy_enum_args, /* copy_args */ 324 free_enum_args, /* free_args */ 325 enum_check_field, /* field_check */ 326 NULL, /* char_check */ 327 next_enum, /* next_choice */ 328 prev_enum /* prev_choice */ 329 }; 330 331 FIELDTYPE *TYPE_ENUM = &builtin_enum; 332 333 334