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@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.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
trim_blanks(char * field)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 *
create_enum_args(va_list * args)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 *
copy_enum_args(char * args)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
free_enum_args(char * args)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
match_enum(char ** choices,unsigned num_choices,bool ignore_case,bool exact,char * this,unsigned * match_num)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
enum_check_field(FIELD * field,char * args)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
next_enum(FIELD * field,char * args)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
prev_enum(FIELD * field,char * args)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