xref: /freebsd/contrib/ncurses/form/fty_num.c (revision 21817992b3314c908ab50f0bb88d2ee750b9c4ac)
14a1a9510SRong-En Fan /****************************************************************************
2*21817992SBaptiste Daroussin  * Copyright 2019-2020,2021 Thomas E. Dickey                                *
3e1865124SBaptiste Daroussin  * Copyright 1998-2010,2012 Free Software Foundation, Inc.                  *
44a1a9510SRong-En Fan  *                                                                          *
54a1a9510SRong-En Fan  * Permission is hereby granted, free of charge, to any person obtaining a  *
64a1a9510SRong-En Fan  * copy of this software and associated documentation files (the            *
74a1a9510SRong-En Fan  * "Software"), to deal in the Software without restriction, including      *
84a1a9510SRong-En Fan  * without limitation the rights to use, copy, modify, merge, publish,      *
94a1a9510SRong-En Fan  * distribute, distribute with modifications, sublicense, and/or sell       *
104a1a9510SRong-En Fan  * copies of the Software, and to permit persons to whom the Software is    *
114a1a9510SRong-En Fan  * furnished to do so, subject to the following conditions:                 *
124a1a9510SRong-En Fan  *                                                                          *
134a1a9510SRong-En Fan  * The above copyright notice and this permission notice shall be included  *
144a1a9510SRong-En Fan  * in all copies or substantial portions of the Software.                   *
154a1a9510SRong-En Fan  *                                                                          *
164a1a9510SRong-En Fan  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
174a1a9510SRong-En Fan  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
184a1a9510SRong-En Fan  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
194a1a9510SRong-En Fan  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
204a1a9510SRong-En Fan  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
214a1a9510SRong-En Fan  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
224a1a9510SRong-En Fan  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
234a1a9510SRong-En Fan  *                                                                          *
244a1a9510SRong-En Fan  * Except as contained in this notice, the name(s) of the above copyright   *
254a1a9510SRong-En Fan  * holders shall not be used in advertising or otherwise to promote the     *
264a1a9510SRong-En Fan  * sale, use or other dealings in this Software without prior written       *
274a1a9510SRong-En Fan  * authorization.                                                           *
284a1a9510SRong-En Fan  ****************************************************************************/
290e3d5408SPeter Wemm 
300e3d5408SPeter Wemm /***************************************************************************
310e3d5408SPeter Wemm *                                                                          *
324a1a9510SRong-En Fan *  Author : Juergen Pfeifer                                                *
330e3d5408SPeter Wemm *                                                                          *
340e3d5408SPeter Wemm ***************************************************************************/
350e3d5408SPeter Wemm 
360e3d5408SPeter Wemm #include "form.priv.h"
370e3d5408SPeter Wemm 
38*21817992SBaptiste Daroussin MODULE_ID("$Id: fty_num.c,v 1.37 2021/03/27 23:49:58 tom Exp $")
390e3d5408SPeter Wemm 
400e3d5408SPeter Wemm #if HAVE_LOCALE_H
410e3d5408SPeter Wemm #include <locale.h>
420e3d5408SPeter Wemm #endif
430e3d5408SPeter Wemm 
44aae38d10SBaptiste Daroussin #if HAVE_LOCALE_H && HAVE_LOCALECONV
454a1a9510SRong-En Fan #define isDecimalPoint(c) ((c) == ((L && L->decimal_point) ? *(L->decimal_point) : '.'))
464a1a9510SRong-En Fan #else
474a1a9510SRong-En Fan #define isDecimalPoint(c) ((c) == '.')
484a1a9510SRong-En Fan #endif
494a1a9510SRong-En Fan 
504a1a9510SRong-En Fan #if USE_WIDEC_SUPPORT
514a1a9510SRong-En Fan #define isDigit(c) (iswdigit((wint_t)(c)) || isdigit(UChar(c)))
524a1a9510SRong-En Fan #else
534a1a9510SRong-En Fan #define isDigit(c) isdigit(UChar(c))
544a1a9510SRong-En Fan #endif
554a1a9510SRong-En Fan 
564a1a9510SRong-En Fan #define thisARG numericARG
574a1a9510SRong-En Fan 
584a1a9510SRong-En Fan typedef struct
594a1a9510SRong-En Fan   {
600e3d5408SPeter Wemm     int precision;
610e3d5408SPeter Wemm     double low;
620e3d5408SPeter Wemm     double high;
630e3d5408SPeter Wemm     struct lconv *L;
644a1a9510SRong-En Fan   }
654a1a9510SRong-En Fan thisARG;
660e3d5408SPeter Wemm 
6706bfebdeSXin LI typedef struct
6806bfebdeSXin LI   {
6906bfebdeSXin LI     int precision;
7006bfebdeSXin LI     double low;
7106bfebdeSXin LI     double high;
7206bfebdeSXin LI   }
7306bfebdeSXin LI thisPARM;
7406bfebdeSXin LI 
7506bfebdeSXin LI /*---------------------------------------------------------------------------
7606bfebdeSXin LI |   Facility      :  libnform
7706bfebdeSXin LI |   Function      :  static void *Generic_This_Type(void * arg)
7806bfebdeSXin LI |
7906bfebdeSXin LI |   Description   :  Allocate structure for numeric type argument.
8006bfebdeSXin LI |
8106bfebdeSXin LI |   Return Values :  Pointer to argument structure or NULL on error
8206bfebdeSXin LI +--------------------------------------------------------------------------*/
8306bfebdeSXin LI static void *
Generic_This_Type(void * arg)8406bfebdeSXin LI Generic_This_Type(void *arg)
8506bfebdeSXin LI {
8606bfebdeSXin LI   thisARG *argn = (thisARG *)0;
8706bfebdeSXin LI   thisPARM *args = (thisPARM *)arg;
8806bfebdeSXin LI 
8906bfebdeSXin LI   if (args)
9006bfebdeSXin LI     {
9106bfebdeSXin LI       argn = typeMalloc(thisARG, 1);
9206bfebdeSXin LI 
9306bfebdeSXin LI       if (argn)
9406bfebdeSXin LI 	{
9506bfebdeSXin LI 	  T((T_CREATE("thisARG %p"), (void *)argn));
9606bfebdeSXin LI 	  argn->precision = args->precision;
9706bfebdeSXin LI 	  argn->low = args->low;
9806bfebdeSXin LI 	  argn->high = args->high;
9906bfebdeSXin LI 
100aae38d10SBaptiste Daroussin #if HAVE_LOCALE_H && HAVE_LOCALECONV
10106bfebdeSXin LI 	  argn->L = localeconv();
10206bfebdeSXin LI #else
10306bfebdeSXin LI 	  argn->L = NULL;
10406bfebdeSXin LI #endif
10506bfebdeSXin LI 	}
10606bfebdeSXin LI     }
10706bfebdeSXin LI   return (void *)argn;
10806bfebdeSXin LI }
10906bfebdeSXin LI 
1100e3d5408SPeter Wemm /*---------------------------------------------------------------------------
1110e3d5408SPeter Wemm |   Facility      :  libnform
1124a1a9510SRong-En Fan |   Function      :  static void *Make_This_Type(va_list * ap)
1130e3d5408SPeter Wemm |
1140e3d5408SPeter Wemm |   Description   :  Allocate structure for numeric type argument.
1150e3d5408SPeter Wemm |
1160e3d5408SPeter Wemm |   Return Values :  Pointer to argument structure or NULL on error
1170e3d5408SPeter Wemm +--------------------------------------------------------------------------*/
1184a1a9510SRong-En Fan static void *
Make_This_Type(va_list * ap)1194a1a9510SRong-En Fan Make_This_Type(va_list *ap)
1200e3d5408SPeter Wemm {
12106bfebdeSXin LI   thisPARM arg;
1220e3d5408SPeter Wemm 
12306bfebdeSXin LI   arg.precision = va_arg(*ap, int);
12406bfebdeSXin LI   arg.low = va_arg(*ap, double);
12506bfebdeSXin LI   arg.high = va_arg(*ap, double);
1264a1a9510SRong-En Fan 
12706bfebdeSXin LI   return Generic_This_Type((void *)&arg);
1280e3d5408SPeter Wemm }
1290e3d5408SPeter Wemm 
1300e3d5408SPeter Wemm /*---------------------------------------------------------------------------
1310e3d5408SPeter Wemm |   Facility      :  libnform
1324a1a9510SRong-En Fan |   Function      :  static void *Copy_This_Type(const void * argp)
1330e3d5408SPeter Wemm |
1340e3d5408SPeter Wemm |   Description   :  Copy structure for numeric type argument.
1350e3d5408SPeter Wemm |
1360e3d5408SPeter Wemm |   Return Values :  Pointer to argument structure or NULL on error.
1370e3d5408SPeter Wemm +--------------------------------------------------------------------------*/
1384a1a9510SRong-En Fan static void *
Copy_This_Type(const void * argp)1394a1a9510SRong-En Fan Copy_This_Type(const void *argp)
1400e3d5408SPeter Wemm {
1414a1a9510SRong-En Fan   const thisARG *ap = (const thisARG *)argp;
1424a1a9510SRong-En Fan   thisARG *result = (thisARG *)0;
1430e3d5408SPeter Wemm 
1440e3d5408SPeter Wemm   if (argp)
1450e3d5408SPeter Wemm     {
1465ca44d1cSRong-En Fan       result = typeMalloc(thisARG, 1);
1477a656419SBaptiste Daroussin 
1480e3d5408SPeter Wemm       if (result)
1495ca44d1cSRong-En Fan 	{
15006bfebdeSXin LI 	  T((T_CREATE("thisARG %p"), (void *)result));
1510e3d5408SPeter Wemm 	  *result = *ap;
1520e3d5408SPeter Wemm 	}
1535ca44d1cSRong-En Fan     }
1540e3d5408SPeter Wemm   return (void *)result;
1550e3d5408SPeter Wemm }
1560e3d5408SPeter Wemm 
1570e3d5408SPeter Wemm /*---------------------------------------------------------------------------
1580e3d5408SPeter Wemm |   Facility      :  libnform
1594a1a9510SRong-En Fan |   Function      :  static void Free_This_Type(void * argp)
1600e3d5408SPeter Wemm |
1610e3d5408SPeter Wemm |   Description   :  Free structure for numeric type argument.
1620e3d5408SPeter Wemm |
1630e3d5408SPeter Wemm |   Return Values :  -
1640e3d5408SPeter Wemm +--------------------------------------------------------------------------*/
1654a1a9510SRong-En Fan static void
Free_This_Type(void * argp)1664a1a9510SRong-En Fan Free_This_Type(void *argp)
1670e3d5408SPeter Wemm {
1680e3d5408SPeter Wemm   if (argp)
1690e3d5408SPeter Wemm     free(argp);
1700e3d5408SPeter Wemm }
1710e3d5408SPeter Wemm 
1720e3d5408SPeter Wemm /*---------------------------------------------------------------------------
1730e3d5408SPeter Wemm |   Facility      :  libnform
1744a1a9510SRong-En Fan |   Function      :  static bool Check_This_Field(FIELD * field,
1750e3d5408SPeter Wemm |                                                 const void * argp)
1760e3d5408SPeter Wemm |
1770e3d5408SPeter Wemm |   Description   :  Validate buffer content to be a valid numeric value
1780e3d5408SPeter Wemm |
1790e3d5408SPeter Wemm |   Return Values :  TRUE  - field is valid
1800e3d5408SPeter Wemm |                    FALSE - field is invalid
1810e3d5408SPeter Wemm +--------------------------------------------------------------------------*/
1824a1a9510SRong-En Fan static bool
Check_This_Field(FIELD * field,const void * argp)1834a1a9510SRong-En Fan Check_This_Field(FIELD *field, const void *argp)
1840e3d5408SPeter Wemm {
1854a1a9510SRong-En Fan   const thisARG *argn = (const thisARG *)argp;
1860e3d5408SPeter Wemm   double low = argn->low;
1870e3d5408SPeter Wemm   double high = argn->high;
1880e3d5408SPeter Wemm   int prec = argn->precision;
1890e3d5408SPeter Wemm   unsigned char *bp = (unsigned char *)field_buffer(field, 0);
1900e3d5408SPeter Wemm   char *s = (char *)bp;
1910e3d5408SPeter Wemm   struct lconv *L = argn->L;
1924a1a9510SRong-En Fan   bool result = FALSE;
1930e3d5408SPeter Wemm 
194*21817992SBaptiste Daroussin   while (*bp == ' ')
1954a1a9510SRong-En Fan     bp++;
1960e3d5408SPeter Wemm   if (*bp)
1970e3d5408SPeter Wemm     {
1980e3d5408SPeter Wemm       if (*bp == '-' || *bp == '+')
1990e3d5408SPeter Wemm 	bp++;
2004a1a9510SRong-En Fan #if USE_WIDEC_SUPPORT
2014a1a9510SRong-En Fan       if (*bp)
2024a1a9510SRong-En Fan 	{
2034a1a9510SRong-En Fan 	  int len;
2044a1a9510SRong-En Fan 	  wchar_t *list = _nc_Widen_String((char *)bp, &len);
2054a1a9510SRong-En Fan 
2064a1a9510SRong-En Fan 	  if (list != 0)
2074a1a9510SRong-En Fan 	    {
208*21817992SBaptiste Daroussin 	      bool blank = FALSE;
209*21817992SBaptiste Daroussin 	      int state = 0;
210*21817992SBaptiste Daroussin 	      int n;
211*21817992SBaptiste Daroussin 
2124a1a9510SRong-En Fan 	      result = TRUE;
2134a1a9510SRong-En Fan 	      for (n = 0; n < len; ++n)
2144a1a9510SRong-En Fan 		{
2154a1a9510SRong-En Fan 		  if (blank)
2164a1a9510SRong-En Fan 		    {
2174a1a9510SRong-En Fan 		      if (list[n] != ' ')
2184a1a9510SRong-En Fan 			{
2194a1a9510SRong-En Fan 			  result = FALSE;
2204a1a9510SRong-En Fan 			  break;
2214a1a9510SRong-En Fan 			}
2224a1a9510SRong-En Fan 		    }
2234a1a9510SRong-En Fan 		  else if (list[n] == ' ')
2244a1a9510SRong-En Fan 		    {
2254a1a9510SRong-En Fan 		      blank = TRUE;
2264a1a9510SRong-En Fan 		    }
2274a1a9510SRong-En Fan 		  else if (isDecimalPoint(list[n]))
2284a1a9510SRong-En Fan 		    {
2294a1a9510SRong-En Fan 		      if (++state > 1)
2304a1a9510SRong-En Fan 			{
2314a1a9510SRong-En Fan 			  result = FALSE;
2324a1a9510SRong-En Fan 			  break;
2334a1a9510SRong-En Fan 			}
2344a1a9510SRong-En Fan 		    }
2354a1a9510SRong-En Fan 		  else if (!isDigit(list[n]))
2364a1a9510SRong-En Fan 		    {
2374a1a9510SRong-En Fan 		      result = FALSE;
2384a1a9510SRong-En Fan 		      break;
2394a1a9510SRong-En Fan 		    }
2404a1a9510SRong-En Fan 		}
2414a1a9510SRong-En Fan 	      free(list);
2424a1a9510SRong-En Fan 	    }
2434a1a9510SRong-En Fan 	}
2444a1a9510SRong-En Fan #else
2450e3d5408SPeter Wemm       while (*bp)
2460e3d5408SPeter Wemm 	{
2474a1a9510SRong-En Fan 	  if (!isdigit(UChar(*bp)))
2484a1a9510SRong-En Fan 	    break;
2490e3d5408SPeter Wemm 	  bp++;
2500e3d5408SPeter Wemm 	}
2514a1a9510SRong-En Fan       if (isDecimalPoint(*bp))
2524a1a9510SRong-En Fan 	{
2534a1a9510SRong-En Fan 	  bp++;
2544a1a9510SRong-En Fan 	  while (*bp)
2554a1a9510SRong-En Fan 	    {
2564a1a9510SRong-En Fan 	      if (!isdigit(UChar(*bp)))
2574a1a9510SRong-En Fan 		break;
2584a1a9510SRong-En Fan 	      bp++;
2594a1a9510SRong-En Fan 	    }
2604a1a9510SRong-En Fan 	}
2614a1a9510SRong-En Fan       while (*bp && *bp == ' ')
2624a1a9510SRong-En Fan 	bp++;
2634a1a9510SRong-En Fan       result = (*bp == '\0');
2640e3d5408SPeter Wemm #endif
2654a1a9510SRong-En Fan       if (result)
2660e3d5408SPeter Wemm 	{
267*21817992SBaptiste Daroussin 	  double val = atof(s);
268*21817992SBaptiste Daroussin 
2690e3d5408SPeter Wemm 	  if (low < high)
2700e3d5408SPeter Wemm 	    {
2714a1a9510SRong-En Fan 	      if (val < low || val > high)
2724a1a9510SRong-En Fan 		result = FALSE;
2730e3d5408SPeter Wemm 	    }
2744a1a9510SRong-En Fan 	  if (result)
2754a1a9510SRong-En Fan 	    {
276*21817992SBaptiste Daroussin 	      char buf[64];
277*21817992SBaptiste Daroussin 
27873f0a83dSXin LI 	      _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
27973f0a83dSXin LI 			  "%.*f", (prec > 0 ? prec : 0), val);
2800e3d5408SPeter Wemm 	      set_field_buffer(field, 0, buf);
2810e3d5408SPeter Wemm 	    }
2820e3d5408SPeter Wemm 	}
2834a1a9510SRong-En Fan     }
2844a1a9510SRong-En Fan   return (result);
2850e3d5408SPeter Wemm }
2860e3d5408SPeter Wemm 
2870e3d5408SPeter Wemm /*---------------------------------------------------------------------------
2880e3d5408SPeter Wemm |   Facility      :  libnform
2894a1a9510SRong-En Fan |   Function      :  static bool Check_This_Character(
2900e3d5408SPeter Wemm |                                      int c,
2910e3d5408SPeter Wemm |                                      const void * argp)
2920e3d5408SPeter Wemm |
2930e3d5408SPeter Wemm |   Description   :  Check a character for the numeric type.
2940e3d5408SPeter Wemm |
2950e3d5408SPeter Wemm |   Return Values :  TRUE  - character is valid
2960e3d5408SPeter Wemm |                    FALSE - character is invalid
2970e3d5408SPeter Wemm +--------------------------------------------------------------------------*/
2984a1a9510SRong-En Fan static bool
Check_This_Character(int c,const void * argp)2994a1a9510SRong-En Fan Check_This_Character(int c, const void *argp)
3000e3d5408SPeter Wemm {
3014a1a9510SRong-En Fan   const thisARG *argn = (const thisARG *)argp;
3020e3d5408SPeter Wemm   struct lconv *L = argn->L;
3030e3d5408SPeter Wemm 
3044a1a9510SRong-En Fan   return ((isDigit(c) ||
3050e3d5408SPeter Wemm 	   c == '+' ||
3060e3d5408SPeter Wemm 	   c == '-' ||
3074a1a9510SRong-En Fan 	   isDecimalPoint(c))
3084a1a9510SRong-En Fan 	  ? TRUE
3094a1a9510SRong-En Fan 	  : FALSE);
3100e3d5408SPeter Wemm }
3110e3d5408SPeter Wemm 
3124a1a9510SRong-En Fan static FIELDTYPE typeTHIS =
3134a1a9510SRong-En Fan {
3140e3d5408SPeter Wemm   _HAS_ARGS | _RESIDENT,
3150e3d5408SPeter Wemm   1,				/* this is mutable, so we can't be const */
3160e3d5408SPeter Wemm   (FIELDTYPE *)0,
3170e3d5408SPeter Wemm   (FIELDTYPE *)0,
3184a1a9510SRong-En Fan   Make_This_Type,
3194a1a9510SRong-En Fan   Copy_This_Type,
3204a1a9510SRong-En Fan   Free_This_Type,
32106bfebdeSXin LI   INIT_FT_FUNC(Check_This_Field),
32206bfebdeSXin LI   INIT_FT_FUNC(Check_This_Character),
32306bfebdeSXin LI   INIT_FT_FUNC(NULL),
32406bfebdeSXin LI   INIT_FT_FUNC(NULL),
32506bfebdeSXin LI #if NCURSES_INTEROP_FUNCS
32606bfebdeSXin LI   Generic_This_Type
32706bfebdeSXin LI #endif
3280e3d5408SPeter Wemm };
3290e3d5408SPeter Wemm 
3307a656419SBaptiste Daroussin FORM_EXPORT_VAR(FIELDTYPE *) TYPE_NUMERIC = &typeTHIS;
3310e3d5408SPeter Wemm 
33206bfebdeSXin LI #if NCURSES_INTEROP_FUNCS
33306bfebdeSXin LI /* The next routines are to simplify the use of ncurses from
3347a656419SBaptiste Daroussin    programming languages with restrictions on interop with C level
33506bfebdeSXin LI    constructs (e.g. variable access or va_list + ellipsis constructs)
33606bfebdeSXin LI */
3377a656419SBaptiste Daroussin FORM_EXPORT(FIELDTYPE *)
_nc_TYPE_NUMERIC(void)33806bfebdeSXin LI _nc_TYPE_NUMERIC(void)
33906bfebdeSXin LI {
34006bfebdeSXin LI   return TYPE_NUMERIC;
34106bfebdeSXin LI }
34206bfebdeSXin LI #endif
34306bfebdeSXin LI 
3440e3d5408SPeter Wemm /* fty_num.c ends here */
345