xref: /freebsd/contrib/ncurses/form/fty_enum.c (revision 0957b409)
1 /****************************************************************************
2  * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /***************************************************************************
30 *                                                                          *
31 *  Author : Juergen Pfeifer                                                *
32 *                                                                          *
33 ***************************************************************************/
34 
35 #include "form.priv.h"
36 
37 MODULE_ID("$Id: fty_enum.c,v 1.26 2010/05/01 21:11:07 tom Exp $")
38 
39 typedef struct
40   {
41     char **kwds;
42     int count;
43     bool checkcase;
44     bool checkunique;
45   }
46 enumARG;
47 
48 typedef struct
49   {
50     char **kwds;
51     int ccase;
52     int cunique;
53   }
54 enumParams;
55 
56 /*---------------------------------------------------------------------------
57 |   Facility      :  libnform
58 |   Function      :  static void *Generic_Enum_Type(void * arg)
59 |
60 |   Description   :  Allocate structure for enumeration type argument.
61 |
62 |   Return Values :  Pointer to argument structure or NULL on error
63 +--------------------------------------------------------------------------*/
64 static void *
65 Generic_Enum_Type(void *arg)
66 {
67   enumARG *argp = (enumARG *)0;
68   enumParams *params = (enumParams *) arg;
69 
70   if (params)
71     {
72       argp = typeMalloc(enumARG, 1);
73 
74       if (argp)
75 	{
76 	  int cnt = 0;
77 	  char **kp = (char **)0;
78 	  char **kwds = (char **)0;
79 	  char **kptarget;
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 	      /* We copy the keywords, because we can't rely on the fact
99 	         that the caller doesn't relocate or free the memory used
100 	         for the keywords (maybe he has GC)
101 	       */
102 	      argp->kwds = typeMalloc(char *, cnt + 1);
103 
104 	      kp = kwds;
105 	      if ((kptarget = argp->kwds) != 0)
106 		{
107 		  while (kp && (*kp))
108 		    {
109 		      (*kptarget++) = strdup(*kp++);
110 		    }
111 		  *kptarget = (char *)0;
112 		}
113 	    }
114 	}
115     }
116   return (void *)argp;
117 }
118 
119 /*---------------------------------------------------------------------------
120 |   Facility      :  libnform
121 |   Function      :  static void *Make_Enum_Type( va_list * ap )
122 |
123 |   Description   :  Allocate structure for enumeration type argument.
124 |
125 |   Return Values :  Pointer to argument structure or NULL on error
126 +--------------------------------------------------------------------------*/
127 static void *
128 Make_Enum_Type(va_list *ap)
129 {
130   enumParams params;
131 
132   params.kwds = va_arg(*ap, char **);
133   params.ccase = va_arg(*ap, int);
134   params.cunique = va_arg(*ap, int);
135 
136   return Generic_Enum_Type((void *)&params);
137 }
138 
139 /*---------------------------------------------------------------------------
140 |   Facility      :  libnform
141 |   Function      :  static void *Copy_Enum_Type( const void * argp )
142 |
143 |   Description   :  Copy structure for enumeration type argument.
144 |
145 |   Return Values :  Pointer to argument structure or NULL on error.
146 +--------------------------------------------------------------------------*/
147 static void *
148 Copy_Enum_Type(const void *argp)
149 {
150   enumARG *result = (enumARG *)0;
151 
152   if (argp)
153     {
154       const enumARG *ap = (const enumARG *)argp;
155 
156       result = typeMalloc(enumARG, 1);
157 
158       if (result)
159 	{
160 	  T((T_CREATE("enumARG %p"), (void *)result));
161 	  *result = *ap;
162 
163 	  if (ap->count > 0)
164 	    {
165 	      char **kptarget;
166 	      char **kp = ap->kwds;
167 	      result->kwds = typeMalloc(char *, 1 + ap->count);
168 
169 	      if ((kptarget = result->kwds) != 0)
170 		{
171 		  while (kp && (*kp))
172 		    {
173 		      (*kptarget++) = strdup(*kp++);
174 		    }
175 		  *kptarget = (char *)0;
176 		}
177 	    }
178 	}
179     }
180   return (void *)result;
181 }
182 
183 /*---------------------------------------------------------------------------
184 |   Facility      :  libnform
185 |   Function      :  static void Free_Enum_Type( void * argp )
186 |
187 |   Description   :  Free structure for enumeration type argument.
188 |
189 |   Return Values :  -
190 +--------------------------------------------------------------------------*/
191 static void
192 Free_Enum_Type(void *argp)
193 {
194   if (argp)
195     {
196       const enumARG *ap = (const enumARG *)argp;
197 
198       if (ap->kwds && ap->count > 0)
199 	{
200 	  char **kp = ap->kwds;
201 	  int cnt = 0;
202 
203 	  while (kp && (*kp))
204 	    {
205 	      free(*kp++);
206 	      cnt++;
207 	    }
208 	  assert(cnt == ap->count);
209 	  free(ap->kwds);
210 	}
211       free(argp);
212     }
213 }
214 
215 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
216 #define NOMATCH 0
217 #define PARTIAL 1
218 #define EXACT   2
219 
220 /*---------------------------------------------------------------------------
221 |   Facility      :  libnform
222 |   Function      :  static int Compare(const unsigned char * s,
223 |                                       const unsigned char * buf,
224 |                                       bool  ccase )
225 |
226 |   Description   :  Check whether or not the text in 'buf' matches the
227 |                    text in 's', at least partial.
228 |
229 |   Return Values :  NOMATCH   - buffer doesn't match
230 |                    PARTIAL   - buffer matches partially
231 |                    EXACT     - buffer matches exactly
232 +--------------------------------------------------------------------------*/
233 static int
234 Compare(const unsigned char *s, const unsigned char *buf,
235 	bool ccase)
236 {
237   SKIP_SPACE(buf);		/* Skip leading spaces in both texts */
238   SKIP_SPACE(s);
239 
240   if (*buf == '\0')
241     {
242       return (((*s) != '\0') ? NOMATCH : EXACT);
243     }
244   else
245     {
246       if (ccase)
247 	{
248 	  while (*s++ == *buf)
249 	    {
250 	      if (*buf++ == '\0')
251 		return EXACT;
252 	    }
253 	}
254       else
255 	{
256 	  while (toupper(*s++) == toupper(*buf))
257 	    {
258 	      if (*buf++ == '\0')
259 		return EXACT;
260 	    }
261 	}
262     }
263   /* At this location buf points to the first character where it no longer
264      matches with s. So if only blanks are following, we have a partial
265      match otherwise there is no match */
266   SKIP_SPACE(buf);
267   if (*buf)
268     return NOMATCH;
269 
270   /* If it happens that the reference buffer is at its end, the partial
271      match is actually an exact match. */
272   return ((s[-1] != '\0') ? PARTIAL : EXACT);
273 }
274 
275 /*---------------------------------------------------------------------------
276 |   Facility      :  libnform
277 |   Function      :  static bool Check_Enum_Field(
278 |                                      FIELD * field,
279 |                                      const void  * argp)
280 |
281 |   Description   :  Validate buffer content to be a valid enumeration value
282 |
283 |   Return Values :  TRUE  - field is valid
284 |                    FALSE - field is invalid
285 +--------------------------------------------------------------------------*/
286 static bool
287 Check_Enum_Field(FIELD *field, const void *argp)
288 {
289   char **kwds = ((const enumARG *)argp)->kwds;
290   bool ccase = ((const enumARG *)argp)->checkcase;
291   bool unique = ((const enumARG *)argp)->checkunique;
292   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
293   char *s, *t, *p;
294   int res;
295 
296   while (kwds && (s = (*kwds++)))
297     {
298       if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
299 	{
300 	  p = t = s;		/* t is at least a partial match */
301 	  if ((unique && res != EXACT))
302 	    {
303 	      while (kwds && (p = *kwds++))
304 		{
305 		  if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
306 		    {
307 		      if (res == EXACT)
308 			{
309 			  t = p;
310 			  break;
311 			}
312 		      else
313 			t = (char *)0;
314 		    }
315 		}
316 	    }
317 	  if (t)
318 	    {
319 	      set_field_buffer(field, 0, t);
320 	      return TRUE;
321 	    }
322 	  if (!p)
323 	    break;
324 	}
325     }
326   return FALSE;
327 }
328 
329 static const char *dummy[] =
330 {(char *)0};
331 
332 /*---------------------------------------------------------------------------
333 |   Facility      :  libnform
334 |   Function      :  static bool Next_Enum(FIELD * field,
335 |                                          const void * argp)
336 |
337 |   Description   :  Check for the next enumeration value
338 |
339 |   Return Values :  TRUE  - next value found and loaded
340 |                    FALSE - no next value loaded
341 +--------------------------------------------------------------------------*/
342 static bool
343 Next_Enum(FIELD *field, const void *argp)
344 {
345   const enumARG *args = (const enumARG *)argp;
346   char **kwds = args->kwds;
347   bool ccase = args->checkcase;
348   int cnt = args->count;
349   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
350 
351   if (kwds)
352     {
353       while (cnt--)
354 	{
355 	  if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
356 	    break;
357 	}
358       if (cnt <= 0)
359 	kwds = args->kwds;
360       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
361 	{
362 	  set_field_buffer(field, 0, *kwds);
363 	  return TRUE;
364 	}
365     }
366   return FALSE;
367 }
368 
369 /*---------------------------------------------------------------------------
370 |   Facility      :  libnform
371 |   Function      :  static bool Previous_Enum(
372 |                                          FIELD * field,
373 |                                          const void * argp)
374 |
375 |   Description   :  Check for the previous enumeration value
376 |
377 |   Return Values :  TRUE  - previous value found and loaded
378 |                    FALSE - no previous value loaded
379 +--------------------------------------------------------------------------*/
380 static bool
381 Previous_Enum(FIELD *field, const void *argp)
382 {
383   const enumARG *args = (const enumARG *)argp;
384   int cnt = args->count;
385   char **kwds = &args->kwds[cnt - 1];
386   bool ccase = args->checkcase;
387   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
388 
389   if (kwds)
390     {
391       while (cnt--)
392 	{
393 	  if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
394 	    break;
395 	}
396 
397       if (cnt <= 0)
398 	kwds = &args->kwds[args->count - 1];
399 
400       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
401 	{
402 	  set_field_buffer(field, 0, *kwds);
403 	  return TRUE;
404 	}
405     }
406   return FALSE;
407 }
408 
409 static FIELDTYPE typeENUM =
410 {
411   _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
412   1,				/* this is mutable, so we can't be const */
413   (FIELDTYPE *)0,
414   (FIELDTYPE *)0,
415   Make_Enum_Type,
416   Copy_Enum_Type,
417   Free_Enum_Type,
418   INIT_FT_FUNC(Check_Enum_Field),
419   INIT_FT_FUNC(NULL),
420   INIT_FT_FUNC(Next_Enum),
421   INIT_FT_FUNC(Previous_Enum),
422 #if NCURSES_INTEROP_FUNCS
423   Generic_Enum_Type
424 #endif
425 };
426 
427 NCURSES_EXPORT_VAR(FIELDTYPE *)
428 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 restictions on interop with C level
433    constructs (e.g. variable access or va_list + ellipsis constructs)
434 */
435 NCURSES_EXPORT(FIELDTYPE *)
436 _nc_TYPE_ENUM(void)
437 {
438   return TYPE_ENUM;
439 }
440 #endif
441 
442 /* fty_enum.c ends here */
443