xref: /freebsd/contrib/ncurses/form/fty_enum.c (revision faf25f48d601ae39f5752602f3020e2e92605625)
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 *
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 *
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 *)&params);
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 *
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
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
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
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
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
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 *)
436 _nc_TYPE_ENUM(void)
437 {
438   return TYPE_ENUM;
439 }
440 #endif
441 
442 /* fty_enum.c ends here */
443