Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d201be1
src/strntoul-detail.hpp: Initial revision.
gerickson Apr 6, 2026
cb864b7
src/strntoul-detail.cpp: Initial revision.
gerickson Apr 6, 2026
01d5a45
src/strntoul-detail-unsigned-template.hpp: Initial revision.
gerickson Apr 6, 2026
6b579aa
src/strntoul-detail-signed-template.hpp: Initial revision.
gerickson Apr 6, 2026
f1f2e71
src/strntoll.h: Initial revision.
gerickson Apr 6, 2026
6f4b628
src/strntoll.cpp: Initial revision.
gerickson Apr 6, 2026
21213f6
src/strntoull.h: Initial revision.
gerickson Apr 6, 2026
202c4eb
src/strntoull.cpp: Initial revision.
gerickson Apr 6, 2026
abc3504
src/strntoimax.h: Initial revision.
gerickson Apr 6, 2026
a22d0b8
src/strntoimax.cpp: Initial revision.
gerickson Apr 6, 2026
57a3074
src/strntoumax.h: Initial revision.
gerickson Apr 6, 2026
03f0e1e
src/strntoumax.cpp: Initial revision.
gerickson Apr 6, 2026
52a3465
src/strntoq.h: Initial revision.
gerickson Apr 6, 2026
2759168
src/strntoq.cpp: Initial revision.
gerickson Apr 6, 2026
f8c58eb
src/strntouq.h: Initial revision.
gerickson Apr 6, 2026
7c41db0
src/strntouq.cpp: Initial revision.
gerickson Apr 6, 2026
51ca634
src/tests/Test_strntoll.cpp: Initial revision.
gerickson Apr 6, 2026
ab69d69
src/tests/Test_strntoull.cpp: Initial revision.
gerickson Apr 6, 2026
216017a
src/tests/Test_strntoimax.cpp: Initial revision.
gerickson Apr 6, 2026
b53e2a2
src/tests/Test_strntoumax.cpp: Initial revision.
gerickson Apr 6, 2026
512385b
src/tests/Test_strntoq.cpp: Initial revision.
gerickson Apr 6, 2026
6727cb2
src/tests/Test_strntouq.cpp: Initial revision.
gerickson Apr 6, 2026
7a5f0e8
src/Makefile.am: Added 'strntoul-detail{,-signed-template,-unsigned-t…
gerickson Apr 6, 2026
c0547f7
src/Makefile.am: Added 'strnto{ll,ull,imax,umax,q,uq}.h' to 'include_…
gerickson Apr 6, 2026
a93a15b
src/Makefile.am: Added '-D_BSD_SOURCE' to 'libstrntoul_la_CPPFLAGS'.
gerickson Apr 6, 2026
c3622f6
src/Makefile.am: Added 'strnto{ll,ull,imax,umax,q,uq,ul-detail}.cpp' …
gerickson Apr 6, 2026
6d8bc7c
src/tests/Makefile.am: Added test targets, variables, and sources for…
gerickson Apr 6, 2026
c30bf64
src/tests/Test_strntol.cpp: Removed 'fprintf' development/debug state…
gerickson Apr 6, 2026
253fc6d
src/tests/Test_strntol.cpp: Use 'TestType' typedef to neutralize the …
gerickson Apr 6, 2026
0b0646b
src/tests/Test_strntoul.cpp: Use 'TestType' typedef to neutralize the…
gerickson Apr 6, 2026
340ea39
src/strntol.cpp: Leverage 'Detail::StringExtentToSignedType' template.
gerickson Apr 6, 2026
85331fd
src/strntoul.cpp: Leverage 'Detail::StringExtentToUnsignedType' templ…
gerickson Apr 6, 2026
5a3afcb
CHANGES.md: Update change log / release notes for 2.0.
gerickson Apr 6, 2026
7a3c50a
.default-version: Change default version from 1.0.1 to 2.0.
gerickson Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .default-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.1
2.0
21 changes: 17 additions & 4 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# Changes and What's New in strntoul

#### 2.0.0 (2026-04-06)

* Added six new interfaces:

1. `strnto[u]ll`
2. `strnto[iu]max`
3. `strnto[u]q`

* Fixed a bug in signed conversion where values representable in the
unsigned type but exceeding the signed range (for example,
0x8000000000000000 for a 64-bit signed type) were silently cast rather
than clamped with `ERANGE`. Historically, this affected `strntol` for
inputs between `LONG_MAX` + 1 and `ULONG_MAX` that did not trigger unsigned
overflow.

#### 1.0.1 (2026-04-04)

* Addresses an issue with single digit conversion of zero ('0') that should
succeed with both `strntol` and `strtoul`.

#### 1.0.0 (2024-02-28)

* Initial public release independent of [OpenHLX]
(https://github.com/gerickson/openhlx/), from which [these sources came]
(https://github.com/gerickson/openhlx/tree/main/src/lib/utilities).

* Initial public release independent of [OpenHLX](https://github.com/gerickson/openhlx/),
from which [these sources came](https://github.com/gerickson/openhlx/tree/main/src/lib/utilities).
19 changes: 18 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2024 Grant Erickson. All Rights Reserved.
# Copyright 2024-2026 Grant Erickson. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,26 +30,43 @@ SUBDIRS = \
$(NULL)

noinst_HEADERS = \
strntoul-detail-signed-template.hpp \
strntoul-detail-unsigned-template.hpp \
strntoul-detail.hpp \
$(NULL)

# Public library headers to distribute and install.

include_HEADERS = \
strntoimax.h \
strntol.h \
strntoll.h \
strntoq.h \
strntoul.h \
strntoull.h \
strntoumax.h \
strntouq.h \
$(NULL)

libstrntoul_la_LDFLAGS = \
-version_info $(LIBSTRNTOUL_VERSION_INFO) \
$(NULL)

libstrntoul_la_CPPFLAGS = \
-D_BSD_SOURCE \
-I$(top_srcdir)/src/include \
$(NULL)

libstrntoul_la_SOURCES = \
strntoimax.cpp \
strntol.cpp \
strntoll.cpp \
strntoq.cpp \
strntoul-detail.cpp \
strntoul.cpp \
strntoull.cpp \
strntoumax.cpp \
strntouq.cpp \
$(NULL)

install-headers: install-includeHEADERS
Expand Down
95 changes: 95 additions & 0 deletions src/strntoimax.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2021-2026 Grant Erickson
* All rights reserved.
*
* 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.
*
*/

/**
* @file
* This file implements a "missing" C99 interface for strntoimax
* which adds a length parameter to the standard strtoimax.
*
*/


#include "strntoimax.h"

#include "strntoll.h"


/**
* @brief
* Convert a string to an intmax_t integer.
*
* This function is similar to the C99 standard strtoimax, except
* that it will use at most @a aLength bytes from @a aString; and @a
* aString does @b not need to be null-terminated if it contains @a
* aLength or more bytes.
*
* This attempts to convert the initial part of the string in @a
* aString to an intmax_t integer value according to @a aBase, which
* must be between 2 and 36 inclusive, or be the special value 0
* which implies that the base should be automatically-detected based
* on the content of @a aString.
*
* @a aString may begin with an arbitrary amount of white space (as
* determined by isspace(3)) followed by a single optional '+' or '-'
* sign. If base is zero (0) or 16, the string may then include a
* "0x" or a "0X" prefix, and the number will be read in base 16
* (hexadecimal); otherwise, a zero (0) base is taken as ten (10)
* (decimal) unless the next character is '0', in which case it is
* taken as eight (8) (octal).
*
* The conversion continues up to @a aLength, a terminating null, or
* until an invalid character is encountered for the base, whichever
* is less.
*
* If @a aEnd is not null, this stores the address of the first
* invalid character in *@a aEnd. If there were no digits at all,
* this stores the original value of @a aString in *@a aEnd (and
* returns 0). In particular, if *@a aEnd is not null but **@a aEnd
* is '\0' or if @a aEnd - @a aString is equal to @a aLength on
* return, the entire string is valid and was fully converted.
*
* On error, @a errno may be set as follows:
*
* - EINVAL @a aBase was an unsupported value.
* - ERANGE The resulting conversion was out of range.
*
* @param[in] aString A pointer to the string to convert.
* @param[in] aLength The maximum number of characters, in bytes,
* of @a aString to process.
* @param[out] aEnd A pointer to storage for the first invalid
* or the last valid character in @a aString.
* @param[in] aBase The base to use to interpret @a aString for
* the conversion in the range 2 to 36,
* inclusive.
*
* @returns
* Either the result of the conversion or, if there was a leading
* minus sign, the negation of the result of the conversion
* represented as an unsigned value, unless the original
* (nonnegated) value would overflow; in the latter case, this
* returns INTMAX_MAX and sets @a errno to ERANGE.
*
* @sa strtoimax
* @sa strntoumax
*
*/
intmax_t
strntoimax(const char *aString, size_t aLength, char **aEnd, int aBase)
{
return (static_cast<intmax_t>(strntoll(aString, aLength, aEnd, aBase)));
}
43 changes: 43 additions & 0 deletions src/strntoimax.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2026 Grant Erickson
* All rights reserved.
*
* 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.
*
*/

/**
* @file
* This file defines a "missing" C11 / POSIX.1-2008 interface for
* strntoimax which adds a length parameter to the standard
* strtoimax.
*
*/

#ifndef STRNTOIMAX_H
#define STRNTOIMAX_H

#include <inttypes.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

extern intmax_t strntoimax(const char *aString, size_t aLength, char **aEnd, int aBase);

#ifdef __cplusplus
}
#endif

#endif /* STRNTOIMAX_H */
88 changes: 8 additions & 80 deletions src/strntol.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Grant Erickson
* Copyright (c) 2021-2026 Grant Erickson
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,89 +23,13 @@
*
*/


#include "strntol.h"

#include <ctype.h>
#include <errno.h>
#include <limits.h>

#include "strntoul.h"

static long
_strntol(const char *aString, const size_t &aLength, char **aEnd, const int &aBase)
{
const char * p = aString;
bool calledStrtoul = false;
bool isNegative = false;
signed long lRetval = 0;


if (aLength == 0)
{
goto done;
}

// Skip any leading space and determine the sign, if any.

while ((p < (aString + aLength)) && isspace(*p))
{
p++;
}

if (p < (aString + aLength))
{
unsigned long lResult;

if (*p == '-')
{
isNegative = true;
#include "strntoul-detail-signed-template.hpp"

p++;

lResult = strntoul(p,
aLength - static_cast<size_t>(p - aString),
aEnd,
aBase);
lRetval = static_cast<long>(-lResult);
}
else
{
if (*p == '+')
{
p++;
}

lResult = strntoul(p,
aLength - static_cast<size_t>(p - aString),
aEnd,
aBase);
lRetval = static_cast<long>(lResult);
}

calledStrtoul = true;
}

done:
// If no digits were converted, then the standard says that aEnd,
// if non-null, must be equal to aString.

if ((lRetval == 0) && ((aEnd != nullptr) && (!calledStrtoul || (p == *aEnd))))
{
*aEnd = const_cast<char *>(aString);
}

// If the correct value is outside the range of representable
// values, the standards says LONG_MIN or LONG_MAX shall be
// returned (according to the sign of the value), and errno set to
// ERANGE (which was set from strtoul).

if (errno == ERANGE)
{
lRetval = (isNegative) ? LONG_MIN : LONG_MAX;
}

return (lRetval);
}

/**
* @brief
Expand Down Expand Up @@ -147,5 +71,9 @@ _strntol(const char *aString, const size_t &aLength, char **aEnd, const int &aBa
long
strntol(const char *aString, size_t aLength, char **aEnd, int aBase)
{
return (_strntol(aString, aLength, aEnd, aBase));
return (Detail::StringExtentToSignedType<long,
unsigned long,
LONG_MIN,
LONG_MAX,
ULONG_MAX>(aString, aLength, aEnd, aBase));
}
Loading