1 /****************************************************************************
2 * Copyright 2020-2024,2025 Thomas E. Dickey *
3 * Copyright 1998-2009,2010 Free Software Foundation, Inc. *
4 * *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
12 * *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
15 * *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
27 * authorization. *
28 ****************************************************************************/
29
30 /***************************************************************************
31 * *
32 * Author : Juergen Pfeifer *
33 * *
34 ***************************************************************************/
35
36 #include "form.priv.h"
37
38 MODULE_ID("$Id: fty_enum.c,v 1.36 2025/11/15 18:12:38 tom Exp $")
39
40 typedef struct
41 {
42 char **kwds;
43 int count;
44 bool checkcase;
45 bool checkunique;
46 }
47 enumARG;
48
49 typedef struct
50 {
51 char **kwds;
52 int ccase;
53 int cunique;
54 }
55 enumParams;
56
57 /*---------------------------------------------------------------------------
58 | Facility : libnform
59 | Function : static void *Generic_Enum_Type(void * arg)
60 |
61 | Description : Allocate structure for enumeration type argument.
62 |
63 | Return Values : Pointer to argument structure or NULL on error
64 +--------------------------------------------------------------------------*/
65 static void *
Generic_Enum_Type(void * arg)66 Generic_Enum_Type(void *arg)
67 {
68 enumARG *argp = (enumARG *)0;
69 enumParams *params = (enumParams *)arg;
70
71 if (params)
72 {
73 argp = typeMalloc(enumARG, 1);
74
75 if (argp)
76 {
77 int cnt = 0;
78 char **kp = (char **)0;
79 char **kwds = (char **)0;
80 int ccase, cunique;
81
82 T((T_CREATE("enumARG %p"), (void *)argp));
83 kwds = params->kwds;
84 ccase = params->ccase;
85 cunique = params->cunique;
86
87 argp->checkcase = ccase ? TRUE : FALSE;
88 argp->checkunique = cunique ? TRUE : FALSE;
89 argp->kwds = (char **)0;
90
91 kp = kwds;
92 while (kp && (*kp++))
93 cnt++;
94 argp->count = cnt;
95
96 if (cnt > 0)
97 {
98 char **kptarget;
99
100 /* We copy the keywords, because we can't rely on the fact
101 that the caller doesn't relocate or free the memory used
102 for the keywords (maybe he has GC)
103 */
104 argp->kwds = typeMalloc(char *, cnt + 1);
105
106 kp = kwds;
107 if ((kptarget = argp->kwds) != NULL)
108 {
109 while (kp && (*kp))
110 {
111 (*kptarget++) = strdup(*kp++);
112 }
113 *kptarget = (char *)0;
114 }
115 }
116 }
117 }
118 return (void *)argp;
119 }
120
121 /*---------------------------------------------------------------------------
122 | Facility : libnform
123 | Function : static void *Make_Enum_Type( va_list * ap )
124 |
125 | Description : Allocate structure for enumeration type argument.
126 |
127 | Return Values : Pointer to argument structure or NULL on error
128 +--------------------------------------------------------------------------*/
129 static void *
Make_Enum_Type(va_list * ap)130 Make_Enum_Type(va_list *ap)
131 {
132 enumParams params;
133
134 params.kwds = va_arg(*ap, char **);
135 params.ccase = va_arg(*ap, int);
136 params.cunique = va_arg(*ap, int);
137
138 return Generic_Enum_Type((void *)¶ms);
139 }
140
141 /*---------------------------------------------------------------------------
142 | Facility : libnform
143 | Function : static void *Copy_Enum_Type( const void * argp )
144 |
145 | Description : Copy structure for enumeration type argument.
146 |
147 | Return Values : Pointer to argument structure or NULL on error.
148 +--------------------------------------------------------------------------*/
149 static void *
Copy_Enum_Type(const void * argp)150 Copy_Enum_Type(const void *argp)
151 {
152 enumARG *result = (enumARG *)0;
153
154 if (argp)
155 {
156 const enumARG *ap = (const enumARG *)argp;
157
158 result = typeMalloc(enumARG, 1);
159
160 if (result)
161 {
162 T((T_CREATE("enumARG %p"), (void *)result));
163 *result = *ap;
164
165 if (ap->count > 0)
166 {
167 char **kptarget;
168 char **kp = ap->kwds;
169 result->kwds = typeMalloc(char *, 1 + ap->count);
170
171 if ((kptarget = result->kwds) != NULL)
172 {
173 while (kp && (*kp))
174 {
175 (*kptarget++) = strdup(*kp++);
176 }
177 *kptarget = (char *)0;
178 }
179 }
180 }
181 }
182 return (void *)result;
183 }
184
185 /*---------------------------------------------------------------------------
186 | Facility : libnform
187 | Function : static void Free_Enum_Type( void * argp )
188 |
189 | Description : Free structure for enumeration type argument.
190 |
191 | Return Values : -
192 +--------------------------------------------------------------------------*/
193 static void
Free_Enum_Type(void * argp)194 Free_Enum_Type(void *argp)
195 {
196 if (argp)
197 {
198 const enumARG *ap = (const enumARG *)argp;
199
200 if (ap->kwds && ap->count > 0)
201 {
202 char **kp = ap->kwds;
203 int cnt = 0;
204
205 while (kp && (*kp))
206 {
207 free(*kp++);
208 cnt++;
209 }
210 assert(cnt == ap->count);
211 free(ap->kwds);
212 (void) cnt;
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 const unsigned char *bp = (unsigned char *)field_buffer(field, 0);
296 const char *s, *t, *p;
297
298 while (kwds && (s = (*kwds++)))
299 {
300 int res;
301
302 if ((res = Compare((const 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((const unsigned char *)p, bp, ccase)) != NOMATCH)
310 {
311 if (res == EXACT)
312 {
313 t = p;
314 break;
315 }
316 else
317 t = NULL;
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 const unsigned char *bp = (const unsigned char *)field_buffer(field, 0);
352
353 if (kwds)
354 {
355 int cnt = args->count;
356 bool ccase = args->checkcase;
357
358 while (cnt--)
359 {
360 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
361 break;
362 }
363 if (cnt <= 0)
364 kwds = args->kwds;
365 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
366 {
367 set_field_buffer(field, 0, *kwds);
368 return TRUE;
369 }
370 }
371 return FALSE;
372 }
373
374 /*---------------------------------------------------------------------------
375 | Facility : libnform
376 | Function : static bool Previous_Enum(
377 | FIELD * field,
378 | const void * argp)
379 |
380 | Description : Check for the previous enumeration value
381 |
382 | Return Values : TRUE - previous value found and loaded
383 | FALSE - no previous value loaded
384 +--------------------------------------------------------------------------*/
385 static bool
Previous_Enum(FIELD * field,const void * argp)386 Previous_Enum(FIELD *field, const void *argp)
387 {
388 const enumARG *args = (const enumARG *)argp;
389 int cnt = args->count;
390 char **kwds = &args->kwds[cnt - 1];
391 const unsigned char *bp = (const unsigned char *)field_buffer(field, 0);
392
393 bool ccase = args->checkcase;
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