1 /* $NetBSD: type_enum.c,v 1.11 2010/05/13 17:52:12 tnozaki Exp $ */ 2 3 /*- 4 * Copyright (c) 1998-1999 Brett Lymn 5 * (blymn@baea.com.au, brett_lymn@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.11 2010/05/13 17:52:12 tnozaki 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 = (enum_args *) malloc(sizeof(enum_args)); 91 92 if (new != NULL) { 93 new->choices = va_arg(*args, char **); 94 new->ignore_case = (va_arg(*args, int)) ? TRUE : FALSE; 95 new->exact = (va_arg(*args, int)) ? TRUE : FALSE; 96 97 #ifdef DEBUG 98 if (_formi_create_dbg_file() != E_OK) 99 return NULL; 100 fprintf(dbg, 101 "create_enum_args: ignore_case %d, no_blanks %d\n", 102 new->ignore_case, new->exact); 103 #endif 104 105 /* count the choices we have */ 106 choices = new->choices; 107 new->num_choices = 0; 108 while (*choices != NULL) { 109 #ifdef DEBUG 110 fprintf(dbg, "create_enum_args: choice[%d] = \'%s\'\n", 111 new->num_choices, 112 new->choices[new->num_choices]); 113 #endif 114 new->num_choices++; 115 choices++; 116 } 117 #ifdef DEBUG 118 fprintf(dbg, "create_enum_args: have %d choices\n", 119 new->num_choices); 120 #endif 121 122 } 123 124 return (void *) new; 125 } 126 127 /* 128 * Copy the enum argument structure. 129 */ 130 static char * 131 copy_enum_args(char *args) 132 { 133 enum_args *new; 134 135 new = (enum_args *) malloc(sizeof(enum_args)); 136 137 if (new != NULL) 138 bcopy(args, new, sizeof(enum_args)); 139 140 return (void *) new; 141 } 142 143 /* 144 * Free the allocated storage associated with the type arguments. 145 */ 146 static void 147 free_enum_args(char *args) 148 { 149 if (args != NULL) 150 free(args); 151 } 152 153 /* 154 * Attempt to match the string in this to the choices given. Returns 155 * TRUE if match found otherwise FALSE. 156 * 157 */ 158 static bool 159 match_enum(char **choices, unsigned num_choices, bool ignore_case, 160 bool exact, char *this, unsigned *match_num) 161 { 162 unsigned i, start, end, enum_start, blen, elen, enum_end; 163 bool cur_match; 164 165 start = _formi_skip_blanks(this, 0); 166 end = trim_blanks(this); 167 168 if (end >= start) 169 blen = (unsigned) (strlen(&this[start]) 170 - strlen(&this[end]) + 1); 171 else 172 blen = 0; 173 174 #ifdef DEBUG 175 fprintf(dbg, "match_enum: start %d, blen %d\n", start, blen); 176 #endif 177 for (i = 0; i < num_choices; i++) { 178 enum_start = _formi_skip_blanks(choices[i], 0); 179 enum_end = trim_blanks(choices[i]); 180 181 if (enum_end >= enum_start) 182 elen = (unsigned) (strlen(&choices[i][enum_start]) 183 - strlen(&choices[i][enum_end]) + 1); 184 else 185 elen = 0; 186 187 #ifdef DEBUG 188 fprintf(dbg, "match_enum: checking choice \'%s\'\n", 189 choices[i]); 190 fprintf(dbg, "match_enum: enum_start %d, elen %d\n", 191 enum_start, elen); 192 #endif 193 194 /* don't bother if we are after an exact match 195 * and the test length is not equal to the enum 196 * in question - it will never match. 197 */ 198 if ((exact == TRUE) && (blen != elen)) 199 continue; 200 201 /* 202 * If the test length is longer than the enum 203 * length then there is no chance of a match 204 * so we skip. 205 */ 206 if ((exact != TRUE) && (blen > elen)) 207 continue; 208 209 if (ignore_case) 210 cur_match = (strncasecmp(&choices[i][enum_start], 211 &this[start], 212 (size_t)blen) == 0) ? 213 TRUE : FALSE; 214 else 215 cur_match = (strncmp(&choices[i][enum_start], 216 &this[start], 217 (size_t) blen) == 0) ? 218 TRUE : FALSE; 219 220 #ifdef DEBUG 221 fprintf(dbg, "match_enum: curmatch is %s\n", 222 (cur_match == TRUE)? "TRUE" : "FALSE"); 223 #endif 224 225 if (cur_match == TRUE) { 226 *match_num = i; 227 return TRUE; 228 } 229 230 } 231 232 #ifdef DEBUG 233 fprintf(dbg, "match_enum: no match found\n"); 234 #endif 235 return FALSE; 236 } 237 238 /* 239 * Check the contents of the field buffer match one of the enum strings only. 240 */ 241 static int 242 enum_check_field(FIELD *field, char *args) 243 { 244 enum_args *ta; 245 unsigned match_num; 246 247 if (args == NULL) 248 return FALSE; 249 250 ta = (enum_args *) (void *) field->args; 251 252 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 253 ta->exact, args, &match_num) == TRUE) { 254 #ifdef DEBUG 255 fprintf(dbg, "enum_check_field: We matched, match_num %d\n", 256 match_num); 257 fprintf(dbg, "enum_check_field: buffer is \'%s\'\n", 258 ta->choices[match_num]); 259 #endif 260 set_field_buffer(field, 0, ta->choices[match_num]); 261 return TRUE; 262 } 263 264 return FALSE; 265 } 266 267 /* 268 * Get the next enum in the list of choices. 269 */ 270 static int 271 next_enum(FIELD *field, char *args) 272 { 273 enum_args *ta; 274 unsigned cur_choice; 275 276 if (args == NULL) 277 return FALSE; 278 279 ta = (enum_args *) (void *) field->args; 280 281 #ifdef DEBUG 282 fprintf(dbg, "next_enum: attempt to match \'%s\'\n", args); 283 #endif 284 285 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 286 ta->exact, args, &cur_choice) == FALSE) { 287 #ifdef DEBUG 288 fprintf(dbg, "next_enum: match failed\n"); 289 #endif 290 return FALSE; 291 } 292 293 #ifdef DEBUG 294 fprintf(dbg, "next_enum: cur_choice is %d\n", cur_choice); 295 #endif 296 297 cur_choice++; 298 299 if (cur_choice >= ta->num_choices) 300 cur_choice = 0; 301 302 #ifdef DEBUG 303 fprintf(dbg, "next_enum: cur_choice is %d on exit\n", 304 cur_choice); 305 #endif 306 307 set_field_buffer(field, 0, ta->choices[cur_choice]); 308 return TRUE; 309 } 310 311 /* 312 * Get the previous enum in the list of choices. 313 */ 314 static int 315 prev_enum(FIELD *field, char *args) 316 { 317 enum_args *ta; 318 unsigned cur_choice; 319 320 if (args == NULL) 321 return FALSE; 322 323 ta = (enum_args *) (void *) field->args; 324 325 #ifdef DEBUG 326 fprintf(dbg, "prev_enum: attempt to match \'%s\'\n", args); 327 #endif 328 329 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 330 ta->exact, args, &cur_choice) == FALSE) { 331 #ifdef DEBUG 332 fprintf(dbg, "prev_enum: match failed\n"); 333 #endif 334 return FALSE; 335 } 336 337 #ifdef DEBUG 338 fprintf(dbg, "prev_enum: cur_choice is %d\n", cur_choice); 339 #endif 340 if (cur_choice == 0) 341 cur_choice = ta->num_choices - 1; 342 else 343 cur_choice--; 344 345 #ifdef DEBUG 346 fprintf(dbg, "prev_enum: cur_choice is %d on exit\n", cur_choice); 347 #endif 348 349 set_field_buffer(field, 0, ta->choices[cur_choice]); 350 return TRUE; 351 } 352 353 354 static FIELDTYPE builtin_enum = { 355 _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 356 0, /* refcount */ 357 NULL, /* link */ 358 create_enum_args, /* make_args */ 359 copy_enum_args, /* copy_args */ 360 free_enum_args, /* free_args */ 361 enum_check_field, /* field_check */ 362 NULL, /* char_check */ 363 next_enum, /* next_choice */ 364 prev_enum /* prev_choice */ 365 }; 366 367 FIELDTYPE *TYPE_ENUM = &builtin_enum; 368 369 370