Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/strtol.c

    r55092672 r1c9bf292  
    4444#include <stdbool.h>
    4545#include <stdlib.h>
    46 
    47 // TODO: unit tests
     46#include <str.h>
     47
     48// FIXME: The original HelenOS functions return EOVERFLOW instead
     49//        of ERANGE. It's a pointless distinction from standard functions,
     50//        so we should change that. Beware the callers though.
     51
     52// TODO: more unit tests
    4853
    4954static inline int _digit_value(int c)
     
    6974}
    7075
     76static inline int _prefixbase(const char *restrict *nptrptr, bool nonstd)
     77{
     78        const char *nptr = *nptrptr;
     79
     80        if (nptr[0] != '0')
     81                return 10;
     82
     83        if (nptr[1] == 'x' || nptr[1] == 'X') {
     84                if (_digit_value(nptr[2]) < 16) {
     85                        *nptrptr += 2;
     86                        return 16;
     87                }
     88        }
     89
     90        if (nonstd) {
     91                switch (nptr[1]) {
     92                case 'b':
     93                case 'B':
     94                        if (_digit_value(nptr[2]) < 2) {
     95                                *nptrptr += 2;
     96                                return 2;
     97                        }
     98                        break;
     99                case 'o':
     100                case 'O':
     101                        if (_digit_value(nptr[2]) < 8) {
     102                                *nptrptr += 2;
     103                                return 8;
     104                        }
     105                        break;
     106                case 'd':
     107                case 'D':
     108                case 't':
     109                case 'T':
     110                        if (_digit_value(nptr[2]) < 10) {
     111                                *nptrptr += 2;
     112                                return 10;
     113                        }
     114                        break;
     115                }
     116        }
     117
     118        return 8;
     119}
     120
    71121static inline uintmax_t _strtoumax(
    72122    const char *restrict nptr, char **restrict endptr, int base,
    73     bool *restrict sgn)
     123    bool *restrict sgn, errno_t *err, bool nonstd)
    74124{
    75125        assert(nptr != NULL);
    76126        assert(sgn != NULL);
     127
     128        const char *first = nptr;
    77129
    78130        /* Skip leading whitespace. */
     
    96148        /* Figure out the base. */
    97149
    98         if (base == 0) {
    99                 if (*nptr == '0') {
    100                         if (tolower(nptr[1]) == 'x') {
    101                                 /* 0x... is hex. */
    102                                 base = 16;
    103                                 nptr += 2;
    104                         } else {
    105                                 /* 0... is octal. */
    106                                 base = 8;
    107                         }
    108                 } else {
    109                         /* Anything else is decimal by default. */
    110                         base = 10;
    111                 }
    112         } else if (base == 16) {
    113                 /* Allow hex number to be prefixed with "0x". */
    114                 if (nptr[0] == '0' && tolower(nptr[1]) == 'x') {
     150        if (base == 0)
     151                base = _prefixbase(&nptr, nonstd);
     152
     153        if (base == 16 && !nonstd) {
     154                /*
     155                 * Standard strto* functions allow hexadecimal prefix to be
     156                 * present when base is explicitly set to 16.
     157                 * Our nonstandard str_* functions don't allow it.
     158                 * I don't know if that is intended, just matching the original
     159                 * functionality here.
     160                 */
     161
     162                if (nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X') &&
     163                    _digit_value(nptr[2]) < base)
    115164                        nptr += 2;
    116                 }
    117         } else if (base < 0 || base == 1 || base > 36) {
    118                 errno = EINVAL;
     165        }
     166
     167        if (base < 2 || base > 36) {
     168                *err = EINVAL;
    119169                return 0;
    120170        }
    121171
    122         /* Read the value. */
     172        /* Must be at least one digit. */
     173
     174        if (_digit_value(*nptr) >= base) {
     175                /* No digits on input. */
     176                if (endptr != NULL)
     177                        *endptr = (char *) first;
     178                return 0;
     179        }
     180
     181        /* Read the value.  */
    123182
    124183        uintmax_t result = 0;
     
    127186
    128187        while (digit = _digit_value(*nptr), digit < base) {
    129 
    130188                if (result > max ||
    131189                    __builtin_add_overflow(result * base, digit, &result)) {
    132190
    133                         errno = ERANGE;
     191                        *err = nonstd ? EOVERFLOW : ERANGE;
    134192                        result = UINTMAX_MAX;
    135193                        break;
     
    145203                 * Move the pointer to the end of the number,
    146204                 * in case it isn't there already.
     205                 * This can happen when the number has legal formatting,
     206                 * but is out of range of the target type.
    147207                 */
    148208                while (_digit_value(*nptr) < base) {
     
    157217
    158218static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
    159     intmax_t min, intmax_t max)
     219    intmax_t min, intmax_t max, errno_t *err, bool nonstd)
    160220{
    161221        bool sgn = false;
    162         uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
     222        uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
    163223
    164224        if (number > (uintmax_t) max) {
     
    167227                }
    168228
    169                 errno = ERANGE;
     229                *err = nonstd ? EOVERFLOW : ERANGE;
    170230                return (sgn ? min : max);
    171231        }
     
    175235
    176236static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
    177     uintmax_t max)
     237    uintmax_t max, errno_t *err, bool nonstd)
    178238{
    179239        bool sgn = false;
    180         uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
    181 
    182         if (sgn) {
    183                 if (number == 0) {
    184                         return 0;
    185                 } else {
    186                         errno = ERANGE;
    187                         return max;
    188                 }
     240        uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
     241
     242        if (nonstd && sgn) {
     243                /* Do not allow negative values */
     244                *err = EINVAL;
     245                return 0;
    189246        }
    190247
    191248        if (number > max) {
    192                 errno = ERANGE;
     249                *err = nonstd ? EOVERFLOW : ERANGE;
    193250                return max;
    194251        }
    195252
    196         return number;
     253        return (sgn ? -number : number);
    197254}
    198255
     
    212269long strtol(const char *nptr, char **endptr, int base)
    213270{
    214         return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX);
     271        return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX, &errno, false);
    215272}
    216273
     
    230287unsigned long strtoul(const char *nptr, char **endptr, int base)
    231288{
    232         return _strtounsigned(nptr, endptr, base, ULONG_MAX);
     289        return _strtounsigned(nptr, endptr, base, ULONG_MAX, &errno, false);
    233290}
    234291
    235292long long strtoll(const char *nptr, char **endptr, int base)
    236293{
    237         return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX);
     294        return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX, &errno, false);
    238295}
    239296
    240297unsigned long long strtoull(const char *nptr, char **endptr, int base)
    241298{
    242         return _strtounsigned(nptr, endptr, base, ULLONG_MAX);
     299        return _strtounsigned(nptr, endptr, base, ULLONG_MAX, &errno, false);
    243300}
    244301
    245302intmax_t strtoimax(const char *nptr, char **endptr, int base)
    246303{
    247         return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX);
     304        return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX, &errno, false);
    248305}
    249306
    250307uintmax_t strtoumax(const char *nptr, char **endptr, int base)
    251308{
    252         return _strtounsigned(nptr, endptr, base, UINTMAX_MAX);
     309        return _strtounsigned(nptr, endptr, base, UINTMAX_MAX, &errno, false);
    253310}
    254311
     
    268325}
    269326
     327/** Convert string to uint8_t.
     328 *
     329 * @param nptr   Pointer to string.
     330 * @param endptr If not NULL, pointer to the first invalid character
     331 *               is stored here.
     332 * @param base   Zero or number between 2 and 36 inclusive.
     333 * @param strict Do not allow any trailing characters.
     334 * @param result Result of the conversion.
     335 *
     336 * @return EOK if conversion was successful.
     337 *
     338 */
     339errno_t str_uint8_t(const char *nptr, const char **endptr, unsigned int base,
     340    bool strict, uint8_t *result)
     341{
     342        assert(result != NULL);
     343
     344        errno_t rc = EOK;
     345        char *lendptr = (char *) nptr;
     346
     347        uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT8_MAX, &rc, true);
     348
     349        if (endptr)
     350                *endptr = lendptr;
     351
     352        if (rc != EOK)
     353                return rc;
     354
     355        if (strict && *lendptr != '\0')
     356                return EINVAL;
     357
     358        *result = r;
     359        return EOK;
     360}
     361
     362/** Convert string to uint16_t.
     363 *
     364 * @param nptr   Pointer to string.
     365 * @param endptr If not NULL, pointer to the first invalid character
     366 *               is stored here.
     367 * @param base   Zero or number between 2 and 36 inclusive.
     368 * @param strict Do not allow any trailing characters.
     369 * @param result Result of the conversion.
     370 *
     371 * @return EOK if conversion was successful.
     372 *
     373 */
     374errno_t str_uint16_t(const char *nptr, const char **endptr, unsigned int base,
     375    bool strict, uint16_t *result)
     376{
     377        assert(result != NULL);
     378
     379        errno_t rc = EOK;
     380        char *lendptr = (char *) nptr;
     381
     382        uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT16_MAX, &rc, true);
     383
     384        if (endptr)
     385                *endptr = lendptr;
     386
     387        if (rc != EOK)
     388                return rc;
     389
     390        if (strict && *lendptr != '\0')
     391                return EINVAL;
     392
     393        *result = r;
     394        return EOK;
     395}
     396
     397/** Convert string to uint32_t.
     398 *
     399 * @param nptr   Pointer to string.
     400 * @param endptr If not NULL, pointer to the first invalid character
     401 *               is stored here.
     402 * @param base   Zero or number between 2 and 36 inclusive.
     403 * @param strict Do not allow any trailing characters.
     404 * @param result Result of the conversion.
     405 *
     406 * @return EOK if conversion was successful.
     407 *
     408 */
     409errno_t str_uint32_t(const char *nptr, const char **endptr, unsigned int base,
     410    bool strict, uint32_t *result)
     411{
     412        assert(result != NULL);
     413
     414        errno_t rc = EOK;
     415        char *lendptr = (char *) nptr;
     416
     417        uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT32_MAX, &rc, true);
     418
     419        if (endptr)
     420                *endptr = lendptr;
     421
     422        if (rc != EOK)
     423                return rc;
     424
     425        if (strict && *lendptr != '\0')
     426                return EINVAL;
     427
     428        *result = r;
     429        return EOK;
     430}
     431
     432/** Convert string to uint64_t.
     433 *
     434 * @param nptr   Pointer to string.
     435 * @param endptr If not NULL, pointer to the first invalid character
     436 *               is stored here.
     437 * @param base   Zero or number between 2 and 36 inclusive.
     438 * @param strict Do not allow any trailing characters.
     439 * @param result Result of the conversion.
     440 *
     441 * @return EOK if conversion was successful.
     442 *
     443 */
     444errno_t str_uint64_t(const char *nptr, const char **endptr, unsigned int base,
     445    bool strict, uint64_t *result)
     446{
     447        assert(result != NULL);
     448
     449        errno_t rc = EOK;
     450        char *lendptr = (char *) nptr;
     451
     452        uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT64_MAX, &rc, true);
     453
     454        if (endptr)
     455                *endptr = lendptr;
     456
     457        if (rc != EOK)
     458                return rc;
     459
     460        if (strict && *lendptr != '\0')
     461                return EINVAL;
     462
     463        *result = r;
     464        return EOK;
     465}
     466
     467/** Convert string to int64_t.
     468 *
     469 * @param nptr   Pointer to string.
     470 * @param endptr If not NULL, pointer to the first invalid character
     471 *               is stored here.
     472 * @param base   Zero or number between 2 and 36 inclusive.
     473 * @param strict Do not allow any trailing characters.
     474 * @param result Result of the conversion.
     475 *
     476 * @return EOK if conversion was successful.
     477 *
     478 */
     479errno_t str_int64_t(const char *nptr, const char **endptr, unsigned int base,
     480    bool strict, int64_t *result)
     481{
     482        assert(result != NULL);
     483
     484        errno_t rc = EOK;
     485        char *lendptr = (char *) nptr;
     486
     487        intmax_t r = _strtosigned(nptr, &lendptr, base, INT64_MIN, INT64_MAX, &rc, true);
     488
     489        if (endptr)
     490                *endptr = lendptr;
     491
     492        if (rc != EOK)
     493                return rc;
     494
     495        if (strict && *lendptr != '\0')
     496                return EINVAL;
     497
     498        *result = r;
     499        return EOK;
     500}
     501
     502/** Convert string to size_t.
     503 *
     504 * @param nptr   Pointer to string.
     505 * @param endptr If not NULL, pointer to the first invalid character
     506 *               is stored here.
     507 * @param base   Zero or number between 2 and 36 inclusive.
     508 * @param strict Do not allow any trailing characters.
     509 * @param result Result of the conversion.
     510 *
     511 * @return EOK if conversion was successful.
     512 *
     513 */
     514errno_t str_size_t(const char *nptr, const char **endptr, unsigned int base,
     515    bool strict, size_t *result)
     516{
     517        assert(result != NULL);
     518
     519        errno_t rc = EOK;
     520        char *lendptr = (char *) nptr;
     521
     522        uintmax_t r = _strtounsigned(nptr, &lendptr, base, SIZE_MAX, &rc, true);
     523
     524        if (endptr)
     525                *endptr = lendptr;
     526
     527        if (rc != EOK)
     528                return rc;
     529
     530        if (strict && *lendptr != '\0')
     531                return EINVAL;
     532
     533        *result = r;
     534        return EOK;
     535}
     536
    270537/** @}
    271538 */
Note: See TracChangeset for help on using the changeset viewer.