xref: /netbsd/lib/libform/type_enum.c (revision 6550d01e)
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