diff options
Diffstat (limited to 'src/lib/ppc405lib/strtox.c')
-rw-r--r-- | src/lib/ppc405lib/strtox.c | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/src/lib/ppc405lib/strtox.c b/src/lib/ppc405lib/strtox.c new file mode 100644 index 0000000..56493fa --- /dev/null +++ b/src/lib/ppc405lib/strtox.c @@ -0,0 +1,617 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strtox.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: strtox.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strtox.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strtox.c +/// \brief Implementation of strtol(), strtoul(), strtoll() and strtoull() +/// +/// <b> Standard String Conversion Routines </b> +/// +/// This file contains implementaions of strtol(), strtoul(), strtoll() and +/// strtoull(). These APIs are all called as +/// +/// \code +/// strtoX(const char* str, char** endptr, int base) +/// \endcode +/// +/// where X is +/// +/// - l : Convert to a long integer +/// - ul : Convert to an unsigned long integer +/// - ll : Convert to a long long integer +/// - ull : Convert to an unsigned long long integer +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// The APIs convert the initial portion of the string pointed to by \a str to +/// an integer, which is either a long integer (strtol), an unsigned long +/// (strtoul()), a long long (strtoll), or an unsigned long long +/// (strtoull). First, the APIs decompose the input string into three parts: +/// +/// - An initial, possibly empty, sequence of white-space characters (as +/// specified by isspace()) +/// +/// - A subject sequence interpreted as an integer represented in some radix +/// determined by the value of \a base +/// +/// - A final string of one or more unrecognized characters, including the +/// terminating null byte of the input string. +/// +/// The APIs then attempt to convert the subject sequence to an integer of the +/// required type and returns the result. +/// +/// If the value of \a base is 0, the expected form of the subject sequence is +/// that of a decimal constant, octal constant, or hexadecimal constant, any +/// of which may be preceded by a '+' or '-' sign. A decimal constant begins +/// with a non-zero digit, and consists of a sequence of decimal digits. An +/// octal constant consists of the prefix '0' optionally followed by a +/// sequence of the digits '0' to '7' only. A hexadecimal constant consists of +/// the prefix 0x or 0X followed by a sequence of the decimal digits and +/// letters 'a' (or 'A' ) to 'f' (or 'F' ) with values 10 to 15 respectively. +/// +/// If the value of \a base is between 2 and 36, the expected form of the +/// subject sequence is a sequence of letters and digits representing an +/// integer with the radix specified by base, optionally preceded by a '+' or +/// '-' sign. The letters from 'a' (or 'A' ) to 'z' (or 'Z' ) inclusive are +/// ascribed the values 10 to 35; only letters whose ascribed values are less +/// than that of base are permitted. If the value of base is 16, the +/// characters 0x or 0X may optionally precede the sequence of letters and +/// digits, following the sign if present. +/// +/// The subject sequence is defined as the longest initial subsequence of the +/// input string, starting with the first non-white-space character that is of +/// the expected form. The subject sequence contains no characters if the +/// input string is empty or consists entirely of white-space characters, or if +/// the first non-white-space character is other than a sign or a permissible +/// letter or digit. +/// +/// If the subject sequence has the expected form and the value of base is 0, +/// the sequence of characters starting with the first digit will be +/// interpreted as an integer constant. If the subject sequence has the +/// expected form and the value of base is between 2 and 36, it will be used +/// as the base for conversion, ascribing to each letter its value as given +/// above. If the subject sequence begins with a minus sign, the value +/// resulting from the conversion will be negated. A pointer to the final +/// string will be stored in the object pointed to by \a endptr, provided that +/// \a endptr is not a null pointer. +/// +/// If the subject sequence is empty or does not have the expected form, no +/// conversion is performed; the value of \a str is stored in the object +/// pointed to by \a endptr, provided that \a endptr is not a null pointer. +/// +/// Note that the unsigned APIs silently convert signed representations into +/// the equivalent unsigned number. +/// +/// Since 0, (L)LONG_MIN and (U)(L)LONG_MAX are returned on error and are +/// also valid returns on success, there is no way for an SSX application to +/// determine whether the conversion succeeded or failed (since SSX does not +/// support \a errno). For this reason it is recommended that SSX-only +/// applications use the underlying APIs _strtol(), _strtoul(), _strtoll() and +/// _strtoull(), or even better the extended APIs strtoi32(), strtou32(), +/// strtoi64() or strtou64() discussed further below. +/// +/// Upon successful completion, strtoX() returns the converted +/// value, if any. If no conversion could be performed or there was an error +/// in the base specification, 0 is returned. +/// +/// If the correct value is outside the range of representable values, +/// (L)LONG_MIN or (U)(L)LONG_MAX will be returned (according to the sign +/// and type of the value). +/// +/// Note: This specification is adapted from IEEE Std. 10003.1, 2003 Edition +/// +/// +/// <b> Underlying APIs </b> +/// +/// The APIs underlying the standard APIs are all called as +/// +/// \code +/// int _strtoX(const char* str, char** endptr, int radix, <type>* value) +/// \endcode +/// +/// where X is +/// +/// - l : Convert to a long integer +/// - ul : Convert to an unsigned long integer +/// - ll : Convert to a long long integer +/// - ull : Convert to an unsigned long long integer +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// \param value The converted value, returned as the return value of the +/// standard API. +/// +/// The return value of the underlying APIs is one of the following +/// +/// \retval 0 Success +/// +/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the +/// string was effectively empty. +/// +/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the +/// string did not parse as an integer. +/// +/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the +/// \a base specification was not valid. +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow +/// +/// +/// <b> Extended APIs </b> +/// +/// The extended APIs are the preferred way to do portable integer +/// conversion. These APIs are all called as +/// +/// \code +/// int strtoX(const char* str, char** endptr, int radix, <type>* value) +/// \endcode +/// +/// where X is +/// +/// - i32 : Convert to an int32_t +/// - u32 : Convert to a uint32_t +/// - i64 : Convert to an int64_t +/// - u64 : Convert to a uint64_t +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// \param value The converted value +/// +/// The return value of the underlying APIs is one of the following +/// +/// \retval 0 Success +/// +/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the +/// string was effectively empty. +/// +/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the +/// string did not parse as an integer. +/// +/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the +/// \a base specification was not valid. +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow +/// + +#include "ssx.h" +#include "ctype.h" +#include "libssx.h" +#include "strtox.h" + + +// Skip whitespace + +static const char * +skip_whitespace(const char *s) +{ + while (isspace(*s)) { + s++; + } + return s; +} + +// Pick up a +/- sign. This is a predicate returning 1 if the value is +// negated. + +static int +sign(const char** s) +{ + if (**s == '+') { + (*s)++; + return 0; + } else if (**s == '-') { + (*s)++; + return 1; + } else { + return 0; + } +} + + +// Look for a radix mark (0, 0[xX]). The string pointer is advanced if it is a +// hex mark (0[xX]), but not for a simple '0' which could be either the start +// of an octal constant or simply the number 0. The return value is either 8, +// 10 or 16. + +static int +radix_mark(const char** s) +{ + const char* p = *s; + + if (p[0] == '0') { + if ((p[1] == 'x') || (p[1] == 'X')) { + *s += 2; + return 16; + } else { + return 8; + } + } else { + return 10; + } +} + + +// Parse a character as a radix-base digit. Return the value of the digit or +// -1 if it is not a legal digit for the radix. + +static int +parse_digit(char c, int radix) +{ + if (isdigit(c)) { + if ((c - '0') < radix) { + return c - '0'; + } else { + return -1; + } + } else if (radix <= 10) { + return -1; + } else { + if (islower(c)) { + if ((c - 'a') < (radix - 10)) { + return c - 'a' + 10; + } else { + return -1; + } + } else if (isupper(c)) { + if ((c - 'A') < (radix - 10)) { + return c - 'A' + 10; + } else { + return -1; + } + } else { + return -1; + } + } +} + + +// The most basic API is strtox(), which converts a string to an unsigned long +// long. All of the base APIs are written in terms of this. This is legal due +// to the fact that conversion is defined to continue even in the event of +// overflow. This API may return the codes STRTOX_NO_CONVERSION_EMPTY, +// STRTOX_NO_CONVERSION_PARSE or STRTOX_INVALID_ARGUMENT, +// which the standard APIs always convert to a 0 +// return value. Otherwise the flags 'overflow' and 'negative' are used by +// the base APIs to determine how to handle special cases. + +static int +strtox(const char *str, char **endptr, int base, + unsigned long long* value, int* negative, int* overflow) +{ + const char* s; + unsigned long long new; + int rc, radix, digit; + + + do { + + s = str; + *value = 0; + *negative = 0; + *overflow = 0; + + // Initial error checks + + if ((base != 0) && ((base < 2) || (base > 36))) { + rc = STRTOX_INVALID_ARGUMENT; + break; + } + + // Skip whitespace + + s = skip_whitespace(s); + if (*s == '\0') { + rc = STRTOX_NO_CONVERSION_EMPTY; + break; + } + + // Process a +/- sign. Only one is allowed. + + *negative = sign(&s); + + // Look for a radix mark. Note that if base == 16 this will cause the + // skip of a leading 0 in the string not followed by [xX], but that's + // OK because it doesn't change the result of the conversion. + + if (base == 0) { + radix = radix_mark(&s); + } else { + radix = base; + if (radix == 16) { + radix_mark(&s); + } + } + + // Parse. Note that once overflow is detected we continue to parse + // (but ignore the data). + + rc = STRTOX_NO_CONVERSION_PARSE; + + while ((digit = parse_digit(*s, radix)) >= 0) { + s++; + if (!*overflow) { + rc = 0; + new = (*value * radix) + digit; + if (new < *value) { + *overflow = 1; + } else { + *value = new; + } + } + } + } while(0); + + if (endptr) { + if (rc == 0) { + *endptr = (char*)s; + } else { + *endptr = (char*)str; + } + } + + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtol(const char* str, char** endptr, int base, long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow || (value_ull != (unsigned long)value_ull)) { + if (negative) { + rc = STRTOX_UNDERFLOW_STRTOL1; + *value = LONG_MIN; + } else { + rc = STRTOX_OVERFLOW_STRTOL1; + *value = LONG_MAX; + } + } else if (negative) { + if (value_ull > ((unsigned long long)LONG_MAX + 1ull)) { + rc = STRTOX_UNDERFLOW_STRTOL2; + *value = LONG_MIN; + } else { + *value = ~value_ull + 1; + } + } else if (value_ull > (unsigned long long)LONG_MAX) { + rc = STRTOX_OVERFLOW_STRTOL2; + *value = LONG_MAX; + } else { + *value = value_ull; + } + } + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtoll(const char* str, char** endptr, int base, long long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow) { + if (negative) { + rc = STRTOX_UNDERFLOW_STRTOLL1; + *value = LLONG_MIN; + } else { + rc = STRTOX_OVERFLOW_STRTOLL1; + *value = LLONG_MAX; + } + } else if (negative) { + if (value_ull > ((unsigned long long)LLONG_MAX + 1ull)) { + rc = STRTOX_UNDERFLOW_STRTOLL2; + *value = LLONG_MIN; + } else { + *value = ~value_ull + 1; + } + } else if (value_ull > (unsigned long long)LLONG_MAX) { + rc = STRTOX_OVERFLOW_STRTOLL2; + *value = LLONG_MAX; + } else { + *value = value_ull; + } + } + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtoul(const char* str, char** endptr, int base, unsigned long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow || (value_ull != (unsigned long)value_ull)) { + rc = STRTOX_OVERFLOW_STRTOUL; + *value = ULONG_MAX; + } else { + *value = value_ull; + if (negative) { + *value = ~*value + 1; + } + } + } + return rc; +} + +/// See documentation for the file strtox.c +int +_strtoull(const char* str, char** endptr, int base, unsigned long long* value) +{ + int rc, negative, overflow; + + rc = strtox(str, endptr, base, value, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow) { + rc = STRTOX_OVERFLOW_STRTOULL; + *value = ULLONG_MAX; + } else { + if (negative) { + *value = ~*value + 1; + } + } + } + return rc; +} + + +/// See documentation for the file strtox.c +long int +strtol(const char* str, char** endptr, int base) +{ + long int value; + + _strtol(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +long long int +strtoll(const char* str, char** endptr, int base) +{ + long long int value; + + _strtoll(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +unsigned long int +strtoul(const char* str, char** endptr, int base) +{ + unsigned long int value; + + _strtoul(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +unsigned long long int +strtoull(const char* str, char** endptr, int base) +{ + unsigned long long int value; + + _strtoull(str, endptr, base, &value); + return value; +} + + +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 1)) + +/// Internal version of strtol() +/// +/// ppcnf-mcp5 (GCC 4.1) requires that the entry point __strtol_internal() be +/// present at certain optimization levels. This is equivalent to strtol() +/// except that it takes an extra argument that must be == 0. The \a group +/// parameter is supposed to control locale-specific thousands grouping. + +long int +__strtol_internal(const char* str, char** endptr, int base, int group) +{ + if (group != 0) { + SSX_PANIC(STRTOX_INVALID_ARGUMENT_STRTOL); + } + return strtol(str, endptr, base); +} + +#endif + |