1 /* $NetBSD: type_numeric.c,v 1.8 2004/10/28 21:14:52 dsl 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.8 2004/10/28 21:14:52 dsl 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 45 typedef 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 */ 56 static char * 57 create_numeric_args(va_list *args) 58 { 59 numeric_args *new; 60 61 new = (numeric_args *) malloc(sizeof(numeric_args)); 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 */ 75 static char * 76 copy_numeric_args(char *args) 77 { 78 numeric_args *new; 79 80 new = (numeric_args *) malloc(sizeof(numeric_args)); 81 82 if (new != NULL) 83 bcopy(args, new, sizeof(numeric_args)); 84 85 return (void *) new; 86 } 87 88 /* 89 * Free the allocated storage associated with the type arguments. 90 */ 91 static void 92 free_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 */ 102 static int 103 numeric_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] != '\0') && (buf[cur] == '.')) { 140 cur++; 141 /* check for more digits now.... */ 142 while(isdigit((unsigned char)buf[cur])) 143 cur++; 144 } 145 146 /* check for an exponent */ 147 if ((buf[cur] != '\0') && 148 ((buf[cur] == 'E') || (buf[cur] == 'e'))) { 149 cur++; 150 if (buf[cur] == '\0') 151 return FALSE; 152 153 /* allow a + or a - for exponent */ 154 if ((buf[cur] == '+') || (buf[cur] == '-')) 155 cur++; 156 157 if (buf[cur] == '\0') 158 return FALSE; 159 160 /* we expect a digit now */ 161 if (!isdigit((unsigned char)buf[cur])) 162 return FALSE; 163 164 /* skip digits for the final time */ 165 while(isdigit((unsigned char)buf[cur])) 166 cur++; 167 } 168 169 /* check there is only trailing whitespace */ 170 while ((buf[cur] != '\0') 171 && ((buf[cur] == ' ') || (buf[cur] == '\t'))) 172 cur++; 173 174 /* no good if we are not at the end of the string */ 175 if (buf[cur] != '\0') 176 return FALSE; 177 178 /* convert and range check the number...*/ 179 number = atof(buf); 180 if ((min < max) && ((number < min) || (number > max))) 181 return FALSE; 182 183 if (asprintf(&new_buf, "%.*f", precision, number) < 0) 184 return FALSE; 185 186 /* re-set the field buffer to be the reformatted numeric */ 187 set_field_buffer(field, 0, new_buf); 188 189 free(new_buf); 190 191 /* otherwise all was ok */ 192 return TRUE; 193 } 194 195 /* 196 * Check the given character is numeric, return TRUE if it is. 197 */ 198 static int 199 numeric_check_char(/* ARGSUSED1 */ int c, char *args) 200 { 201 return ((isdigit(c) || (c == '-') || (c == '+') 202 || (c == '.') || (c == 'e') || (c == 'E')) ? TRUE : FALSE); 203 } 204 205 static FIELDTYPE builtin_numeric = { 206 _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */ 207 0, /* refcount */ 208 NULL, /* link */ 209 create_numeric_args, /* make_args */ 210 copy_numeric_args, /* copy_args */ 211 free_numeric_args, /* free_args */ 212 numeric_check_field, /* field_check */ 213 numeric_check_char, /* char_check */ 214 NULL, /* next_choice */ 215 NULL /* prev_choice */ 216 }; 217 218 FIELDTYPE *TYPE_NUMERIC = &builtin_numeric; 219 220 221