summaryrefslogtreecommitdiff
path: root/snmplib/strtoull.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/strtoull.c')
-rw-r--r--snmplib/strtoull.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/snmplib/strtoull.c b/snmplib/strtoull.c
new file mode 100644
index 0000000..6c98fc4
--- /dev/null
+++ b/snmplib/strtoull.c
@@ -0,0 +1,158 @@
+/*
+ * An implementation of strtoull() for compilers that do not have this
+ * function, e.g. MSVC.
+ * See also http://www.opengroup.org/onlinepubs/000095399/functions/strtoul.html
+ * for more information about strtoull().
+ */
+
+
+/*
+ * For MSVC, disable the warning "unary minus operator applied to unsigned
+ * type, result still unsigned"
+ */
+#ifdef _MSC_VER
+#pragma warning (disable: 4146)
+#endif
+
+
+#define __STDC_CONSTANT_MACROS /* Enable UINT64_C in <stdint.h> */
+#define __STDC_FORMAT_MACROS /* Enable PRIu64 in <inttypes.h> */
+
+#include <net-snmp/net-snmp-config.h>
+
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <net-snmp/types.h>
+#include <net-snmp/library/system.h>
+
+/*
+ * UINT64_C: C99 macro for the suffix for uint64_t constants.
+ */
+#ifndef UINT64_C
+#ifdef _MSC_VER
+#define UINT64_C(c) c##ui64
+#else
+#define UINT64_C(c) c##ULL
+#endif
+#endif
+
+/*
+ * According to the C99 standard, the constant ULLONG_MAX must be defined in
+ * <limits.h>. Define it here for pre-C99 compilers.
+ */
+#ifndef ULLONG_MAX
+#define ULLONG_MAX UINT64_C(0xffffffffffffffff)
+#endif
+
+uint64_t
+strtoull(const char *nptr, char **endptr, int base)
+{
+ uint64_t result = 0;
+ const char *p;
+ const char *first_nonspace;
+ const char *digits_start;
+ int sign = 1;
+ int out_of_range = 0;
+
+ if (base != 0 && (base < 2 || base > 36))
+ goto invalid_input;
+
+ p = nptr;
+
+ /*
+ * Process the initial, possibly empty, sequence of white-space characters.
+ */
+ while (isspace((unsigned char) (*p)))
+ p++;
+
+ first_nonspace = p;
+
+ /*
+ * Determine sign.
+ */
+ if (*p == '+')
+ p++;
+ else if (*p == '-') {
+ p++;
+ sign = -1;
+ }
+
+ if (base == 0) {
+ /*
+ * Determine base.
+ */
+ if (*p == '0') {
+ if ((p[1] == 'x' || p[1] == 'X')) {
+ if (isxdigit((unsigned char)(p[2]))) {
+ base = 16;
+ p += 2;
+ } else {
+ /*
+ * Special case: treat the string "0x" without any further
+ * hex digits as a decimal number.
+ */
+ base = 10;
+ }
+ } else {
+ base = 8;
+ p++;
+ }
+ } else {
+ base = 10;
+ }
+ } else if (base == 16) {
+ /*
+ * For base 16, skip the optional "0x" / "0X" prefix.
+ */
+ if (*p == '0' && (p[1] == 'x' || p[1] == 'X')
+ && isxdigit((unsigned char)(p[2]))) {
+ p += 2;
+ }
+ }
+
+ digits_start = p;
+
+ for (; *p; p++) {
+ int digit;
+ digit = ('0' <= *p && *p <= '9') ? *p - '0'
+ : ('a' <= *p && *p <= 'z') ? (*p - 'a' + 10)
+ : ('A' <= *p && *p <= 'Z') ? (*p - 'A' + 10) : 36;
+ if (digit < base) {
+ if (! out_of_range) {
+ if (result > ULLONG_MAX / base
+ || result * base > ULLONG_MAX - digit) {
+ out_of_range = 1;
+ }
+ result = result * base + digit;
+ }
+ } else
+ break;
+ }
+
+ if (p > first_nonspace && p == digits_start)
+ goto invalid_input;
+
+ if (p == first_nonspace)
+ p = nptr;
+
+ if (endptr)
+ *endptr = (char *) p;
+
+ if (out_of_range) {
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+
+ return sign > 0 ? result : -result;
+
+ invalid_input:
+ errno = EINVAL;
+ if (endptr)
+ *endptr = (char *) nptr;
+ return 0;
+}