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