xref: /minix/lib/libform/type_numeric.c (revision a0e6850f)
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