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
37MODULE_ID("$Id: fty_enum.c,v 1.26 2010/05/01 21:11:07 tom Exp $")
38
39typedef struct
40  {
41    char **kwds;
42    int count;
43    bool checkcase;
44    bool checkunique;
45  }
46enumARG;
47
48typedef struct
49  {
50    char **kwds;
51    int ccase;
52    int cunique;
53  }
54enumParams;
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+--------------------------------------------------------------------------*/
64static void *
65Generic_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+--------------------------------------------------------------------------*/
127static void *
128Make_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+--------------------------------------------------------------------------*/
147static void *
148Copy_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+--------------------------------------------------------------------------*/
191static void
192Free_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+--------------------------------------------------------------------------*/
233static int
234Compare(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+--------------------------------------------------------------------------*/
286static bool
287Check_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
329static 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+--------------------------------------------------------------------------*/
342static bool
343Next_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+--------------------------------------------------------------------------*/
380static bool
381Previous_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
409static 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
427NCURSES_EXPORT_VAR(FIELDTYPE *)
428TYPE_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*/
435NCURSES_EXPORT(FIELDTYPE *)
436_nc_TYPE_ENUM(void)
437{
438  return TYPE_ENUM;
439}
440#endif
441
442/* fty_enum.c ends here */
443