libCom: Add routines for parsing strings into epicsTypes
Adds new routines for converting numeric strings into the standard epicsInt8, epicsUInt8, epicsInt16, epicsUInt16, epicsInt32 and epicsUInt32 types, along with Long, ULong, Double and Float. These all provide error checking and detection of extraneous characters. The epicsScanDouble and epicsScanFloat routines originally in epicsStdlib.h are replaced by macros that call the epicsParse routine, and this also provides epicsScanLong and epicsScanULong to match. A test file is added to ensure conversions work properly and report appropriate errors. This file also checks the native strtod() routine if not used to check whether the epicsStrtod() code is required on this platform.
This commit is contained in:
+221
-17
@@ -1,50 +1,248 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 UChicago Argonna LLC, as Operator of Argonne
|
||||
* Copyright (c) 2012 UChicago Argonna LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*epicsStdlib.c*/
|
||||
/*Author: Eric Norum */
|
||||
/* $Revision-Id$ */
|
||||
/* Authors: Eric Norum & Andrew Johnson */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsMath.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsConvert.h"
|
||||
|
||||
|
||||
epicsShareFunc int epicsScanDouble(const char *str, double *dest)
|
||||
/* These are the conversion primitives */
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseLong(const char *str, long *to, int base)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double dtmp;
|
||||
long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtol(str, &endp, base);
|
||||
|
||||
dtmp = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return 0;
|
||||
*dest = dtmp;
|
||||
return 1;
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int epicsScanFloat(const char *str, float *dest)
|
||||
epicsShareFunc int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double dtmp;
|
||||
unsigned long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(str, &endp, base);
|
||||
|
||||
dtmp = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return 0;
|
||||
*dest = (float)dtmp;
|
||||
return 1;
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Systems with a working strtod() just #define epicsStrtod strtod */
|
||||
epicsShareFunc int
|
||||
epicsParseDouble(const char *str, double *to)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* These call the primitives */
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x80 || value > 0x7f)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x8000 || value > 0x7fff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xffff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (LONG_MAX > 0x7fffffff)
|
||||
if (value < -0x80000000L || value > 0x7fffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (ULONG_MAX > 0xffffffff)
|
||||
if (value > 0xffffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseFloat(const char *str, float *to)
|
||||
{
|
||||
double value, abs;
|
||||
int status = epicsParseDouble(str, &value);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
abs = fabs(value);
|
||||
if (value > 0 && abs <= FLT_MIN)
|
||||
return S_stdlib_underflow;
|
||||
if (finite(value) && abs >= FLT_MAX)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* If strtod() works properly, osdStrtod.h defines this macro:
|
||||
* #define epicsStrtod strtod
|
||||
*
|
||||
* If strtod() is broken, osdStrtod.h defines this prototype:
|
||||
* epicsShareFunc double epicsStrtod(const char *str, char **endp);
|
||||
*/
|
||||
|
||||
#ifndef epicsStrtod
|
||||
epicsShareFunc double epicsStrtod(const char *str, char **endp)
|
||||
epicsShareFunc double
|
||||
epicsStrtod(const char *str, char **endp)
|
||||
{
|
||||
const char *cp = str;
|
||||
int negative = 0;
|
||||
@@ -60,6 +258,12 @@ epicsShareFunc double epicsStrtod(const char *str, char **endp)
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (epicsStrnCaseCmp("0x", cp, 2) == 0) {
|
||||
if (negative)
|
||||
return strtol(str, endp, 16);
|
||||
else
|
||||
return strtoul(str, endp, 16);
|
||||
}
|
||||
if (!isalpha((int)*cp))
|
||||
return strtod(str, endp);
|
||||
|
||||
|
||||
@@ -1,26 +1,67 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*epicsStdlib.h*/
|
||||
/*Author: Eric Norum */
|
||||
/* epicsStdlib.h */
|
||||
/* Author: Eric Norum */
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "osdStrtod.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc int epicsScanDouble(const char *str, double *dest);
|
||||
epicsShareFunc int epicsScanFloat(const char *str, float *dest);
|
||||
#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */
|
||||
#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */
|
||||
#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */
|
||||
#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */
|
||||
#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osdStrtod.h>
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseLong(const char *str, long *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseDouble(const char *str, double *to);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseFloat(const char *str, float *to);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base);
|
||||
|
||||
#define epicsParseFloat32(str, to) epicsParseFloat(str, to)
|
||||
#define epicsParseFloat64(str, to) epicsParseDouble(str, to)
|
||||
|
||||
/* These macros return 1 if successful, 0 on failure.
|
||||
* This is analagous to the return value from sscanf()
|
||||
*/
|
||||
#define epicsScanLong(str, to, base) !epicsParseLong(str, to, base)
|
||||
#define epicsScanULong(str, to, base) !epicsParseULong(str, to, base)
|
||||
#define epicsScanFloat(str, to) !epicsParseFloat(str, to)
|
||||
#define epicsScanDouble(str, to) !epicsParseDouble(str, to)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user