jcs's openbsd hax
openbsd
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
39MODULE_ID("$Id: fty_enum.c,v 1.12 2023/10/17 09:52:10 nicm Exp $")
40
41typedef struct
42 {
43 char **kwds;
44 int count;
45 bool checkcase;
46 bool checkunique;
47 }
48enumARG;
49
50typedef struct
51 {
52 char **kwds;
53 int ccase;
54 int cunique;
55 }
56enumParams;
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+--------------------------------------------------------------------------*/
66static void *
67Generic_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+--------------------------------------------------------------------------*/
130static void *
131Make_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+--------------------------------------------------------------------------*/
150static void *
151Copy_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+--------------------------------------------------------------------------*/
194static void
195Free_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+--------------------------------------------------------------------------*/
236static int
237Compare(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+--------------------------------------------------------------------------*/
289static bool
290Check_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
333static 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+--------------------------------------------------------------------------*/
346static bool
347Next_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+--------------------------------------------------------------------------*/
384static bool
385Previous_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
413static 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
431FORM_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*/
438FORM_EXPORT(FIELDTYPE *)
439_nc_TYPE_ENUM(void)
440{
441 return TYPE_ENUM;
442}
443#endif
444
445/* fty_enum.c ends here */