1 /****************************************************************************
2 * Copyright 2020 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.31 2020/12/12 01:15:37 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 char **kptarget;
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 /* We copy the keywords, because we can't rely on the fact
100 that the caller doesn't relocate or free the memory used
101 for the keywords (maybe he has GC)
102 */
103 argp->kwds = typeMalloc(char *, cnt + 1);
104
105 kp = kwds;
106 if ((kptarget = argp->kwds) != 0)
107 {
108 while (kp && (*kp))
109 {
110 (*kptarget++) = strdup(*kp++);
111 }
112 *kptarget = (char *)0;
113 }
114 }
115 }
116 }
117 return (void *)argp;
118 }
119
120 /*---------------------------------------------------------------------------
121 | Facility : libnform
122 | Function : static void *Make_Enum_Type( va_list * ap )
123 |
124 | Description : Allocate structure for enumeration type argument.
125 |
126 | Return Values : Pointer to argument structure or NULL on error
127 +--------------------------------------------------------------------------*/
128 static void *
Make_Enum_Type(va_list * ap)129 Make_Enum_Type(va_list *ap)
130 {
131 enumParams params;
132
133 params.kwds = va_arg(*ap, char **);
134 params.ccase = va_arg(*ap, int);
135 params.cunique = va_arg(*ap, int);
136
137 return Generic_Enum_Type((void *)¶ms);
138 }
139
140 /*---------------------------------------------------------------------------
141 | Facility : libnform
142 | Function : static void *Copy_Enum_Type( const void * argp )
143 |
144 | Description : Copy structure for enumeration type argument.
145 |
146 | Return Values : Pointer to argument structure or NULL on error.
147 +--------------------------------------------------------------------------*/
148 static void *
Copy_Enum_Type(const void * argp)149 Copy_Enum_Type(const void *argp)
150 {
151 enumARG *result = (enumARG *)0;
152
153 if (argp)
154 {
155 const enumARG *ap = (const enumARG *)argp;
156
157 result = typeMalloc(enumARG, 1);
158
159 if (result)
160 {
161 T((T_CREATE("enumARG %p"), (void *)result));
162 *result = *ap;
163
164 if (ap->count > 0)
165 {
166 char **kptarget;
167 char **kp = ap->kwds;
168 result->kwds = typeMalloc(char *, 1 + ap->count);
169
170 if ((kptarget = result->kwds) != 0)
171 {
172 while (kp && (*kp))
173 {
174 (*kptarget++) = strdup(*kp++);
175 }
176 *kptarget = (char *)0;
177 }
178 }
179 }
180 }
181 return (void *)result;
182 }
183
184 /*---------------------------------------------------------------------------
185 | Facility : libnform
186 | Function : static void Free_Enum_Type( void * argp )
187 |
188 | Description : Free structure for enumeration type argument.
189 |
190 | Return Values : -
191 +--------------------------------------------------------------------------*/
192 static void
Free_Enum_Type(void * argp)193 Free_Enum_Type(void *argp)
194 {
195 if (argp)
196 {
197 const enumARG *ap = (const enumARG *)argp;
198
199 if (ap->kwds && ap->count > 0)
200 {
201 char **kp = ap->kwds;
202 int cnt = 0;
203
204 while (kp && (*kp))
205 {
206 free(*kp++);
207 cnt++;
208 }
209 assert(cnt == ap->count);
210 free(ap->kwds);
211 }
212 free(argp);
213 }
214 }
215
216 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
217 #define NOMATCH 0
218 #define PARTIAL 1
219 #define EXACT 2
220
221 /*---------------------------------------------------------------------------
222 | Facility : libnform
223 | Function : static int Compare(const unsigned char * s,
224 | const unsigned char * buf,
225 | bool ccase )
226 |
227 | Description : Check whether or not the text in 'buf' matches the
228 | text in 's', at least partial.
229 |
230 | Return Values : NOMATCH - buffer doesn't match
231 | PARTIAL - buffer matches partially
232 | EXACT - buffer matches exactly
233 +--------------------------------------------------------------------------*/
234 static int
Compare(const unsigned char * s,const unsigned char * buf,bool ccase)235 Compare(const unsigned char *s, const unsigned char *buf,
236 bool ccase)
237 {
238 SKIP_SPACE(buf); /* Skip leading spaces in both texts */
239 SKIP_SPACE(s);
240
241 if (*buf == '\0')
242 {
243 return (((*s) != '\0') ? NOMATCH : EXACT);
244 }
245 else
246 {
247 if (ccase)
248 {
249 while (*s++ == *buf)
250 {
251 if (*buf++ == '\0')
252 return EXACT;
253 }
254 }
255 else
256 {
257 while (toupper(*s++) == toupper(*buf))
258 {
259 if (*buf++ == '\0')
260 return EXACT;
261 }
262 }
263 }
264 /* At this location buf points to the first character where it no longer
265 matches with s. So if only blanks are following, we have a partial
266 match otherwise there is no match */
267 SKIP_SPACE(buf);
268 if (*buf)
269 return NOMATCH;
270
271 /* If it happens that the reference buffer is at its end, the partial
272 match is actually an exact match. */
273 return ((s[-1] != '\0') ? PARTIAL : EXACT);
274 }
275
276 /*---------------------------------------------------------------------------
277 | Facility : libnform
278 | Function : static bool Check_Enum_Field(
279 | FIELD * field,
280 | const void * argp)
281 |
282 | Description : Validate buffer content to be a valid enumeration value
283 |
284 | Return Values : TRUE - field is valid
285 | FALSE - field is invalid
286 +--------------------------------------------------------------------------*/
287 static bool
Check_Enum_Field(FIELD * field,const void * argp)288 Check_Enum_Field(FIELD *field, const void *argp)
289 {
290 char **kwds = ((const enumARG *)argp)->kwds;
291 bool ccase = ((const enumARG *)argp)->checkcase;
292 bool unique = ((const enumARG *)argp)->checkunique;
293 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
294 char *s, *t, *p;
295 int res;
296
297 while (kwds && (s = (*kwds++)))
298 {
299 if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
300 {
301 p = t = s; /* t is at least a partial match */
302 if ((unique && res != EXACT))
303 {
304 while (kwds && (p = *kwds++))
305 {
306 if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
307 {
308 if (res == EXACT)
309 {
310 t = p;
311 break;
312 }
313 else
314 t = (char *)0;
315 }
316 }
317 }
318 if (t)
319 {
320 set_field_buffer(field, 0, t);
321 return TRUE;
322 }
323 if (!p)
324 break;
325 }
326 }
327 return FALSE;
328 }
329
330 static const char *dummy[] =
331 {(char *)0};
332
333 /*---------------------------------------------------------------------------
334 | Facility : libnform
335 | Function : static bool Next_Enum(FIELD * field,
336 | const void * argp)
337 |
338 | Description : Check for the next enumeration value
339 |
340 | Return Values : TRUE - next value found and loaded
341 | FALSE - no next value loaded
342 +--------------------------------------------------------------------------*/
343 static bool
Next_Enum(FIELD * field,const void * argp)344 Next_Enum(FIELD *field, const void *argp)
345 {
346 const enumARG *args = (const enumARG *)argp;
347 char **kwds = args->kwds;
348 bool ccase = args->checkcase;
349 int cnt = args->count;
350 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
351
352 if (kwds)
353 {
354 while (cnt--)
355 {
356 if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
357 break;
358 }
359 if (cnt <= 0)
360 kwds = args->kwds;
361 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
362 {
363 set_field_buffer(field, 0, *kwds);
364 return TRUE;
365 }
366 }
367 return FALSE;
368 }
369
370 /*---------------------------------------------------------------------------
371 | Facility : libnform
372 | Function : static bool Previous_Enum(
373 | FIELD * field,
374 | const void * argp)
375 |
376 | Description : Check for the previous enumeration value
377 |
378 | Return Values : TRUE - previous value found and loaded
379 | FALSE - no previous value loaded
380 +--------------------------------------------------------------------------*/
381 static bool
Previous_Enum(FIELD * field,const void * argp)382 Previous_Enum(FIELD *field, const void *argp)
383 {
384 const enumARG *args = (const enumARG *)argp;
385 int cnt = args->count;
386 char **kwds = &args->kwds[cnt - 1];
387 bool ccase = args->checkcase;
388 unsigned char *bp = (unsigned char *)field_buffer(field, 0);
389
390 if (kwds)
391 {
392 while (cnt--)
393 {
394 if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
395 break;
396 }
397
398 if (cnt <= 0)
399 kwds = &args->kwds[args->count - 1];
400
401 if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
402 {
403 set_field_buffer(field, 0, *kwds);
404 return TRUE;
405 }
406 }
407 return FALSE;
408 }
409
410 static FIELDTYPE typeENUM =
411 {
412 _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
413 1, /* this is mutable, so we can't be const */
414 (FIELDTYPE *)0,
415 (FIELDTYPE *)0,
416 Make_Enum_Type,
417 Copy_Enum_Type,
418 Free_Enum_Type,
419 INIT_FT_FUNC(Check_Enum_Field),
420 INIT_FT_FUNC(NULL),
421 INIT_FT_FUNC(Next_Enum),
422 INIT_FT_FUNC(Previous_Enum),
423 #if NCURSES_INTEROP_FUNCS
424 Generic_Enum_Type
425 #endif
426 };
427
428 FORM_EXPORT_VAR(FIELDTYPE *) TYPE_ENUM = &typeENUM;
429
430 #if NCURSES_INTEROP_FUNCS
431 /* The next routines are to simplify the use of ncurses from
432 programming languages with restrictions on interop with C level
433 constructs (e.g. variable access or va_list + ellipsis constructs)
434 */
435 FORM_EXPORT(FIELDTYPE *)
_nc_TYPE_ENUM(void)436 _nc_TYPE_ENUM(void)
437 {
438 return TYPE_ENUM;
439 }
440 #endif
441
442 /* fty_enum.c ends here */
443