c249b5b039632a203c42dad423e9b0622cb301c3
[ncurses.git] / form / fty_enum.c
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2009 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.24 2009/11/07 20:17:58 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"), 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               kptarget = argp->kwds;
106               while (kptarget && kp && (*kp))
107                 {
108                   (*kptarget++) = strdup(*kp++);
109                 }
110               *kptarget = (char *)0;
111             }
112         }
113     }
114   return (void *)argp;
115 }
116
117 /*---------------------------------------------------------------------------
118 |   Facility      :  libnform  
119 |   Function      :  static void *Make_Enum_Type( va_list * ap )
120 |   
121 |   Description   :  Allocate structure for enumeration type argument.
122 |
123 |   Return Values :  Pointer to argument structure or NULL on error
124 +--------------------------------------------------------------------------*/
125 static void *
126 Make_Enum_Type(va_list *ap)
127 {
128   enumParams params;
129
130   params.kwds = va_arg(*ap, char **);
131   params.ccase = va_arg(*ap, int);
132   params.cunique = va_arg(*ap, int);
133
134   return Generic_Enum_Type((void *)&params);
135 }
136
137 /*---------------------------------------------------------------------------
138 |   Facility      :  libnform  
139 |   Function      :  static void *Copy_Enum_Type( const void * argp )
140 |   
141 |   Description   :  Copy structure for enumeration type argument.  
142 |
143 |   Return Values :  Pointer to argument structure or NULL on error.
144 +--------------------------------------------------------------------------*/
145 static void *
146 Copy_Enum_Type(const void *argp)
147 {
148   enumARG *result = (enumARG *)0;
149
150   if (argp)
151     {
152       const enumARG *ap = (const enumARG *)argp;
153
154       result = typeMalloc(enumARG, 1);
155
156       if (result)
157         {
158           T((T_CREATE("enumARG %p"), result));
159           *result = *ap;
160
161           if (ap->count > 0)
162             {
163               char **kptarget;
164               char **kp = ap->kwds;
165               result->kwds = typeMalloc(char *, 1 + ap->count);
166
167               kptarget = result->kwds;
168               while (kptarget && kp && (*kp))
169                 {
170                   (*kptarget++) = strdup(*kp++);
171                 }
172               *kptarget = (char *)0;
173             }
174         }
175     }
176   return (void *)result;
177 }
178
179 /*---------------------------------------------------------------------------
180 |   Facility      :  libnform  
181 |   Function      :  static void Free_Enum_Type( void * argp )
182 |   
183 |   Description   :  Free structure for enumeration type argument.
184 |
185 |   Return Values :  -
186 +--------------------------------------------------------------------------*/
187 static void
188 Free_Enum_Type(void *argp)
189 {
190   if (argp)
191     {
192       const enumARG *ap = (const enumARG *)argp;
193
194       if (ap->kwds && ap->count > 0)
195         {
196           char **kp = ap->kwds;
197           int cnt = 0;
198
199           while (kp && (*kp))
200             {
201               free(*kp++);
202               cnt++;
203             }
204           assert(cnt == ap->count);
205           free(ap->kwds);
206         }
207       free(argp);
208     }
209 }
210
211 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
212 #define NOMATCH 0
213 #define PARTIAL 1
214 #define EXACT   2
215
216 /*---------------------------------------------------------------------------
217 |   Facility      :  libnform  
218 |   Function      :  static int Compare(const unsigned char * s,  
219 |                                       const unsigned char * buf,
220 |                                       bool  ccase )
221 |   
222 |   Description   :  Check whether or not the text in 'buf' matches the
223 |                    text in 's', at least partial.
224 |
225 |   Return Values :  NOMATCH   - buffer doesn't match
226 |                    PARTIAL   - buffer matches partially
227 |                    EXACT     - buffer matches exactly
228 +--------------------------------------------------------------------------*/
229 static int
230 Compare(const unsigned char *s, const unsigned char *buf,
231         bool ccase)
232 {
233   SKIP_SPACE(buf);              /* Skip leading spaces in both texts */
234   SKIP_SPACE(s);
235
236   if (*buf == '\0')
237     {
238       return (((*s) != '\0') ? NOMATCH : EXACT);
239     }
240   else
241     {
242       if (ccase)
243         {
244           while (*s++ == *buf)
245             {
246               if (*buf++ == '\0')
247                 return EXACT;
248             }
249         }
250       else
251         {
252           while (toupper(*s++) == toupper(*buf))
253             {
254               if (*buf++ == '\0')
255                 return EXACT;
256             }
257         }
258     }
259   /* At this location buf points to the first character where it no longer
260      matches with s. So if only blanks are following, we have a partial
261      match otherwise there is no match */
262   SKIP_SPACE(buf);
263   if (*buf)
264     return NOMATCH;
265
266   /* If it happens that the reference buffer is at its end, the partial
267      match is actually an exact match. */
268   return ((s[-1] != '\0') ? PARTIAL : EXACT);
269 }
270
271 /*---------------------------------------------------------------------------
272 |   Facility      :  libnform  
273 |   Function      :  static bool Check_Enum_Field(
274 |                                      FIELD * field,
275 |                                      const void  * argp)
276 |   
277 |   Description   :  Validate buffer content to be a valid enumeration value
278 |
279 |   Return Values :  TRUE  - field is valid
280 |                    FALSE - field is invalid
281 +--------------------------------------------------------------------------*/
282 static bool
283 Check_Enum_Field(FIELD *field, const void *argp)
284 {
285   char **kwds = ((const enumARG *)argp)->kwds;
286   bool ccase = ((const enumARG *)argp)->checkcase;
287   bool unique = ((const enumARG *)argp)->checkunique;
288   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
289   char *s, *t, *p;
290   int res;
291
292   while (kwds && (s = (*kwds++)))
293     {
294       if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
295         {
296           p = t = s;            /* t is at least a partial match */
297           if ((unique && res != EXACT))
298             {
299               while (kwds && (p = *kwds++))
300                 {
301                   if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
302                     {
303                       if (res == EXACT)
304                         {
305                           t = p;
306                           break;
307                         }
308                       else
309                         t = (char *)0;
310                     }
311                 }
312             }
313           if (t)
314             {
315               set_field_buffer(field, 0, t);
316               return TRUE;
317             }
318           if (!p)
319             break;
320         }
321     }
322   return FALSE;
323 }
324
325 static const char *dummy[] =
326 {(char *)0};
327
328 /*---------------------------------------------------------------------------
329 |   Facility      :  libnform  
330 |   Function      :  static bool Next_Enum(FIELD * field,
331 |                                          const void * argp)
332 |   
333 |   Description   :  Check for the next enumeration value
334 |
335 |   Return Values :  TRUE  - next value found and loaded
336 |                    FALSE - no next value loaded
337 +--------------------------------------------------------------------------*/
338 static bool
339 Next_Enum(FIELD *field, const void *argp)
340 {
341   const enumARG *args = (const enumARG *)argp;
342   char **kwds = args->kwds;
343   bool ccase = args->checkcase;
344   int cnt = args->count;
345   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
346
347   if (kwds)
348     {
349       while (cnt--)
350         {
351           if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
352             break;
353         }
354       if (cnt <= 0)
355         kwds = args->kwds;
356       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
357         {
358           set_field_buffer(field, 0, *kwds);
359           return TRUE;
360         }
361     }
362   return FALSE;
363 }
364
365 /*---------------------------------------------------------------------------
366 |   Facility      :  libnform  
367 |   Function      :  static bool Previous_Enum(
368 |                                          FIELD * field,
369 |                                          const void * argp)
370 |   
371 |   Description   :  Check for the previous enumeration value
372 |
373 |   Return Values :  TRUE  - previous value found and loaded
374 |                    FALSE - no previous value loaded
375 +--------------------------------------------------------------------------*/
376 static bool
377 Previous_Enum(FIELD *field, const void *argp)
378 {
379   const enumARG *args = (const enumARG *)argp;
380   int cnt = args->count;
381   char **kwds = &args->kwds[cnt - 1];
382   bool ccase = args->checkcase;
383   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
384
385   if (kwds)
386     {
387       while (cnt--)
388         {
389           if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
390             break;
391         }
392
393       if (cnt <= 0)
394         kwds = &args->kwds[args->count - 1];
395
396       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
397         {
398           set_field_buffer(field, 0, *kwds);
399           return TRUE;
400         }
401     }
402   return FALSE;
403 }
404
405 static FIELDTYPE typeENUM =
406 {
407   _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
408   1,                            /* this is mutable, so we can't be const */
409   (FIELDTYPE *)0,
410   (FIELDTYPE *)0,
411   Make_Enum_Type,
412   Copy_Enum_Type,
413   Free_Enum_Type,
414   INIT_FT_FUNC(Check_Enum_Field),
415   INIT_FT_FUNC(NULL),
416   INIT_FT_FUNC(Next_Enum),
417   INIT_FT_FUNC(Previous_Enum),
418 #if NCURSES_INTEROP_FUNCS
419   Generic_Enum_Type
420 #endif
421 };
422
423 NCURSES_EXPORT_VAR(FIELDTYPE *)
424 TYPE_ENUM = &typeENUM;
425
426 #if NCURSES_INTEROP_FUNCS
427 /* The next routines are to simplify the use of ncurses from
428    programming languages with restictions on interop with C level
429    constructs (e.g. variable access or va_list + ellipsis constructs)
430 */
431 NCURSES_EXPORT(FIELDTYPE *)
432 _nc_TYPE_ENUM(void)
433 {
434   return TYPE_ENUM;
435 }
436 #endif
437
438 /* fty_enum.c ends here */