1 /* $OpenBSD: fty_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $ */
2 /****************************************************************************
3 * Copyright 2020,2021 Thomas E. Dickey *
4 * Copyright 1998-2009,2010 Free Software Foundation, Inc. *
5 * *
6 * Permission is hereby granted, free of charge, to any person obtaining a *
7 * copy of this software and associated documentation files (the *
8 * "Software"), to deal in the Software without restriction, including *
9 * without limitation the rights to use, copy, modify, merge, publish, *
10 * distribute, distribute with modifications, sublicense, and/or sell *
11 * copies of the Software, and to permit persons to whom the Software is *
12 * furnished to do so, subject to the following conditions: *
13 * *
14 * The above copyright notice and this permission notice shall be included *
15 * in all copies or substantial portions of the Software. *
16 * *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * *
25 * Except as contained in this notice, the name(s) of the above copyright *
26 * holders shall not be used in advertising or otherwise to promote the *
27 * sale, use or other dealings in this Software without prior written *
28 * authorization. *
29 ****************************************************************************/
30
31 /***************************************************************************
32 * *
33 * Author : Juergen Pfeifer *
34 * *
35 ***************************************************************************/
36
37 #include "form.priv.h"
38
39 MODULE_ID("$Id: fty_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $")
40
41 typedef struct
42 {
43 char **kwds;
44 int count;
45 bool checkcase;
46 bool checkunique;
47 }
48 enumARG;
49
50 typedef struct
51 {
52 char **kwds;
53 int ccase;
54 int cunique;
55 }
56 enumParams;
57
58 /*---------------------------------------------------------------------------
59 | Facility : libnform
60 | Function : static void *Generic_Enum_Type(void * arg)
61 |
62 | Description : Allocate structure for enumeration type argument.
63 |
64 | Return Values : Pointer to argument structure or NULL on error
65 +--------------------------------------------------------------------------*/
66 static void *
Generic_Enum_Type(void * arg)67 Generic_Enum_Type(void *arg)
68 {
69 enumARG *argp = (enumARG *)0;
70 enumParams *params = (enumParams *)arg;
71
72 if (params)
73 {
74 argp = typeMalloc(enumARG, 1);
75
76 if (argp)
77 {
78 int cnt = 0;
79 char **kp = (char **)0;
80 char **kwds = (char **)0;
81 int ccase, cunique;
82
83 T((T_CREATE("enumARG %p"), (void *)argp));
84 kwds = params->kwds;
85 ccase = params->ccase;
86 cunique = params->cunique;
87
88 argp->checkcase = ccase ? TRUE : FALSE;
89 argp->checkunique = cunique ? TRUE : FALSE;
90 argp->kwds = (char **)0;
91
92 kp = kwds;
93 while (kp && (*kp++))
94 cnt++;
95 argp->count = cnt;
96
97 if (cnt > 0)
98 {
99 char **kptarget;
100
101 /* We copy the keywords, because we can't rely on the fact
102 that the caller doesn't relocate or free the memory used
103 for the keywords (maybe he has GC)
104 */
105 argp->kwds = typeMalloc(char *, cnt + 1);
106
107 kp = kwds;
108 if ((kptarget = argp->kwds) != 0)
109 {
110 while (kp && (*kp))
111 {
112 (*kptarget++) = strdup(*kp++);
113 }
114 *kptarget = (char *)0;
115 }
116 }
117 }
118 }
119 return (void *)argp;
120 }
121
122 /*---------------------------------------------------------------------------
123 | Facility : libnform
124 | Function : static void *Make_Enum_Type( va_list * ap )
125 |
126 | Description : Allocate structure for enumeration type argument.
127 |
128 | Return Values : Pointer to argument structure or NULL on error
129 +--------------------------------------------------------------------------*/
130 static void *
Make_Enum_Type(va_list * ap)131 Make_Enum_Type(va_list *ap)
132 {
133 enumParams params;
134
135 params.kwds = va_arg(*ap, char **);
136 params.ccase = va_arg(*ap, int);
137 params.cunique = va_arg(*ap, int);
138
139 return Generic_Enum_Type((void *)¶ms);
140 }
141
142 /*---------------------------------------------------------------------------
143 | Facility : libnform
144 | Function : static void *Copy_Enum_Type( const void * argp )
145 |
146 | Description : Copy structure for enumeration type argument.
147 |
148 | Return Values : Pointer to argument structure or NULL on error.
149 +--------------------------------------------------------------------------*/
150 static void *
Copy_Enum_Type(const void * argp)151 Copy_Enum_Type(const void *argp)
152 {
153 enumARG *result = (enumARG *)0;
154
155 if (argp)
156 {
157 const enumARG *ap = (const enumARG *)argp;
158
159 result = typeMalloc(enumARG, 1);
160
161 if (result)
162 {
163 T((T_CREATE("enumARG %p"), (void *)result));
164 *result = *ap;
165
166 if (ap->count > 0)
167 {
168 char **kptarget;
169 char **kp = ap->kwds;
170 result->kwds = typeMalloc(char *, 1 + ap->count);
171
172 if ((kptarget = result->kwds) != 0)
173 {
174 while (kp && (*kp))
175 {
176 (*kptarget++) = strdup(*kp++);
177 }
178 *kptarget = (char *)0;
179 }
180 }
181 }
182 }
183 return (void *)result;
184 }
185
186 /*---------------------------------------------------------------------------
187 | Facility : libnform
188 | Function : static void Free_Enum_Type( void * argp )
189 |
190 | Description : Free structure for enumeration type argument.
191 |
192 | Return Values : -
193 +--------------------------------------------------------------------------*/
194 static void
Free_Enum_Type(void * argp)195 Free_Enum_Type(void *argp)
196 {
197 if (argp)
198 {
199 const enumARG *ap = (const enumARG *)argp;
200
201 if (ap->kwds && ap->count > 0)
202 {
203 char **kp = ap->kwds;
204 int cnt = 0;
205
206 while (kp && (*kp))
207 {
208 free(*kp++);
209 cnt++;
210 }
211 assert(cnt == ap->count);
212 free(ap->kwds);
213 }
214 free(argp);
215 }
216 }
217
218 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
219 #define NOMATCH 0
220 #define PARTIAL 1
221 #define EXACT 2
222
223 /*---------------------------------------------------------------------------
224 | Facility : libnform
225 | Function : static int Compare(const unsigned char * s,
226 | const unsigned char * buf,
227 | bool ccase )
228 |
229 | Description : Check whether or not the text in 'buf' matches the
230 | text in 's', at least partial.
231 |
232 | Return Values : NOMATCH - buffer doesn't match
233 | PARTIAL - buffer matches partially
234 | EXACT - buffer matches exactly
235 +--------------------------------------------------------------------------*/
236 static int
Compare(const unsigned char * s,const unsigned char * buf,bool ccase)237 Compare(const unsigned char *s, const unsigned char *buf,
238 bool ccase)
239 {
240 SKIP_SPACE(buf); /* Skip leading spaces in both texts */
241 SKIP_SPACE(s);
242
243 if (*buf == '\0')
244 {
245 return (((*s) != '\0') ? NOMATCH : EXACT);
246 }
247 else
248 {
249 if (ccase)
250 {
251 while (*s++ == *buf)
252 {
253 if (*buf++ == '\0')
254 return EXACT;
255 }
256 }
257 else
258 {
259 while (toupper(*s++) == toupper(*buf))
260 {
261 if (*buf++ == '\0')
262 return EXACT;
263 }
264 }
265 }
266 /* At this location buf points to the first character where it no longer
267 matches with s. So if only blanks are following, we have a partial
268 match otherwise there is no match */
269 SKIP_SPACE(buf);
270 if (*buf)
271 return NOMATCH;
272
273 /* If it happens that the reference buffer is at its end, the partial
274 match is actually an exact match. */
275 return ((s[-1] != '\0') ? PARTIAL : EXACT);
276 }
277
278 /*---------------------------------------------------------------------------
279 | Facility : libnform
280 | Function : static bool Check_Enum_Field(
281 | FIELD * field,
282 | const void * argp)
283 |
284 | Description : Validate buffer content to be a valid enumeration value
285 |
286 | Return Values : TRUE - field is valid
287 | FALSE - field is invalid
288 +--------------------------------------------------------------------------*/
289 static bool
Check_Enum_Field(FIELD * field,const void * argp)290 Check_Enum_Field(FIELD *field, const void *argp)
291 {
292 char **kwds = ((const enumARG *)argp)->kwds;
293 bool ccase = ((const enumARG *)argp)->checkcase;
294 bool unique = ((const enumARG *)argp)->checkunique;
295 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
296 char *s, *t, *p;
297
298 while (kwds && (s = (*kwds++)))
299 {
300 int res;
301
302 if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
303 {
304 p = t = s; /* t is at least a partial match */
305 if ((unique && res != EXACT))
306 {
307 while (kwds && (p = *kwds++))
308 {
309 if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
310 {
311 if (res == EXACT)
312 {
313 t = p;
314 break;
315 }
316 else
317 t = (char *)0;
318 }
319 }
320 }
321 if (t)
322 {
323 set_field_buffer(field, 0, t);
324 return TRUE;
325 }
326 if (!p)
327 break;
328 }
329 }
330 return FALSE;
331 }
332
333 static const char *dummy[] =
334 {(char *)0};
335
336 /*---------------------------------------------------------------------------
337 | Facility : libnform
338 | Function : static bool Next_Enum(FIELD * field,
339 | const void * argp)
340 |
341 | Description : Check for the next enumeration value
342 |
343 | Return Values : TRUE - next value found and loaded
344 | FALSE - no next value loaded
345 +--------------------------------------------------------------------------*/
346 static bool
Next_Enum(FIELD * field,const void * argp)347 Next_Enum(FIELD *field, const void *argp)
348 {
349 const enumARG *args = (const enumARG *)argp;
350 char **kwds = args->kwds;
351 bool ccase = args->checkcase;
352 int cnt = args->count;
353 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
354
355 if (kwds)
356 {
357 while (cnt--)
358 {
359 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
360 break;
361 }
362 if (cnt <= 0)
363 kwds = args->kwds;
364 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
365 {
366 set_field_buffer(field, 0, *kwds);
367 return TRUE;
368 }
369 }
370 return FALSE;
371 }
372
373 /*---------------------------------------------------------------------------
374 | Facility : libnform
375 | Function : static bool Previous_Enum(
376 | FIELD * field,
377 | const void * argp)
378 |
379 | Description : Check for the previous enumeration value
380 |
381 | Return Values : TRUE - previous value found and loaded
382 | FALSE - no previous value loaded
383 +--------------------------------------------------------------------------*/
384 static bool
Previous_Enum(FIELD * field,const void * argp)385 Previous_Enum(FIELD *field, const void *argp)
386 {
387 const enumARG *args = (const enumARG *)argp;
388 int cnt = args->count;
389 char **kwds = &args->kwds[cnt - 1];
390 bool ccase = args->checkcase;
391 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
392
393 if (kwds)
394 {
395 while (cnt--)
396 {
397 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
398 break;
399 }
400
401 if (cnt <= 0)
402 kwds = &args->kwds[args->count - 1];
403
404 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
405 {
406 set_field_buffer(field, 0, *kwds);
407 return TRUE;
408 }
409 }
410 return FALSE;
411 }
412
413 static FIELDTYPE typeENUM =
414 {
415 _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
416 1, /* this is mutable, so we can't be const */
417 (FIELDTYPE *)0,
418 (FIELDTYPE *)0,
419 Make_Enum_Type,
420 Copy_Enum_Type,
421 Free_Enum_Type,
422 INIT_FT_FUNC(Check_Enum_Field),
423 INIT_FT_FUNC(NULL),
424 INIT_FT_FUNC(Next_Enum),
425 INIT_FT_FUNC(Previous_Enum),
426 #if NCURSES_INTEROP_FUNCS
427 Generic_Enum_Type
428 #endif
429 };
430
431 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM;
432
433 #if NCURSES_INTEROP_FUNCS
434 /* The next routines are to simplify the use of ncurses from
435 programming languages with restrictions on interop with C level
436 constructs (e.g. variable access or va_list + ellipsis constructs)
437 */
438 FORM_EXPORT(FIELDTYPE *)
_nc_TYPE_ENUM(void)439 _nc_TYPE_ENUM(void)
440 {
441 return TYPE_ENUM;
442 }
443 #endif
444
445 /* fty_enum.c ends here */
446