As mentioned in the title, I'm looking for a way to convert a char* (coming from argv) to a uint16_t. The command line argument is a port number, and so, can't be > to 65535, nor negative.
Currently, I did this (compiling with -std=gnu99):
#include <stdbool.h>
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>
/*
* Converts a string to an unsigned int and stores the result in "res".
*/
bool str_to_uint(const char* str, unsigned long int* res) {
if (str[0] == '-')
return false;
char* first_wrong_character;
uintmax_t result = strtoumax(str, &first_wrong_character, 10);
if ((result == UINTMAX_MAX) && (errno == ERANGE))
return false; // Overflow)
if ((*str != '\0') && (*first_wrong_character != '\0'))
return false; // Not everything has been converted
if ((result == 0) && (str == first_wrong_character))
return false; // Nothing to convert
*res = result;
return true;
}
/*
* Converts a string to an uint16_t and stores the result in "res".
*/
bool str_to_uint16(const char* str, uint16_t* res) {
unsigned long uint;
if (!str_to_uint(str, &uint))
return false;
if (uint > UINT16_MAX)
return false;
*res = (uint16_t)uint;
return true;
}
I'm not sure it's the best way to do it, so if you could tell me what is the good way ?
There's no need to use strtoumax
. I'd go with the more portable strtol
. The error handling can also be simplified to something like this:
bool str_to_uint16(const char *str, uint16_t *res) {
char *end;
errno = 0;
long val = strtol(str, &end, 10);
if (errno || end == str || *end != '\0' || val < 0 || val >= 0x10000) {
return false;
}
*res = (uint16_t)val;
return true;
}
You can use strtol(3)
that is able to return an error in case of integer overflow (ERANGE
), and simply check if the parsed integer is not too large vs. the uint16_t
capacity:
#include <stdint.h> /* fixed-width integer types */
#include <stdlib.h> /* strtol */
#include <stdbool.h>
#include <errno.h>
static bool
str_to_uint16(const char *str, uint16_t *res)
{
long int val = strtol(str, NULL, 10);
if (errno == ERANGE || val > UINT16_MAX || val < 0)
return false;
*res = (uint16_t) val;
return true;
}
EDIT
Since the question concerns C99, and if I include a better error management (thanks to @nwellnhof and @chux) I would say the version below should be the right candidate:
#include <inttypes.h> /* strtoimax */
static bool
str_to_uint16(const char *str, uint16_t *res)
{
char *end;
errno = 0;
intmax_t val = strtoimax(str, &end, 10);
if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == str || *end != '\0')
return false;
*res = (uint16_t) val;
return true;
}
It succeeds with:
1981
65535
(UINT16_MAX
)It returns a conversion error (as expected) with:
65536
(UINT16_MAX+1
)a1981
1981a
abcd
9223372036854775808
(INTMAX_MAX+1
: in such a case ERANGE
occurs)-9223372036854775809
(INTMAX_MIN-1
: in such a case ERANGE
occurs)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With