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