net-snmp 5.7
strtoull.c
00001 /*
00002  * An implementation of strtoull() for compilers that do not have this
00003  * function, e.g. MSVC.
00004  * See also http://www.opengroup.org/onlinepubs/000095399/functions/strtoul.html
00005  * for more information about strtoull().
00006  */
00007 
00008 
00009 /*
00010  * For MSVC, disable the warning "unary minus operator applied to unsigned
00011  * type, result still unsigned"
00012  */
00013 #ifdef _MSC_VER
00014 #pragma warning (disable: 4146)
00015 #endif
00016 
00017 
00018 #define __STDC_CONSTANT_MACROS  /* Enable UINT64_C in <stdint.h> */
00019 #define __STDC_FORMAT_MACROS    /* Enable PRIu64 in <inttypes.h> */
00020 
00021 #include <net-snmp/net-snmp-config.h>
00022 
00023 #include <errno.h>
00024 #include <ctype.h>
00025 #include <limits.h>
00026 #ifdef HAVE_INTTYPES_H
00027 #include <inttypes.h>
00028 #endif
00029 
00030 #include <net-snmp/types.h>
00031 #include <net-snmp/library/system.h>
00032 
00033 /*
00034  * UINT64_C: C99 macro for the suffix for uint64_t constants. 
00035  */
00036 #ifndef UINT64_C
00037 #ifdef _MSC_VER
00038 #define UINT64_C(c) c##ui64
00039 #else
00040 #define UINT64_C(c) c##ULL
00041 #endif
00042 #endif
00043 
00044 /*
00045  * According to the C99 standard, the constant ULLONG_MAX must be defined in
00046  * <limits.h>. Define it here for pre-C99 compilers.
00047  */
00048 #ifndef ULLONG_MAX
00049 #define ULLONG_MAX UINT64_C(0xffffffffffffffff)
00050 #endif
00051 
00052 #ifdef STRTOULL_UNIT_TEST
00053 uint64_t
00054 my_strtoull(const char *nptr, char **endptr, int base)
00055 #else
00056 uint64_t
00057 strtoull(const char *nptr, char **endptr, int base)
00058 #endif
00059 {
00060     uint64_t        result = 0;
00061     const char     *p;
00062     const char     *first_nonspace;
00063     const char     *digits_start;
00064     int             sign = 1;
00065     int             out_of_range = 0;
00066 
00067     if (base != 0 && (base < 2 || base > 36))
00068         goto invalid_input;
00069 
00070     p = nptr;
00071 
00072     /*
00073      * Process the initial, possibly empty, sequence of white-space characters.
00074      */
00075     while (isspace((unsigned char) (*p)))
00076         p++;
00077 
00078     first_nonspace = p;
00079 
00080     /*
00081      * Determine sign.
00082      */
00083     if (*p == '+')
00084         p++;
00085     else if (*p == '-') {
00086         p++;
00087         sign = -1;
00088     }
00089 
00090     if (base == 0) {
00091         /*
00092          * Determine base.
00093          */
00094         if (*p == '0') {
00095             if ((p[1] == 'x' || p[1] == 'X')) {
00096                 if (isxdigit((unsigned char)(p[2]))) {
00097                     base = 16;
00098                     p += 2;
00099                 } else {
00100                     /*
00101                      * Special case: treat the string "0x" without any further
00102                      * hex digits as a decimal number.
00103                      */
00104                     base = 10;
00105                 }
00106             } else {
00107                 base = 8;
00108                 p++;
00109             }
00110         } else {
00111             base = 10;
00112         }
00113     } else if (base == 16) {
00114         /*
00115          * For base 16, skip the optional "0x" / "0X" prefix.
00116          */
00117         if (*p == '0' && (p[1] == 'x' || p[1] == 'X')
00118             && isxdigit((unsigned char)(p[2]))) {
00119             p += 2;
00120         }
00121     }
00122 
00123     digits_start = p;
00124 
00125     for (; *p; p++) {
00126         int             digit;
00127         digit = ('0' <= *p && *p <= '9') ? *p - '0'
00128             : ('a' <= *p && *p <= 'z') ? (*p - 'a' + 10)
00129             : ('A' <= *p && *p <= 'Z') ? (*p - 'A' + 10) : 36;
00130         if (digit < base) {
00131             if (! out_of_range) {
00132                 if (result > ULLONG_MAX / base
00133                     || result * base > ULLONG_MAX - digit) {
00134                     out_of_range = 1;
00135                 }
00136                 result = result * base + digit;
00137             }
00138         } else
00139             break;
00140     }
00141 
00142     if (p > first_nonspace && p == digits_start)
00143         goto invalid_input;
00144 
00145     if (p == first_nonspace)
00146         p = nptr;
00147 
00148     if (endptr)
00149         *endptr = (char *) p;
00150 
00151     if (out_of_range) {
00152         errno = ERANGE;
00153         return ULLONG_MAX;
00154     }
00155 
00156     return sign > 0 ? result : -result;
00157 
00158   invalid_input:
00159     errno = EINVAL;
00160     if (endptr)
00161         *endptr = (char *) nptr;
00162     return 0;
00163 }
00164 
00165 #if defined(STRTOULL_UNIT_TEST)
00166 
00167 #include <stdio.h>
00168 #include <stdlib.h>
00169 
00170 #ifndef PRIu64
00171 #ifdef _MSC_VER
00172 #define PRIu64 "I64u"
00173 #else
00174 #define PRIu64 "llu"
00175 #endif
00176 #endif
00177 
00178 struct strtoull_testcase {
00179     /*
00180      * inputs 
00181      */
00182     const char     *nptr;
00183     int             base;
00184     /*
00185      * expected outputs 
00186      */
00187     int             expected_errno;
00188     int             expected_end;
00189     uint64_t        expected_result;
00190 };
00191 
00192 static const struct strtoull_testcase test_input[] = {
00193     {"0x0", 0, 0, 3, 0},
00194     {"1", 0, 0, 1, 1},
00195     {"0x1", 0, 0, 3, 1},
00196     {"  -0666", 0, 0, 7, -0666},
00197     {"  -0x666", 0, 0, 8, -0x666},
00198     {"18446744073709551614", 0, 0, 20, UINT64_C(0xfffffffffffffffe)},
00199     {"0xfffffffffffffffe", 0, 0, 18, UINT64_C(0xfffffffffffffffe)},
00200     {"18446744073709551615", 0, 0, 20, UINT64_C(0xffffffffffffffff)},
00201     {"0xffffffffffffffff", 0, 0, 18, UINT64_C(0xffffffffffffffff)},
00202     {"18446744073709551616", 0, ERANGE, 20, UINT64_C(0xffffffffffffffff)},
00203     {"0x10000000000000000", 0, ERANGE, 19, UINT64_C(0xffffffffffffffff)},
00204     {"ff", 16, 0, 2, 255},
00205     {"0xff", 16, 0, 4, 255},
00206     {" ", 0, 0, 0, 0},
00207     {"0x", 0, 0, 1, 0},
00208     {"0x", 8, 0, 1, 0},
00209     {"0x", 16, 0, 1, 0},
00210     {"zyyy", 0, 0, 0, 0},
00211     {"0xfffffffffffffffff", 0, ERANGE, 19, ULLONG_MAX},
00212     {"0xfffffffffffffffffz", 0, ERANGE, 19, ULLONG_MAX}
00213 };
00214 
00215 int
00216 main(void)
00217 {
00218     int             failure_count = 0;
00219     unsigned int    i;
00220 
00221     for (i = 0; i < sizeof(test_input) / sizeof(test_input[0]); i++) {
00222         const struct strtoull_testcase *const p = &test_input[i];
00223         char           *endptr;
00224         uint64_t        result;
00225 
00226         errno = 0;
00227         result = my_strtoull(p->nptr, &endptr, p->base);
00228         if (errno != p->expected_errno) {
00229             failure_count++;
00230             printf("test %d failed (input \"%s\"): expected errno %d"
00231                    ", got errno %d\n",
00232                    i, p->nptr, p->expected_errno, errno);
00233         }
00234         if (result != p->expected_result) {
00235             failure_count++;
00236             printf("test %d failed (input \"%s\"): expected result %" PRIu64
00237                    ", got result %" PRIu64 "\n",
00238                    i, p->nptr, p->expected_result, result);
00239         }
00240         if (endptr - p->nptr != p->expected_end) {
00241             failure_count++;
00242             printf("test %d failed (input \"%s\"): expected end %d,"
00243                    " got end %d\n",
00244                    i, p->nptr, p->expected_end, (int) (endptr - p->nptr));
00245         }
00246 
00247 #if HAVE_STRTOULL
00248         errno = 0;
00249         result = strtoull(p->nptr, &endptr, p->base);
00250         if (errno != p->expected_errno) {
00251             failure_count++;
00252             printf("test %d (input \"%s\"): expected errno %d"
00253                    ", libc strtoull() returned errno %d\n",
00254                    i, p->nptr, p->expected_errno, errno);
00255         }
00256         if (result != p->expected_result) {
00257             failure_count++;
00258             printf("test %d (input \"%s\"): expected result %" PRIu64
00259                    ", libc strtoull() returned result %" PRIu64 "\n",
00260                    i, p->nptr, p->expected_result, result);
00261         }
00262         if (endptr - p->nptr != p->expected_end) {
00263             failure_count++;
00264             printf("test %d (input \"%s\"): expected end %d,"
00265                    " libc strtoull() returned end %d\n",
00266                    i, p->nptr, p->expected_end, (int) (endptr - p->nptr));
00267         }
00268 #endif
00269     }
00270     if (failure_count == 0)
00271         printf("All %d tests passed.\n", i);
00272     return 0;
00273 }
00274 
00275 #endif /* defined(STRTOULL_UNIT_TEST) */
00276 
00277 /*
00278  * Local variables:
00279  * c-basic-offset: 4
00280  * indent-tabs-mode: nil
00281  * compile-command: "gcc -Wall -Werror -DSTRTOULL_UNIT_TEST=1 -I../include -g -o strtoull-unit-test strtoull.c && ./strtoull-unit-test"
00282  * End:
00283  */