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