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