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