xref: /freebsd/contrib/ncurses/form/fty_enum.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /****************************************************************************
2  * Copyright (c) 1998-2004,2006 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /***************************************************************************
30 *                                                                          *
31 *  Author : Juergen Pfeifer                                                *
32 *                                                                          *
33 ***************************************************************************/
34 
35 #include "form.priv.h"
36 
37 MODULE_ID("$Id: fty_enum.c,v 1.20 2006/04/22 21:33:05 tom Exp $")
38 
39 typedef struct
40   {
41     char **kwds;
42     int count;
43     bool checkcase;
44     bool checkunique;
45   }
46 enumARG;
47 
48 /*---------------------------------------------------------------------------
49 |   Facility      :  libnform
50 |   Function      :  static void *Make_Enum_Type( va_list * ap )
51 |
52 |   Description   :  Allocate structure for enumeration type argument.
53 |
54 |   Return Values :  Pointer to argument structure or NULL on error
55 +--------------------------------------------------------------------------*/
56 static void *
57 Make_Enum_Type(va_list *ap)
58 {
59   enumARG *argp = (enumARG *)malloc(sizeof(enumARG));
60 
61   if (argp)
62     {
63       int cnt = 0;
64       char **kp = (char **)0;
65       int ccase, cunique;
66 
67       argp->kwds = va_arg(*ap, char **);
68       ccase = va_arg(*ap, int);
69       cunique = va_arg(*ap, int);
70 
71       argp->checkcase = ccase ? TRUE : FALSE;
72       argp->checkunique = cunique ? TRUE : FALSE;
73 
74       kp = argp->kwds;
75       while (kp && (*kp++))
76 	cnt++;
77       argp->count = cnt;
78     }
79   return (void *)argp;
80 }
81 
82 /*---------------------------------------------------------------------------
83 |   Facility      :  libnform
84 |   Function      :  static void *Copy_Enum_Type( const void * argp )
85 |
86 |   Description   :  Copy structure for enumeration type argument.
87 |
88 |   Return Values :  Pointer to argument structure or NULL on error.
89 +--------------------------------------------------------------------------*/
90 static void *
91 Copy_Enum_Type(const void *argp)
92 {
93   enumARG *result = (enumARG *)0;
94 
95   if (argp)
96     {
97       const enumARG *ap = (const enumARG *)argp;
98 
99       result = (enumARG *)malloc(sizeof(enumARG));
100 
101       if (result)
102 	*result = *ap;
103     }
104   return (void *)result;
105 }
106 
107 /*---------------------------------------------------------------------------
108 |   Facility      :  libnform
109 |   Function      :  static void Free_Enum_Type( void * argp )
110 |
111 |   Description   :  Free structure for enumeration type argument.
112 |
113 |   Return Values :  -
114 +--------------------------------------------------------------------------*/
115 static void
116 Free_Enum_Type(void *argp)
117 {
118   if (argp)
119     free(argp);
120 }
121 
122 #define SKIP_SPACE(x) while(((*(x))!='\0') && (is_blank(*(x)))) (x)++
123 #define NOMATCH 0
124 #define PARTIAL 1
125 #define EXACT   2
126 
127 /*---------------------------------------------------------------------------
128 |   Facility      :  libnform
129 |   Function      :  static int Compare(const unsigned char * s,
130 |                                       const unsigned char * buf,
131 |                                       bool  ccase )
132 |
133 |   Description   :  Check whether or not the text in 'buf' matches the
134 |                    text in 's', at least partial.
135 |
136 |   Return Values :  NOMATCH   - buffer doesn't match
137 |                    PARTIAL   - buffer matches partially
138 |                    EXACT     - buffer matches exactly
139 +--------------------------------------------------------------------------*/
140 static int
141 Compare(const unsigned char *s, const unsigned char *buf,
142 	bool ccase)
143 {
144   SKIP_SPACE(buf);		/* Skip leading spaces in both texts */
145   SKIP_SPACE(s);
146 
147   if (*buf == '\0')
148     {
149       return (((*s) != '\0') ? NOMATCH : EXACT);
150     }
151   else
152     {
153       if (ccase)
154 	{
155 	  while (*s++ == *buf)
156 	    {
157 	      if (*buf++ == '\0')
158 		return EXACT;
159 	    }
160 	}
161       else
162 	{
163 	  while (toupper(*s++) == toupper(*buf))
164 	    {
165 	      if (*buf++ == '\0')
166 		return EXACT;
167 	    }
168 	}
169     }
170   /* At this location buf points to the first character where it no longer
171      matches with s. So if only blanks are following, we have a partial
172      match otherwise there is no match */
173   SKIP_SPACE(buf);
174   if (*buf)
175     return NOMATCH;
176 
177   /* If it happens that the reference buffer is at its end, the partial
178      match is actually an exact match. */
179   return ((s[-1] != '\0') ? PARTIAL : EXACT);
180 }
181 
182 /*---------------------------------------------------------------------------
183 |   Facility      :  libnform
184 |   Function      :  static bool Check_Enum_Field(
185 |                                      FIELD * field,
186 |                                      const void  * argp)
187 |
188 |   Description   :  Validate buffer content to be a valid enumeration value
189 |
190 |   Return Values :  TRUE  - field is valid
191 |                    FALSE - field is invalid
192 +--------------------------------------------------------------------------*/
193 static bool
194 Check_Enum_Field(FIELD *field, const void *argp)
195 {
196   char **kwds = ((const enumARG *)argp)->kwds;
197   bool ccase = ((const enumARG *)argp)->checkcase;
198   bool unique = ((const enumARG *)argp)->checkunique;
199   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
200   char *s, *t, *p;
201   int res;
202 
203   while (kwds && (s = (*kwds++)))
204     {
205       if ((res = Compare((unsigned char *)s, bp, ccase)) != NOMATCH)
206 	{
207 	  p = t = s;		/* t is at least a partial match */
208 	  if ((unique && res != EXACT))
209 	    {
210 	      while (kwds && (p = *kwds++))
211 		{
212 		  if ((res = Compare((unsigned char *)p, bp, ccase)) != NOMATCH)
213 		    {
214 		      if (res == EXACT)
215 			{
216 			  t = p;
217 			  break;
218 			}
219 		      else
220 			t = (char *)0;
221 		    }
222 		}
223 	    }
224 	  if (t)
225 	    {
226 	      set_field_buffer(field, 0, t);
227 	      return TRUE;
228 	    }
229 	  if (!p)
230 	    break;
231 	}
232     }
233   return FALSE;
234 }
235 
236 static const char *dummy[] =
237 {(char *)0};
238 
239 /*---------------------------------------------------------------------------
240 |   Facility      :  libnform
241 |   Function      :  static bool Next_Enum(FIELD * field,
242 |                                          const void * argp)
243 |
244 |   Description   :  Check for the next enumeration value
245 |
246 |   Return Values :  TRUE  - next value found and loaded
247 |                    FALSE - no next value loaded
248 +--------------------------------------------------------------------------*/
249 static bool
250 Next_Enum(FIELD *field, const void *argp)
251 {
252   const enumARG *args = (const enumARG *)argp;
253   char **kwds = args->kwds;
254   bool ccase = args->checkcase;
255   int cnt = args->count;
256   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
257 
258   if (kwds)
259     {
260       while (cnt--)
261 	{
262 	  if (Compare((unsigned char *)(*kwds++), bp, ccase) == EXACT)
263 	    break;
264 	}
265       if (cnt <= 0)
266 	kwds = args->kwds;
267       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
268 	{
269 	  set_field_buffer(field, 0, *kwds);
270 	  return TRUE;
271 	}
272     }
273   return FALSE;
274 }
275 
276 /*---------------------------------------------------------------------------
277 |   Facility      :  libnform
278 |   Function      :  static bool Previous_Enum(
279 |                                          FIELD * field,
280 |                                          const void * argp)
281 |
282 |   Description   :  Check for the previous enumeration value
283 |
284 |   Return Values :  TRUE  - previous value found and loaded
285 |                    FALSE - no previous value loaded
286 +--------------------------------------------------------------------------*/
287 static bool
288 Previous_Enum(FIELD *field, const void *argp)
289 {
290   const enumARG *args = (const enumARG *)argp;
291   int cnt = args->count;
292   char **kwds = &args->kwds[cnt - 1];
293   bool ccase = args->checkcase;
294   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
295 
296   if (kwds)
297     {
298       while (cnt--)
299 	{
300 	  if (Compare((unsigned char *)(*kwds--), bp, ccase) == EXACT)
301 	    break;
302 	}
303 
304       if (cnt <= 0)
305 	kwds = &args->kwds[args->count - 1];
306 
307       if ((cnt >= 0) || (Compare((const unsigned char *)dummy, bp, ccase) == EXACT))
308 	{
309 	  set_field_buffer(field, 0, *kwds);
310 	  return TRUE;
311 	}
312     }
313   return FALSE;
314 }
315 
316 static FIELDTYPE typeENUM =
317 {
318   _HAS_ARGS | _HAS_CHOICE | _RESIDENT,
319   1,				/* this is mutable, so we can't be const */
320   (FIELDTYPE *)0,
321   (FIELDTYPE *)0,
322   Make_Enum_Type,
323   Copy_Enum_Type,
324   Free_Enum_Type,
325   Check_Enum_Field,
326   NULL,
327   Next_Enum,
328   Previous_Enum
329 };
330 
331 NCURSES_EXPORT_VAR(FIELDTYPE *)
332 TYPE_ENUM = &typeENUM;
333 
334 /* fty_enum.c ends here */
335