Home | History | Annotate | Line # | Download | only in libform
      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