1 /* $NetBSD: type_enum.c,v 1.6 2001/06/13 10:45:59 wiz 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 <stdlib.h> 33 #include <strings.h> 34 #include "form.h" 35 #include "internals.h" 36 37 /* 38 * The enum type handling. 39 */ 40 41 typedef struct 42 { 43 char **choices; 44 unsigned num_choices; 45 bool ignore_case; 46 bool no_blanks; 47 } enum_args; 48 49 /* 50 * Create the enum arguments structure from the given args. Return NULL 51 * if the call fails, otherwise return a pointer to the structure allocated. 52 */ 53 static char * 54 create_enum_args(va_list *args) 55 { 56 enum_args *new; 57 char **choices; 58 59 new = (enum_args *) malloc(sizeof(enum_args)); 60 61 if (new != NULL) { 62 new->choices = va_arg(*args, char **); 63 new->ignore_case = (va_arg(*args, int)) ? TRUE : FALSE; 64 new->no_blanks = (va_arg(*args, int)) ? TRUE : FALSE; 65 66 #ifdef DEBUG 67 if (_formi_create_dbg_file() != E_OK) 68 return NULL; 69 fprintf(dbg, 70 "create_enum_args: ignore_case %d, no_blanks %d\n", 71 new->ignore_case, new->no_blanks); 72 #endif 73 74 /* count the choices we have */ 75 choices = new->choices; 76 new->num_choices = 0; 77 while (*choices != NULL) { 78 #ifdef DEBUG 79 fprintf(dbg, "create_enum_args: choice[%d] = \'%s\'\n", 80 new->num_choices, 81 new->choices[new->num_choices]); 82 #endif 83 new->num_choices++; 84 choices++; 85 } 86 #ifdef DEBUG 87 fprintf(dbg, "create_enum_args: have %d choices\n", 88 new->num_choices); 89 #endif 90 91 } 92 93 return (void *) new; 94 } 95 96 /* 97 * Copy the the enum argument structure. 98 */ 99 static char * 100 copy_enum_args(char *args) 101 { 102 enum_args *new; 103 104 new = (enum_args *) malloc(sizeof(enum_args)); 105 106 if (new != NULL) 107 bcopy(args, new, sizeof(enum_args)); 108 109 return (void *) new; 110 } 111 112 /* 113 * Free the allocated storage associated with the type arguments. 114 */ 115 static void 116 free_enum_args(char *args) 117 { 118 if (args != NULL) 119 free(args); 120 } 121 122 /* 123 * Attempt to match the string in this to the choices given. Returns 124 * TRUE if match found otherwise FALSE. 125 * 126 */ 127 static bool 128 match_enum(char **choices, unsigned num_choices, bool ignore_case, 129 bool no_blanks, char *this, unsigned *match_num) 130 { 131 unsigned i, start, enum_start, blen, elen, trailing; 132 bool cur_match; 133 134 start = _formi_skip_blanks(this, 0); 135 blen = strlen(&this[start]); 136 137 #ifdef DEBUG 138 fprintf(dbg, "match_enum: start %d, blen %d\n", start, blen); 139 #endif 140 for (i = 0; i < num_choices; i++) { 141 enum_start = _formi_skip_blanks(choices[i], 0); 142 elen = strlen(&choices[i][enum_start]); 143 #ifdef DEBUG 144 fprintf(dbg, "match_enum: checking choice \'%s\'\n", 145 choices[i]); 146 fprintf(dbg, "match_enum: enum_start %d, elen %d\n", 147 enum_start, elen); 148 #endif 149 150 /* don't bother if blanks are significant and the 151 * lengths don't match - no chance of a hit. 152 */ 153 if ((no_blanks == TRUE) && (blen > elen)) 154 continue; 155 156 if (ignore_case) 157 cur_match = (strncasecmp(&choices[i][enum_start], 158 &this[start], elen) == 0) ? 159 TRUE : FALSE; 160 else 161 cur_match = (strncmp(&choices[i][enum_start], 162 &this[start], elen) == 0) ? 163 TRUE : FALSE; 164 165 #ifdef DEBUG 166 fprintf(dbg, "match_enum: curmatch is %s\n", 167 (cur_match == TRUE)? "TRUE" : "FALSE"); 168 #endif 169 170 /* if trailing blanks not allowed and we matched 171 * and the buffer & enum element are the same size 172 * then we have a match 173 */ 174 if (no_blanks && cur_match && (elen == blen)) { 175 #ifdef DEBUG 176 fprintf(dbg, 177 "match_enum: no_blanks set and no trailing stuff\n"); 178 #endif 179 *match_num = i; 180 return TRUE; 181 } 182 183 /* 184 * If trailing blanks allowed and we matched then check 185 * we only have trailing blanks, match if this is true. 186 * Note that we continue on here to see if there is a 187 * better match.... 188 */ 189 if (!no_blanks && cur_match) { 190 trailing = _formi_skip_blanks(this, start + blen); 191 if (this[trailing] == '\0') { 192 #ifdef DEBUG 193 fprintf(dbg, 194 "match_enum: no_blanks false and only trailing blanks found\n"); 195 #endif 196 *match_num = i; 197 return TRUE; 198 } 199 } 200 } 201 202 #ifdef DEBUG 203 fprintf(dbg, "match_enum: no match found\n"); 204 #endif 205 return FALSE; 206 } 207 208 /* 209 * Check the contents of the field buffer match one of the enum strings only. 210 */ 211 static int 212 enum_check_field(FIELD *field, char *args) 213 { 214 enum_args *ta; 215 unsigned match_num; 216 217 if (args == NULL) 218 return FALSE; 219 220 ta = (enum_args *) (void *) field->args; 221 222 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 223 ta->no_blanks, args, &match_num) == TRUE) { 224 #ifdef DEBUG 225 fprintf(dbg, "enum_check_field: We matched, match_num %d\n", 226 match_num); 227 fprintf(dbg, "enum_check_field: buffer is \'%s\'\n", 228 ta->choices[match_num]); 229 #endif 230 set_field_buffer(field, 0, ta->choices[match_num]); 231 return TRUE; 232 } 233 234 return FALSE; 235 } 236 237 /* 238 * Get the next enum in the list of choices. 239 */ 240 static int 241 next_enum(FIELD *field, char *args) 242 { 243 enum_args *ta; 244 unsigned cur_choice; 245 246 if (args == NULL) 247 return FALSE; 248 249 ta = (enum_args *) (void *) field->args; 250 251 #ifdef DEBUG 252 fprintf(dbg, "next_enum: attempt to match \'%s\'\n", args); 253 #endif 254 255 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 256 ta->no_blanks, args, &cur_choice) == FALSE) { 257 #ifdef DEBUG 258 fprintf(dbg, "next_enum: match failed\n"); 259 #endif 260 return FALSE; 261 } 262 263 #ifdef DEBUG 264 fprintf(dbg, "next_enum: cur_choice is %d\n", cur_choice); 265 #endif 266 267 cur_choice++; 268 269 if (cur_choice >= ta->num_choices) 270 cur_choice = 0; 271 272 #ifdef DEBUG 273 fprintf(dbg, "next_enum: cur_choice is %d on exit\n", 274 cur_choice); 275 #endif 276 277 set_field_buffer(field, 0, ta->choices[cur_choice]); 278 return TRUE; 279 } 280 281 /* 282 * Get the previous enum in the list of choices. 283 */ 284 static int 285 prev_enum(FIELD *field, char *args) 286 { 287 enum_args *ta; 288 unsigned cur_choice; 289 290 if (args == NULL) 291 return FALSE; 292 293 ta = (enum_args *) (void *) field->args; 294 295 #ifdef DEBUG 296 fprintf(dbg, "prev_enum: attempt to match \'%s\'\n", args); 297 #endif 298 299 if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, 300 ta->no_blanks, args, &cur_choice) == FALSE) { 301 #ifdef DEBUG 302 fprintf(dbg, "prev_enum: match failed\n"); 303 #endif 304 return FALSE; 305 } 306 307 #ifdef DEBUG 308 fprintf(dbg, "prev_enum: cur_choice is %d\n", cur_choice); 309 #endif 310 if (cur_choice == 0) 311 cur_choice = ta->num_choices - 1; 312 else 313 cur_choice--; 314 315 #ifdef DEBUG 316 fprintf(dbg, "prev_enum: cur_choice is %d on exit\n", cur_choice); 317 #endif 318 319 set_field_buffer(field, 0, ta->choices[cur_choice]); 320 return TRUE; 321 } 322 323 324 static FIELDTYPE builtin_enum = { 325 _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 326 0, /* refcount */ 327 NULL, /* link */ 328 create_enum_args, /* make_args */ 329 copy_enum_args, /* copy_args */ 330 free_enum_args, /* free_args */ 331 enum_check_field, /* field_check */ 332 NULL, /* char_check */ 333 next_enum, /* next_choice */ 334 prev_enum /* prev_choice */ 335 }; 336 337 FIELDTYPE *TYPE_ENUM = &builtin_enum; 338 339 340