xref: /freebsd/contrib/ncurses/form/fty_enum.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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.15 2000/12/09 23:46:12 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 wether 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 */
296