ncurses 6.2 - patch 20211009
[ncurses.git] / form / fty_enum.c
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 *
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 *
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 *)&params);
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 *
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
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
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
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
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
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 *)
438 _nc_TYPE_ENUM(void)
439 {
440   return TYPE_ENUM;
441 }
442 #endif
443
444 /* fty_enum.c ends here */