1/****************************************************************************
2 * Copyright (c) 1998-2006,2007 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.22 2007/10/13 19:32:26 tom Exp $")
38
39typedef struct
40  {
41    char **kwds;
42    int count;
43    bool checkcase;
44    bool checkunique;
45  }
46enumARG;
47
48/*---------------------------------------------------------------------------
49|   Facility      :  libnform
50|   Function      :  static void *Make_Enum_Type( va_list * ap )
51|
52|   Description   :  Allocate structure for enumeration type argument.
53|
54|   Return Values :  Pointer to argument structure or NULL on error
55+--------------------------------------------------------------------------*/
56static void *
57Make_Enum_Type(va_list *ap)
58{
59  enumARG *argp = typeMalloc(enumARG, 1);
60
61  if (argp)
62    {
63      int cnt = 0;
64      char **kp = (char **)0;
65      int ccase, cunique;
66
67      T((T_CREATE("enumARG %p"), argp));
68      argp->kwds = va_arg(*ap, char **);
69      ccase = va_arg(*ap, int);
70      cunique = va_arg(*ap, int);
71
72      argp->checkcase = ccase ? TRUE : FALSE;
73      argp->checkunique = cunique ? TRUE : FALSE;
74
75      kp = argp->kwds;
76      while (kp && (*kp++))
77	cnt++;
78      argp->count = cnt;
79    }
80  return (void *)argp;
81}
82
83/*---------------------------------------------------------------------------
84|   Facility      :  libnform
85|   Function      :  static void *Copy_Enum_Type( const void * argp )
86|
87|   Description   :  Copy structure for enumeration type argument.
88|
89|   Return Values :  Pointer to argument structure or NULL on error.
90+--------------------------------------------------------------------------*/
91static void *
92Copy_Enum_Type(const void *argp)
93{
94  enumARG *result = (enumARG *)0;
95
96  if (argp)
97    {
98      const enumARG *ap = (const enumARG *)argp;
99
100      result = typeMalloc(enumARG, 1);
101
102      if (result)
103	{
104	  T((T_CREATE("enumARG %p"), result));
105	  *result = *ap;
106	}
107    }
108  return (void *)result;
109}
110
111/*---------------------------------------------------------------------------
112|   Facility      :  libnform
113|   Function      :  static void Free_Enum_Type( void * argp )
114|
115|   Description   :  Free structure for enumeration type argument.
116|
117|   Return Values :  -
118+--------------------------------------------------------------------------*/
119static void
120Free_Enum_Type(void *argp)
121{
122  if (argp)
123    free(argp);
124}
125
126#define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
127#define NOMATCH 0
128#define PARTIAL 1
129#define EXACT   2
130
131/*---------------------------------------------------------------------------
132|   Facility      :  libnform
133|   Function      :  static int Compare(const unsigned char * s,
134|                                       const unsigned char * buf,
135|                                       bool  ccase )
136|
137|   Description   :  Check whether or not the text in 'buf' matches the
138|                    text in 's', at least partial.
139|
140|   Return Values :  NOMATCH   - buffer doesn't match
141|                    PARTIAL   - buffer matches partially
142|                    EXACT     - buffer matches exactly
143+--------------------------------------------------------------------------*/
144static int
145Compare(const unsigned char *s, const unsigned char *buf,
146	bool ccase)
147{
148  SKIP_SPACE(buf);		/* Skip leading spaces in both texts */
149  SKIP_SPACE(s);
150
151  if (*buf == '\0')
152    {
153      return (((*s) != '\0') ? NOMATCH : EXACT);
154    }
155  else
156    {
157      if (ccase)
158	{
159	  while (*s++ == *buf)
160	    {
161	      if (*buf++ == '\0')
162		return EXACT;
163	    }
164	}
165      else
166	{
167	  while (toupper(*s++) == toupper(*buf))
168	    {
169	      if (*buf++ == '\0')
170		return EXACT;
171	    }
172	}
173    }
174  /* At this location buf points to the first character where it no longer
175     matches with s. So if only blanks are following, we have a partial
176     match otherwise there is no match */
177  SKIP_SPACE(buf);
178  if (*buf)
179    return NOMATCH;
180
181  /* If it happens that the reference buffer is at its end, the partial
182     match is actually an exact match. */
183  return ((s[-1] != '\0') ? PARTIAL : EXACT);
184}
185
186/*---------------------------------------------------------------------------
187|   Facility      :  libnform
188|   Function      :  static bool Check_Enum_Field(
189|                                      FIELD * field,
190|                                      const void  * argp)
191|
192|   Description   :  Validate buffer content to be a valid enumeration value
193|
194|   Return Values :  TRUE  - field is valid
195|                    FALSE - field is invalid
196+--------------------------------------------------------------------------*/
197static bool
198Check_Enum_Field(FIELD *field, const void *argp)
199{
200  char **kwds = ((const enumARG *)argp)->kwds;
201  bool ccase = ((const enumARG *)argp)->checkcase;
202  bool unique = ((const enumARG *)argp)->checkunique;
203  unsigned char *bp = (unsigned char *)field_buffer(field, 0);
204  char *s, *t, *p;
205  int res;
206
207  while (kwds && (s = (*kwds++)))
208    {
209      if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
210	{
211	  p = t = s;		/* t is at least a partial match */
212	  if ((unique && res != EXACT))
213	    {
214	      while (kwds && (p = *kwds++))
215		{
216		  if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
217		    {
218		      if (res == EXACT)
219			{
220			  t = p;
221			  break;
222			}
223		      else
224			t = (char *)0;
225		    }
226		}
227	    }
228	  if (t)
229	    {
230	      set_field_buffer(field, 0, t);
231	      return TRUE;
232	    }
233	  if (!p)
234	    break;
235	}
236    }
237  return FALSE;
238}
239
240static const char *dummy[] =
241{(char *)0};
242
243/*---------------------------------------------------------------------------
244|   Facility      :  libnform
245|   Function      :  static bool Next_Enum(FIELD * field,
246|                                          const void * argp)
247|
248|   Description   :  Check for the next enumeration value
249|
250|   Return Values :  TRUE  - next value found and loaded
251|                    FALSE - no next value loaded
252+--------------------------------------------------------------------------*/
253static bool
254Next_Enum(FIELD *field, const void *argp)
255{
256  const enumARG *args = (const enumARG *)argp;
257  char **kwds = args->kwds;
258  bool ccase = args->checkcase;
259  int cnt = args->count;
260  unsigned char *bp = (unsigned char *)field_buffer(field, 0);
261
262  if (kwds)
263    {
264      while (cnt--)
265	{
266	  if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
267	    break;
268	}
269      if (cnt <= 0)
270	kwds = args->kwds;
271      if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
272	{
273	  set_field_buffer(field, 0, *kwds);
274	  return TRUE;
275	}
276    }
277  return FALSE;
278}
279
280/*---------------------------------------------------------------------------
281|   Facility      :  libnform
282|   Function      :  static bool Previous_Enum(
283|                                          FIELD * field,
284|                                          const void * argp)
285|
286|   Description   :  Check for the previous enumeration value
287|
288|   Return Values :  TRUE  - previous value found and loaded
289|                    FALSE - no previous value loaded
290+--------------------------------------------------------------------------*/
291static bool
292Previous_Enum(FIELD *field, const void *argp)
293{
294  const enumARG *args = (const enumARG *)argp;
295  int cnt = args->count;
296  char **kwds = &args->kwds[cnt - 1];
297  bool ccase = args->checkcase;
298  unsigned char *bp = (unsigned char *)field_buffer(field, 0);
299
300  if (kwds)
301    {
302      while (cnt--)
303	{
304	  if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
305	    break;
306	}
307
308      if (cnt <= 0)
309	kwds = &args->kwds[args->count - 1];
310
311      if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
312	{
313	  set_field_buffer(field, 0, *kwds);
314	  return TRUE;
315	}
316    }
317  return FALSE;
318}
319
320static FIELDTYPE typeENUM =
321{
322  _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
323  1,				/* this is mutable, so we can't be const */
324  (FIELDTYPE *)0,
325  (FIELDTYPE *)0,
326  Make_Enum_Type,
327  Copy_Enum_Type,
328  Free_Enum_Type,
329  Check_Enum_Field,
330  NULL,
331  Next_Enum,
332  Previous_Enum
333};
334
335NCURSES_EXPORT_VAR(FIELDTYPE *)
336TYPE_ENUM = &typeENUM;
337
338/* fty_enum.c ends here */
339