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