/* $NetBSD: type_enum.c,v 1.12 2016/03/09 19:47:13 christos Exp $ */ /*- * Copyright (c) 1998-1999 Brett Lymn * (blymn@baea.com.au, brett_lymn@yahoo.com.au) * All rights reserved. * * This code has been donated to The NetBSD Foundation by the Author. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * */ #include __RCSID("$NetBSD: type_enum.c,v 1.12 2016/03/09 19:47:13 christos Exp $"); #include #include #include #include "form.h" #include "internals.h" /* * Prototypes. */ static int trim_blanks(char *field); /* * The enum type handling. */ typedef struct { char **choices; unsigned num_choices; bool ignore_case; bool exact; } enum_args; /* * Find the first non-blank character at the end of a field, return the * index of that character. */ static int trim_blanks(char *field) { int i; i = (int) strlen(field); if (i > 0) i--; else return 0; while ((i > 0) && isblank((unsigned char)field[i])) i--; return i; } /* * Create the enum arguments structure from the given args. Return NULL * if the call fails, otherwise return a pointer to the structure allocated. */ static char * create_enum_args(va_list *args) { enum_args *new; char **choices; new = malloc(sizeof(*new)); if (new == NULL) return NULL; new->choices = va_arg(*args, char **); new->ignore_case = (va_arg(*args, int)) ? TRUE : FALSE; new->exact = (va_arg(*args, int)) ? TRUE : FALSE; _formi_dbg_printf("%s: ignore_case %d, no_blanks %d\n", __func__, new->ignore_case, new->exact); /* count the choices we have */ choices = new->choices; new->num_choices = 0; while (*choices != NULL) { _formi_dbg_printf("%s: choice[%u] = \'%s\'\n", __func__, new->num_choices, new->choices[new->num_choices]); new->num_choices++; choices++; } _formi_dbg_printf("%s: have %u choices\n", __func__, new->num_choices); return (void *) new; } /* * Copy the enum argument structure. */ static char * copy_enum_args(char *args) { enum_args *new; new = (enum_args *) malloc(sizeof(enum_args)); if (new != NULL) bcopy(args, new, sizeof(enum_args)); return (void *) new; } /* * Free the allocated storage associated with the type arguments. */ static void free_enum_args(char *args) { if (args != NULL) free(args); } /* * Attempt to match the string in this to the choices given. Returns * TRUE if match found otherwise FALSE. * */ static bool match_enum(char **choices, unsigned num_choices, bool ignore_case, bool exact, char *this, unsigned *match_num) { unsigned i, start, end, enum_start, blen, elen, enum_end; bool cur_match; start = _formi_skip_blanks(this, 0); end = trim_blanks(this); if (end >= start) blen = (unsigned) (strlen(&this[start]) - strlen(&this[end]) + 1); else blen = 0; _formi_dbg_printf("%s: start %u, blen %u\n", __func__, start, blen); for (i = 0; i < num_choices; i++) { enum_start = _formi_skip_blanks(choices[i], 0); enum_end = trim_blanks(choices[i]); if (enum_end >= enum_start) elen = (unsigned) (strlen(&choices[i][enum_start]) - strlen(&choices[i][enum_end]) + 1); else elen = 0; _formi_dbg_printf("%s: checking choice \'%s\'\n", __func__, choices[i]); _formi_dbg_printf("%s: enum_start %u, elen %u\n", __func__, enum_start, elen); /* don't bother if we are after an exact match * and the test length is not equal to the enum * in question - it will never match. */ if ((exact == TRUE) && (blen != elen)) continue; /* * If the test length is longer than the enum * length then there is no chance of a match * so we skip. */ if ((exact != TRUE) && (blen > elen)) continue; if (ignore_case) cur_match = (strncasecmp(&choices[i][enum_start], &this[start], (size_t)blen) == 0) ? TRUE : FALSE; else cur_match = (strncmp(&choices[i][enum_start], &this[start], (size_t) blen) == 0) ? TRUE : FALSE; _formi_dbg_printf("%s: curmatch is %s\n", __func__, (cur_match == TRUE)? "TRUE" : "FALSE"); if (cur_match == TRUE) { *match_num = i; return TRUE; } } _formi_dbg_printf("%s: no match found\n", __func__); return FALSE; } /* * Check the contents of the field buffer match one of the enum strings only. */ static int enum_check_field(FIELD *field, char *args) { enum_args *ta; unsigned match_num; if (args == NULL) return FALSE; ta = (enum_args *) (void *) field->args; if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, ta->exact, args, &match_num) == TRUE) { _formi_dbg_printf("%s: We matched, match_num %u\n", __func__, match_num); _formi_dbg_printf("%s: buffer is \'%s\'\n", __func__, ta->choices[match_num]); set_field_buffer(field, 0, ta->choices[match_num]); return TRUE; } return FALSE; } /* * Get the next enum in the list of choices. */ static int next_enum(FIELD *field, char *args) { enum_args *ta; unsigned cur_choice; if (args == NULL) return FALSE; ta = (enum_args *) (void *) field->args; _formi_dbg_printf("%s: attempt to match \'%s\'\n", __func__, args); if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, ta->exact, args, &cur_choice) == FALSE) { _formi_dbg_printf("%s: match failed\n", __func__); return FALSE; } _formi_dbg_printf("%s: cur_choice is %u\n", __func__, cur_choice); cur_choice++; if (cur_choice >= ta->num_choices) cur_choice = 0; _formi_dbg_printf("%s: cur_choice is %u on exit\n", __func__, cur_choice); set_field_buffer(field, 0, ta->choices[cur_choice]); return TRUE; } /* * Get the previous enum in the list of choices. */ static int prev_enum(FIELD *field, char *args) { enum_args *ta; unsigned cur_choice; if (args == NULL) return FALSE; ta = (enum_args *) (void *) field->args; _formi_dbg_printf("%s: attempt to match \'%s\'\n", __func__, args); if (match_enum(ta->choices, ta->num_choices, ta->ignore_case, ta->exact, args, &cur_choice) == FALSE) { _formi_dbg_printf("%s: match failed\n", __func__); return FALSE; } _formi_dbg_printf("%s: cur_choice is %u\n", __func__, cur_choice); if (cur_choice == 0) cur_choice = ta->num_choices - 1; else cur_choice--; _formi_dbg_printf("%s: cur_choice is %u on exit\n", __func__, cur_choice); set_field_buffer(field, 0, ta->choices[cur_choice]); return TRUE; } static FIELDTYPE builtin_enum = { _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 0, /* refcount */ NULL, /* link */ create_enum_args, /* make_args */ copy_enum_args, /* copy_args */ free_enum_args, /* free_args */ enum_check_field, /* field_check */ NULL, /* char_check */ next_enum, /* next_choice */ prev_enum /* prev_choice */ }; FIELDTYPE *TYPE_ENUM = &builtin_enum;