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