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