mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			129 lines
		
	
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Sane helper for 'strtod()'.
 | |
|  *
 | |
|  * Sad that we even need this, but the C library version has
 | |
|  * insane locale behavior, and while the Qt "doDouble()" routines
 | |
|  * are better in that regard, they don't have an end pointer
 | |
|  * (having replaced it with the completely idiotic "ok" boolean
 | |
|  * pointer instead).
 | |
|  *
 | |
|  * I wonder what drugs people are on sometimes.
 | |
|  *
 | |
|  * Right now we support the following flags to limit the
 | |
|  * parsing some ways:
 | |
|  *
 | |
|  *   STRTOD_NO_SIGN	- don't accept signs
 | |
|  *   STRTOD_NO_DOT	- no decimal dots, I'm European
 | |
|  *   STRTOD_NO_COMMA	- no comma, please, I'm C locale
 | |
|  *   STRTOD_NO_EXPONENT	- no exponent parsing, I'm human
 | |
|  *
 | |
|  * The "negative" flags are so that the common case can just
 | |
|  * use a flag value of 0, and only if you have some special
 | |
|  * requirements do you need to state those with explicit flags.
 | |
|  *
 | |
|  * So if you want the C locale kind of parsing, you'd use the
 | |
|  * STRTOD_NO_COMMA flag to disallow a decimal comma. But if you
 | |
|  * want a more relaxed "Hey, Europeans are people too, even if
 | |
|  * they have locales with commas", just pass in a zero flag.
 | |
|  */
 | |
| #include <ctype.h>
 | |
| #include "dive.h"
 | |
| 
 | |
| double strtod_flags(const char *str, const char **ptr, unsigned int flags)
 | |
| {
 | |
| 	char c;
 | |
| 	const char *p = str, *ep;
 | |
| 	double val = 0.0;
 | |
| 	double decimal = 1.0;
 | |
| 	int sign = 0, esign = 0;
 | |
| 	int numbers = 0, dot = 0;
 | |
| 
 | |
| 	/* skip spaces */
 | |
| 	while (isspace(c = *p++))
 | |
| 		/* */;
 | |
| 
 | |
| 	/* optional sign */
 | |
| 	if (!(flags & STRTOD_NO_SIGN)) {
 | |
| 		switch (c) {
 | |
| 		case '-':
 | |
| 			sign = 1;
 | |
| 		/* fallthrough */
 | |
| 		case '+':
 | |
| 			c = *p++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Mantissa */
 | |
| 	for (;; c = *p++) {
 | |
| 		if ((c == '.' && !(flags & STRTOD_NO_DOT)) ||
 | |
| 		    (c == ',' && !(flags & STRTOD_NO_COMMA))) {
 | |
| 			if (dot)
 | |
| 				goto done;
 | |
| 			dot = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (c >= '0' && c <= '9') {
 | |
| 			numbers++;
 | |
| 			val = (val * 10) + (c - '0');
 | |
| 			if (dot)
 | |
| 				decimal *= 10;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (c != 'e' && c != 'E')
 | |
| 			goto done;
 | |
| 		if (flags & STRTOD_NO_EXPONENT)
 | |
| 			goto done;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (!numbers)
 | |
| 		goto done;
 | |
| 
 | |
| 	/* Exponent */
 | |
| 	ep = p;
 | |
| 	c = *ep++;
 | |
| 	switch (c) {
 | |
| 	case '-':
 | |
| 		esign = 1;
 | |
| 	/* fallthrough */
 | |
| 	case '+':
 | |
| 		c = *ep++;
 | |
| 	}
 | |
| 
 | |
| 	if (c >= '0' && c <= '9') {
 | |
| 		p = ep;
 | |
| 		int exponent = c - '0';
 | |
| 
 | |
| 		for (;;) {
 | |
| 			c = *p++;
 | |
| 			if (c < '0' || c > '9')
 | |
| 				break;
 | |
| 			exponent *= 10;
 | |
| 			exponent += c - '0';
 | |
| 		}
 | |
| 
 | |
| 		/* We're not going to bother playing games */
 | |
| 		if (exponent > 308)
 | |
| 			exponent = 308;
 | |
| 
 | |
| 		while (exponent-- > 0) {
 | |
| 			if (esign)
 | |
| 				decimal *= 10;
 | |
| 			else
 | |
| 				decimal /= 10;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	if (!numbers)
 | |
| 		goto no_conversion;
 | |
| 	if (ptr)
 | |
| 		*ptr = p - 1;
 | |
| 	return (sign ? -val : val) / decimal;
 | |
| 
 | |
| no_conversion:
 | |
| 	if (ptr)
 | |
| 		*ptr = str;
 | |
| 	return 0.0;
 | |
| }
 |