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