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