1/*	$OpenBSD: fty_num.c,v 1.10 2023/10/17 09:52:10 nicm Exp $	*/
2/****************************************************************************
3 * Copyright 2019-2020,2021 Thomas E. Dickey                                *
4 * Copyright 1998-2010,2012 Free Software Foundation, Inc.                  *
5 *                                                                          *
6 * Permission is hereby granted, free of charge, to any person obtaining a  *
7 * copy of this software and associated documentation files (the            *
8 * "Software"), to deal in the Software without restriction, including      *
9 * without limitation the rights to use, copy, modify, merge, publish,      *
10 * distribute, distribute with modifications, sublicense, and/or sell       *
11 * copies of the Software, and to permit persons to whom the Software is    *
12 * furnished to do so, subject to the following conditions:                 *
13 *                                                                          *
14 * The above copyright notice and this permission notice shall be included  *
15 * in all copies or substantial portions of the Software.                   *
16 *                                                                          *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24 *                                                                          *
25 * Except as contained in this notice, the name(s) of the above copyright   *
26 * holders shall not be used in advertising or otherwise to promote the     *
27 * sale, use or other dealings in this Software without prior written       *
28 * authorization.                                                           *
29 ****************************************************************************/
30
31/***************************************************************************
32*                                                                          *
33*  Author : Juergen Pfeifer                                                *
34*                                                                          *
35***************************************************************************/
36
37#include "form.priv.h"
38
39MODULE_ID("$Id: fty_num.c,v 1.10 2023/10/17 09:52:10 nicm Exp $")
40
41#if HAVE_LOCALE_H
42#include <locale.h>
43#endif
44
45#if HAVE_LOCALE_H && HAVE_LOCALECONV
46#define isDecimalPoint(c) ((c) == ((L && L->decimal_point) ? *(L->decimal_point) : '.'))
47#else
48#define isDecimalPoint(c) ((c) == '.')
49#endif
50
51#if USE_WIDEC_SUPPORT
52#define isDigit(c) (iswdigit((wint_t)(c)) || isdigit(UChar(c)))
53#else
54#define isDigit(c) isdigit(UChar(c))
55#endif
56
57#define thisARG numericARG
58
59typedef struct
60  {
61    int precision;
62    double low;
63    double high;
64    struct lconv *L;
65  }
66thisARG;
67
68typedef struct
69  {
70    int precision;
71    double low;
72    double high;
73  }
74thisPARM;
75
76/*---------------------------------------------------------------------------
77|   Facility      :  libnform
78|   Function      :  static void *Generic_This_Type(void * arg)
79|
80|   Description   :  Allocate structure for numeric type argument.
81|
82|   Return Values :  Pointer to argument structure or NULL on error
83+--------------------------------------------------------------------------*/
84static void *
85Generic_This_Type(void *arg)
86{
87  thisARG *argn = (thisARG *)0;
88  thisPARM *args = (thisPARM *)arg;
89
90  if (args)
91    {
92      argn = typeMalloc(thisARG, 1);
93
94      if (argn)
95	{
96	  T((T_CREATE("thisARG %p"), (void *)argn));
97	  argn->precision = args->precision;
98	  argn->low = args->low;
99	  argn->high = args->high;
100
101#if HAVE_LOCALE_H && HAVE_LOCALECONV
102	  argn->L = localeconv();
103#else
104	  argn->L = NULL;
105#endif
106	}
107    }
108  return (void *)argn;
109}
110
111/*---------------------------------------------------------------------------
112|   Facility      :  libnform
113|   Function      :  static void *Make_This_Type(va_list * ap)
114|
115|   Description   :  Allocate structure for numeric type argument.
116|
117|   Return Values :  Pointer to argument structure or NULL on error
118+--------------------------------------------------------------------------*/
119static void *
120Make_This_Type(va_list *ap)
121{
122  thisPARM arg;
123
124  arg.precision = va_arg(*ap, int);
125  arg.low = va_arg(*ap, double);
126  arg.high = va_arg(*ap, double);
127
128  return Generic_This_Type((void *)&arg);
129}
130
131/*---------------------------------------------------------------------------
132|   Facility      :  libnform
133|   Function      :  static void *Copy_This_Type(const void * argp)
134|
135|   Description   :  Copy structure for numeric type argument.
136|
137|   Return Values :  Pointer to argument structure or NULL on error.
138+--------------------------------------------------------------------------*/
139static void *
140Copy_This_Type(const void *argp)
141{
142  const thisARG *ap = (const thisARG *)argp;
143  thisARG *result = (thisARG *)0;
144
145  if (argp)
146    {
147      result = typeMalloc(thisARG, 1);
148
149      if (result)
150	{
151	  T((T_CREATE("thisARG %p"), (void *)result));
152	  *result = *ap;
153	}
154    }
155  return (void *)result;
156}
157
158/*---------------------------------------------------------------------------
159|   Facility      :  libnform
160|   Function      :  static void Free_This_Type(void * argp)
161|
162|   Description   :  Free structure for numeric type argument.
163|
164|   Return Values :  -
165+--------------------------------------------------------------------------*/
166static void
167Free_This_Type(void *argp)
168{
169  if (argp)
170    free(argp);
171}
172
173/*---------------------------------------------------------------------------
174|   Facility      :  libnform
175|   Function      :  static bool Check_This_Field(FIELD * field,
176|                                                 const void * argp)
177|
178|   Description   :  Validate buffer content to be a valid numeric value
179|
180|   Return Values :  TRUE  - field is valid
181|                    FALSE - field is invalid
182+--------------------------------------------------------------------------*/
183static bool
184Check_This_Field(FIELD *field, const void *argp)
185{
186  const thisARG *argn = (const thisARG *)argp;
187  double low = argn->low;
188  double high = argn->high;
189  int prec = argn->precision;
190  unsigned char *bp = (unsigned char *)field_buffer(field, 0);
191  char *s = (char *)bp;
192  struct lconv *L = argn->L;
193  bool result = FALSE;
194
195  while (*bp == ' ')
196    bp++;
197  if (*bp)
198    {
199      if (*bp == '-' || *bp == '+')
200	bp++;
201#if USE_WIDEC_SUPPORT
202      if (*bp)
203	{
204	  int len;
205	  wchar_t *list = _nc_Widen_String((char *)bp, &len);
206
207	  if (list != 0)
208	    {
209	      bool blank = FALSE;
210	      int state = 0;
211	      int n;
212
213	      result = TRUE;
214	      for (n = 0; n < len; ++n)
215		{
216		  if (blank)
217		    {
218		      if (list[n] != ' ')
219			{
220			  result = FALSE;
221			  break;
222			}
223		    }
224		  else if (list[n] == ' ')
225		    {
226		      blank = TRUE;
227		    }
228		  else if (isDecimalPoint(list[n]))
229		    {
230		      if (++state > 1)
231			{
232			  result = FALSE;
233			  break;
234			}
235		    }
236		  else if (!isDigit(list[n]))
237		    {
238		      result = FALSE;
239		      break;
240		    }
241		}
242	      free(list);
243	    }
244	}
245#else
246      while (*bp)
247	{
248	  if (!isdigit(UChar(*bp)))
249	    break;
250	  bp++;
251	}
252      if (isDecimalPoint(*bp))
253	{
254	  bp++;
255	  while (*bp)
256	    {
257	      if (!isdigit(UChar(*bp)))
258		break;
259	      bp++;
260	    }
261	}
262      while (*bp && *bp == ' ')
263	bp++;
264      result = (*bp == '\0');
265#endif
266      if (result)
267	{
268	  double val = atof(s);
269
270	  if (low < high)
271	    {
272	      if (val < low || val > high)
273		result = FALSE;
274	    }
275	  if (result)
276	    {
277	      char buf[64];
278
279	      _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
280			  "%.*f", (prec > 0 ? prec : 0), val);
281	      set_field_buffer(field, 0, buf);
282	    }
283	}
284    }
285  return (result);
286}
287
288/*---------------------------------------------------------------------------
289|   Facility      :  libnform
290|   Function      :  static bool Check_This_Character(
291|                                      int c,
292|                                      const void * argp)
293|
294|   Description   :  Check a character for the numeric type.
295|
296|   Return Values :  TRUE  - character is valid
297|                    FALSE - character is invalid
298+--------------------------------------------------------------------------*/
299static bool
300Check_This_Character(int c, const void *argp)
301{
302  const thisARG *argn = (const thisARG *)argp;
303  struct lconv *L = argn->L;
304
305  return ((isDigit(c) ||
306	   c == '+' ||
307	   c == '-' ||
308	   isDecimalPoint(c))
309	  ? TRUE
310	  : FALSE);
311}
312
313static FIELDTYPE typeTHIS =
314{
315  _HAS_ARGS | _RESIDENT,
316  1,				/* this is mutable, so we can't be const */
317  (FIELDTYPE *)0,
318  (FIELDTYPE *)0,
319  Make_This_Type,
320  Copy_This_Type,
321  Free_This_Type,
322  INIT_FT_FUNC(Check_This_Field),
323  INIT_FT_FUNC(Check_This_Character),
324  INIT_FT_FUNC(NULL),
325  INIT_FT_FUNC(NULL),
326#if NCURSES_INTEROP_FUNCS
327  Generic_This_Type
328#endif
329};
330
331FORM_EXPORT_VAR(FIELDTYPE *) TYPE_NUMERIC = &typeTHIS;
332
333#if NCURSES_INTEROP_FUNCS
334/* The next routines are to simplify the use of ncurses from
335   programming languages with restrictions on interop with C level
336   constructs (e.g. variable access or va_list + ellipsis constructs)
337*/
338FORM_EXPORT(FIELDTYPE *)
339_nc_TYPE_NUMERIC(void)
340{
341  return TYPE_NUMERIC;
342}
343#endif
344
345/* fty_num.c ends here */
346