1 /****************************************************************************
2 * Copyright 2020,2021 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.33 2021/06/17 21:11:08 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) != 0)
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) != 0)
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 }
213 free(argp);
214 }
215 }
216
217 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
218 #define NOMATCH 0
219 #define PARTIAL 1
220 #define EXACT 2
221
222 /*---------------------------------------------------------------------------
223 | Facility : libnform
224 | Function : static int Compare(const unsigned char * s,
225 | const unsigned char * buf,
226 | bool ccase )
227 |
228 | Description : Check whether or not the text in 'buf' matches the
229 | text in 's', at least partial.
230 |
231 | Return Values : NOMATCH - buffer doesn't match
232 | PARTIAL - buffer matches partially
233 | EXACT - buffer matches exactly
234 +--------------------------------------------------------------------------*/
235 static int
Compare(const unsigned char * s,const unsigned char * buf,bool ccase)236 Compare(const unsigned char *s, const unsigned char *buf,
237 bool ccase)
238 {
239 SKIP_SPACE(buf); /* Skip leading spaces in both texts */
240 SKIP_SPACE(s);
241
242 if (*buf == '\0')
243 {
244 return (((*s) != '\0') ? NOMATCH : EXACT);
245 }
246 else
247 {
248 if (ccase)
249 {
250 while (*s++ == *buf)
251 {
252 if (*buf++ == '\0')
253 return EXACT;
254 }
255 }
256 else
257 {
258 while (toupper(*s++) == toupper(*buf))
259 {
260 if (*buf++ == '\0')
261 return EXACT;
262 }
263 }
264 }
265 /* At this location buf points to the first character where it no longer
266 matches with s. So if only blanks are following, we have a partial
267 match otherwise there is no match */
268 SKIP_SPACE(buf);
269 if (*buf)
270 return NOMATCH;
271
272 /* If it happens that the reference buffer is at its end, the partial
273 match is actually an exact match. */
274 return ((s[-1] != '\0') ? PARTIAL : EXACT);
275 }
276
277 /*---------------------------------------------------------------------------
278 | Facility : libnform
279 | Function : static bool Check_Enum_Field(
280 | FIELD * field,
281 | const void * argp)
282 |
283 | Description : Validate buffer content to be a valid enumeration value
284 |
285 | Return Values : TRUE - field is valid
286 | FALSE - field is invalid
287 +--------------------------------------------------------------------------*/
288 static bool
Check_Enum_Field(FIELD * field,const void * argp)289 Check_Enum_Field(FIELD *field, const void *argp)
290 {
291 char **kwds = ((const enumARG *)argp)->kwds;
292 bool ccase = ((const enumARG *)argp)->checkcase;
293 bool unique = ((const enumARG *)argp)->checkunique;
294 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
295 char *s, *t, *p;
296
297 while (kwds && (s = (*kwds++)))
298 {
299 int res;
300
301 if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
302 {
303 p = t = s; /* t is at least a partial match */
304 if ((unique && res != EXACT))
305 {
306 while (kwds && (p = *kwds++))
307 {
308 if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
309 {
310 if (res == EXACT)
311 {
312 t = p;
313 break;
314 }
315 else
316 t = (char *)0;
317 }
318 }
319 }
320 if (t)
321 {
322 set_field_buffer(field, 0, t);
323 return TRUE;
324 }
325 if (!p)
326 break;
327 }
328 }
329 return FALSE;
330 }
331
332 static const char *dummy[] =
333 {(char *)0};
334
335 /*---------------------------------------------------------------------------
336 | Facility : libnform
337 | Function : static bool Next_Enum(FIELD * field,
338 | const void * argp)
339 |
340 | Description : Check for the next enumeration value
341 |
342 | Return Values : TRUE - next value found and loaded
343 | FALSE - no next value loaded
344 +--------------------------------------------------------------------------*/
345 static bool
Next_Enum(FIELD * field,const void * argp)346 Next_Enum(FIELD *field, const void *argp)
347 {
348 const enumARG *args = (const enumARG *)argp;
349 char **kwds = args->kwds;
350 bool ccase = args->checkcase;
351 int cnt = args->count;
352 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
353
354 if (kwds)
355 {
356 while (cnt--)
357 {
358 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
359 break;
360 }
361 if (cnt <= 0)
362 kwds = args->kwds;
363 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
364 {
365 set_field_buffer(field, 0, *kwds);
366 return TRUE;
367 }
368 }
369 return FALSE;
370 }
371
372 /*---------------------------------------------------------------------------
373 | Facility : libnform
374 | Function : static bool Previous_Enum(
375 | FIELD * field,
376 | const void * argp)
377 |
378 | Description : Check for the previous enumeration value
379 |
380 | Return Values : TRUE - previous value found and loaded
381 | FALSE - no previous value loaded
382 +--------------------------------------------------------------------------*/
383 static bool
Previous_Enum(FIELD * field,const void * argp)384 Previous_Enum(FIELD *field, const void *argp)
385 {
386 const enumARG *args = (const enumARG *)argp;
387 int cnt = args->count;
388 char **kwds = &args->kwds[cnt - 1];
389 bool ccase = args->checkcase;
390 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
391
392 if (kwds)
393 {
394 while (cnt--)
395 {
396 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
397 break;
398 }
399
400 if (cnt <= 0)
401 kwds = &args->kwds[args->count - 1];
402
403 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
404 {
405 set_field_buffer(field, 0, *kwds);
406 return TRUE;
407 }
408 }
409 return FALSE;
410 }
411
412 static FIELDTYPE typeENUM =
413 {
414 _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
415 1, /* this is mutable, so we can't be const */
416 (FIELDTYPE *)0,
417 (FIELDTYPE *)0,
418 Make_Enum_Type,
419 Copy_Enum_Type,
420 Free_Enum_Type,
421 INIT_FT_FUNC(Check_Enum_Field),
422 INIT_FT_FUNC(NULL),
423 INIT_FT_FUNC(Next_Enum),
424 INIT_FT_FUNC(Previous_Enum),
425 #if NCURSES_INTEROP_FUNCS
426 Generic_Enum_Type
427 #endif
428 };
429
430 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM;
431
432 #if NCURSES_INTEROP_FUNCS
433 /* The next routines are to simplify the use of ncurses from
434 programming languages with restrictions on interop with C level
435 constructs (e.g. variable access or va_list + ellipsis constructs)
436 */
437 FORM_EXPORT(FIELDTYPE *)
_nc_TYPE_ENUM(void)438 _nc_TYPE_ENUM(void)
439 {
440 return TYPE_ENUM;
441 }
442 #endif
443
444 /* fty_enum.c ends here */
445