1/* $NetBSD: type_numeric.c,v 1.10 2021/04/13 13:13:04 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1998-1999 Brett Lymn 5 * (blymn@baea.com.au, brett_lymn@yahoo.com.au) 6 * All rights reserved. 7 * 8 * This code has been donated to The NetBSD Foundation by the Author. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * 30 */ 31 32#include <sys/cdefs.h> 33__RCSID("$NetBSD: type_numeric.c,v 1.10 2021/04/13 13:13:04 christos Exp $"); 34 35#include <stdlib.h> 36#include <string.h> 37#include <ctype.h> 38#include "form.h" 39#include "internals.h" 40 41/* 42 * The numeric type handling. 43 */ 44 45typedef struct 46{ 47 unsigned precision; 48 double min; 49 double max; 50} numeric_args; 51 52/* 53 * Create the numeric arguments structure from the given args. Return NULL 54 * if the call fails, otherwise return a pointer to the structure allocated. 55 */ 56static char * 57create_numeric_args(va_list *args) 58{ 59 numeric_args *new; 60 61 new = malloc(sizeof(*new)); 62 63 if (new != NULL) { 64 new->precision = va_arg(*args, unsigned); 65 new->min = va_arg(*args, double); 66 new->max = va_arg(*args, double); 67 } 68 69 return (void *) new; 70} 71 72/* 73 * Copy the numeric argument structure. 74 */ 75static char * 76copy_numeric_args(char *args) 77{ 78 numeric_args *new; 79 80 new = malloc(sizeof(*new)); 81 82 if (new != NULL) 83 memcpy(new, args, sizeof(*args)); 84 85 return (void *) new; 86} 87 88/* 89 * Free the allocated storage associated with the type arguments. 90 */ 91static void 92free_numeric_args(char *args) 93{ 94 if (args != NULL) 95 free(args); 96} 97 98/* 99 * Check the contents of the field buffer are numeric only. A valid 100 * number is of the form nnnn[.mmmmm][Ee[+-]ddd] 101 */ 102static int 103numeric_check_field(FIELD *field, char *args) 104{ 105 int cur; 106 double number, max, min; 107 int precision; 108 char *buf, *new_buf; 109 110 if (args == NULL) 111 return FALSE; 112 113 precision = ((numeric_args *) (void *) field->args)->precision; 114 min = ((numeric_args *) (void *) field->args)->min; 115 max = ((numeric_args *) (void *) field->args)->max; 116 117 buf = args; 118 cur = 0; 119 120 /* skip leading white space */ 121 while ((buf[cur] != '\0') 122 && ((buf[cur] == ' ') || (buf[cur] == '\t'))) 123 cur++; 124 125 /* no good if we have hit the end */ 126 if (buf[cur] == '\0') 127 return FALSE; 128 129 /* find the end of the digits but allow a leading + or - sign, and 130 * a decimal point. 131 */ 132 if ((buf[cur] == '-') || (buf[cur] == '+')) 133 cur++; 134 135 while(isdigit((unsigned char)buf[cur])) 136 cur++; 137 138 /* if not at end of string then check for decimal... */ 139 if (buf[cur] == '.') { 140 cur++; 141 /* check for more digits now.... */ 142 while (buf[cur] && isdigit((unsigned char)buf[cur])) 143 cur++; 144 } 145 146 /* check for an exponent */ 147 if ((buf[cur] == 'E') || (buf[cur] == 'e')) { 148 cur++; 149 if (buf[cur] == '\0') 150 return FALSE; 151 152 /* allow a + or a - for exponent */ 153 if ((buf[cur] == '+') || (buf[cur] == '-')) 154 cur++; 155 156 if (buf[cur] == '\0') 157 return FALSE; 158 159 /* we expect a digit now */ 160 if (!isdigit((unsigned char)buf[cur])) 161 return FALSE; 162 163 /* skip digits for the final time */ 164 while(isdigit((unsigned char)buf[cur])) 165 cur++; 166 } 167 168 /* check there is only trailing whitespace */ 169 while ((buf[cur] != '\0') 170 && ((buf[cur] == ' ') || (buf[cur] == '\t'))) 171 cur++; 172 173 /* no good if we are not at the end of the string */ 174 if (buf[cur] != '\0') 175 return FALSE; 176 177 /* convert and range check the number...*/ 178 number = atof(buf); 179 if ((min < max) && ((number < min) || (number > max))) 180 return FALSE; 181 182 if (asprintf(&new_buf, "%.*f", precision, number) < 0) 183 return FALSE; 184 185 /* re-set the field buffer to be the reformatted numeric */ 186 set_field_buffer(field, 0, new_buf); 187 188 free(new_buf); 189 190 /* otherwise all was ok */ 191 return TRUE; 192} 193 194/* 195 * Check the given character is numeric, return TRUE if it is. 196 */ 197static int 198numeric_check_char(/* ARGSUSED1 */ int c, char *args) 199{ 200 return ((isdigit(c) || (c == '-') || (c == '+') 201 || (c == '.') || (c == 'e') || (c == 'E')) ? TRUE : FALSE); 202} 203 204static FIELDTYPE builtin_numeric = { 205 _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 206 0, /* refcount */ 207 NULL, /* link */ 208 create_numeric_args, /* make_args */ 209 copy_numeric_args, /* copy_args */ 210 free_numeric_args, /* free_args */ 211 numeric_check_field, /* field_check */ 212 numeric_check_char, /* char_check */ 213 NULL, /* next_choice */ 214 NULL /* prev_choice */ 215}; 216 217FIELDTYPE *TYPE_NUMERIC = &builtin_numeric; 218 219 220