diff --git a/include/stdlib.h b/include/stdlib.h index 3e54b5feb6de..305aea4b8672 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -1,413 +1,415 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _STDLIB_H_ #define _STDLIB_H_ #include #include #include __NULLABILITY_PRAGMA_PUSH #ifndef _SIZE_T_DECLARED typedef __size_t size_t; #define _SIZE_T_DECLARED #endif #ifndef __cplusplus #ifndef _WCHAR_T_DECLARED typedef ___wchar_t wchar_t; #define _WCHAR_T_DECLARED #endif #endif typedef struct { int quot; /* quotient */ int rem; /* remainder */ } div_t; typedef struct { long quot; long rem; } ldiv_t; #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 /* * I.e., INT_MAX; rand(3) returns a signed integer but must produce output in * the range [0, RAND_MAX], so half of the possible output range is unused. */ #define RAND_MAX 0x7fffffff #if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0 #include #endif __BEGIN_DECLS #ifdef _XLOCALE_H_ #include #endif extern int __mb_cur_max; extern int ___mb_cur_max(void); #define MB_CUR_MAX ((size_t)___mb_cur_max()) _Noreturn void abort(void) __noexcept; int abs(int) __pure2; int atexit(void (* _Nonnull)(void)) __noexcept; double atof(const char *); int atoi(const char *); long atol(const char *); void *bsearch(const void *, const void *, size_t, size_t, int (*)(const void * _Nonnull, const void *)); void *calloc(size_t, size_t) __malloc_like __result_use_check __alloc_size2(1, 2); div_t div(int, int) __pure2; _Noreturn void exit(int); void free(void *); char *getenv(const char *); #if __BSD_VISIBLE int getenv_r(const char *, char * _Nonnull, size_t); #endif long labs(long) __pure2; ldiv_t ldiv(long, long) __pure2; void *malloc(size_t) __malloc_like __result_use_check __alloc_size(1); int mblen(const char *, size_t); size_t mbstowcs(wchar_t * __restrict , const char * __restrict, size_t); int mbtowc(wchar_t * __restrict, const char * __restrict, size_t); void qsort(void *, size_t, size_t, int (* _Nonnull)(const void *, const void *)); int rand(void); void *realloc(void *, size_t) __result_use_check __alloc_size(2); void srand(unsigned); double strtod(const char * __restrict, char ** __restrict); float strtof(const char * __restrict, char ** __restrict); long strtol(const char * __restrict, char ** __restrict, int); long double strtold(const char * __restrict, char ** __restrict); unsigned long strtoul(const char * __restrict, char ** __restrict, int); int system(const char *); int wctomb(char *, wchar_t); size_t wcstombs(char * __restrict, const wchar_t * __restrict, size_t); /* * Functions added in C99 which we make conditionally available in the * BSD^C89 namespace if the compiler supports `long long'. * The #if test is more complicated than it ought to be because * __BSD_VISIBLE implies __ISO_C_VISIBLE == 1999 *even if* `long long' * is not supported in the compilation environment (which therefore means * that it can't really be ISO C99). * * (The only other extension made by C99 in this header is _Exit().) */ #if __ISO_C_VISIBLE >= 1999 || defined(__cplusplus) #ifdef __LONG_LONG_SUPPORTED /* LONGLONG */ typedef struct { long long quot; long long rem; } lldiv_t; /* LONGLONG */ long long atoll(const char *); /* LONGLONG */ long long llabs(long long) __pure2; /* LONGLONG */ lldiv_t lldiv(long long, long long) __pure2; /* LONGLONG */ long long strtoll(const char * __restrict, char ** __restrict, int); /* LONGLONG */ unsigned long long strtoull(const char * __restrict, char ** __restrict, int); #endif /* __LONG_LONG_SUPPORTED */ _Noreturn void _Exit(int) __noexcept; #endif /* __ISO_C_VISIBLE >= 1999 */ /* * If we're in a mode greater than C99, expose C11 functions. */ #if __ISO_C_VISIBLE >= 2011 || __cplusplus >= 201103L void * aligned_alloc(size_t, size_t) __malloc_like __alloc_align(1) __alloc_size(2); int at_quick_exit(void (*)(void)) __noexcept; _Noreturn void quick_exit(int) /* __noexcept -- not ready ABI issues? */; #endif /* __ISO_C_VISIBLE >= 2011 */ /* * C23 extensions */ #if __ISO_C_VISIBLE >= 2023 size_t memalignment(const void *) __pure2; #endif /* __ISO_C_VISIBLE >= 2023 */ /* * Extensions made by POSIX relative to C. */ #if __POSIX_VISIBLE >= 199506 || __XSI_VISIBLE char *realpath(const char * __restrict, char * __restrict); #endif #if __POSIX_VISIBLE >= 199506 int rand_r(unsigned *); /* (TSF) */ #endif #if __POSIX_VISIBLE >= 200112 int posix_memalign(void **, size_t, size_t); /* (ADV) */ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif #if __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE int getsubopt(char **, char *const *, char **); #ifndef _MKDTEMP_DECLARED char *mkdtemp(char *); #define _MKDTEMP_DECLARED #endif #ifndef _MKSTEMP_DECLARED int mkstemp(char *); #define _MKSTEMP_DECLARED #endif #endif /* __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE */ /* * The only changes to the XSI namespace in revision 6 were the deletion * of the ttyslot() and valloc() functions, which FreeBSD never declared * in this header. For revision 7, ecvt(), fcvt(), and gcvt(), which * FreeBSD also does not have, and mktemp(), are to be deleted. */ #if __XSI_VISIBLE /* XXX XSI requires pollution from here. We'd rather not. */ long a64l(const char *); double drand48(void); /* char *ecvt(double, int, int * __restrict, int * __restrict); */ double erand48(unsigned short[3]); /* char *fcvt(double, int, int * __restrict, int * __restrict); */ /* char *gcvt(double, int, int * __restrict, int * __restrict); */ char *initstate(unsigned int, char *, size_t); long jrand48(unsigned short[3]); char *l64a(long); void lcong48(unsigned short[7]); long lrand48(void); #if !defined(_MKTEMP_DECLARED) && (__BSD_VISIBLE || __XSI_VISIBLE <= 600) char *mktemp(char *); #define _MKTEMP_DECLARED #endif long mrand48(void); long nrand48(unsigned short[3]); int putenv(char *); long random(void); unsigned short *seed48(unsigned short[3]); char *setstate(/* const */ char *); void srand48(long); void srandom(unsigned int); #endif /* __XSI_VISIBLE */ #if __XSI_VISIBLE int grantpt(int); int posix_openpt(int); char *ptsname(int); int unlockpt(int); #endif /* __XSI_VISIBLE */ #if __BSD_VISIBLE /* ptsname_r will be included in POSIX issue 8 */ int ptsname_r(int, char *, size_t); #endif #if __BSD_VISIBLE extern const char *malloc_conf; extern void (*malloc_message)(void *, const char *); /* * The alloca() function can't be implemented in C, and on some * platforms it can't be implemented at all as a callable function. * The GNU C compiler provides a built-in alloca() which we can use. * On platforms where alloca() is not in libc, programs which use it * will fail to link when compiled with non-GNU compilers. */ #if __GNUC__ >= 2 #undef alloca /* some GNU bits try to get cute and define this on their own */ #define alloca(sz) __builtin_alloca(sz) #endif void abort2(const char *, int, void **) __dead2; __uint32_t arc4random(void); void arc4random_buf(void *, size_t); __uint32_t arc4random_uniform(__uint32_t); #ifdef __BLOCKS__ int atexit_b(void (^ _Nonnull)(void)); void *bsearch_b(const void *, const void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); #endif char *getbsize(int *, long *); /* getcap(3) functions */ char *cgetcap(char *, const char *, int); int cgetclose(void); int cgetent(char **, char **, const char *); int cgetfirst(char **, char **); int cgetmatch(const char *, const char *); int cgetnext(char **, char **); int cgetnum(char *, const char *, long *); int cgetset(const char *); int cgetstr(char *, const char *, char **); int cgetustr(char *, const char *, char **); int clearenv(void); int daemon(int, int); int daemonfd(int, int); char *devname(__dev_t, __mode_t); char *devname_r(__dev_t, __mode_t, char *, int); char *fdevname(int); char *fdevname_r(int, char *, int); int getloadavg(double [], int); const char * getprogname(void); int heapsort(void *, size_t, size_t, int (* _Nonnull)(const void *, const void *)); #ifdef __BLOCKS__ int heapsort_b(void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); void qsort_b(void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); #endif int l64a_r(long, char *, int); int mergesort(void *, size_t, size_t, int (*)(const void *, const void *)); #ifdef __BLOCKS__ int mergesort_b(void *, size_t, size_t, int (^)(const void *, const void *)); #endif int mkostemp(char *, int); int mkostemps(char *, int, int); int mkostempsat(int, char *, int, int); void qsort_r(void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); int radixsort(const unsigned char **, int, const unsigned char *, unsigned); void *reallocarray(void *, size_t, size_t) __result_use_check __alloc_size2(2, 3); void *recallocarray(void *, size_t, size_t, size_t) __result_use_check __alloc_size2(3, 4); void *reallocf(void *, size_t) __result_use_check __alloc_size(2); int rpmatch(const char *); char *secure_getenv(const char *); void setprogname(const char *); int sradixsort(const unsigned char **, int, const unsigned char *, unsigned); void srandomdev(void); long long strtonum(const char *, long long, long long, const char **); +long long + strtonumx(const char *, long long, long long, const char **, int); /* Deprecated interfaces, to be removed. */ __int64_t strtoq(const char *, char **, int); __uint64_t strtouq(const char *, char **, int); /* * In FreeBSD 14, the prototype of qsort_r() was modified to comply with * POSIX. The standardized qsort_r()'s order of last two parameters was * changed, and the comparator function is now taking thunk as its last * parameter, and both are different from the ones expected by the historical * FreeBSD qsort_r() interface. * * Apply a workaround where we explicitly link against the historical interface, * qsort_r@FBSD_1.0, in case when qsort_r() is called with the last parameter * with a function pointer that exactly matches the historical FreeBSD qsort_r() * comparator signature, so applications written for the historical interface * can continue to work without modification. Toolchains that don't support * symbol versioning don't define __sym_compat, so only provide this symbol in * supported environments. */ #ifdef __sym_compat #if defined(__generic) || defined(__cplusplus) void __qsort_r_compat(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *)); __sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0); #endif #endif #if defined(__generic) && !defined(__cplusplus) #define qsort_r(base, nel, width, arg4, arg5) \ __generic(arg5, int (*)(void *, const void *, const void *), \ __qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5) #elif defined(__cplusplus) && defined(__sym_compat) __END_DECLS extern "C++" { static inline void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *)) { __qsort_r_compat(base, nmemb, size, thunk, compar); } } __BEGIN_DECLS #endif extern char *suboptarg; /* getsubopt(3) external variable */ #endif /* __BSD_VISIBLE */ #if __EXT1_VISIBLE #ifndef _RSIZE_T_DEFINED #define _RSIZE_T_DEFINED typedef size_t rsize_t; #endif #ifndef _ERRNO_T_DEFINED #define _ERRNO_T_DEFINED typedef int errno_t; #endif /* K.3.6 */ typedef void (*constraint_handler_t)(const char * __restrict, void * __restrict, errno_t); /* K.3.6.1.1 */ constraint_handler_t set_constraint_handler_s(constraint_handler_t handler); /* K.3.6.1.2 */ _Noreturn void abort_handler_s(const char * __restrict, void * __restrict, errno_t); /* K3.6.1.3 */ void ignore_handler_s(const char * __restrict, void * __restrict, errno_t); /* K.3.6.3.2 */ errno_t qsort_s(void *, rsize_t, rsize_t, int (*)(const void *, const void *, void *), void *); #endif /* __EXT1_VISIBLE */ __END_DECLS __NULLABILITY_PRAGMA_POP #endif /* !_STDLIB_H_ */ diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index c311ba3d2bcc..c2107fdaeaae 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -1,151 +1,152 @@ # machine-independent stdlib sources .PATH: ${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib ${LIBC_SRCTOP}/stdlib MISRCS+= \ C99_Exit.c \ a64l.c \ abort.c \ abs.c \ atexit.c \ atof.c \ atoi.c \ atol.c \ atoll.c \ bsearch.c \ bsearch_b.c \ cxa_thread_atexit.c \ cxa_thread_atexit_impl.c \ div.c \ exit.c \ getenv.c \ getopt.c \ getopt_long.c \ getsubopt.c \ hcreate.c \ hcreate_r.c \ hdestroy_r.c \ heapsort.c \ heapsort_b.c \ hsearch_r.c \ imaxabs.c \ imaxdiv.c \ insque.c \ l64a.c \ labs.c \ ldiv.c \ llabs.c \ lldiv.c \ lsearch.c \ memalignment.c \ merge.c \ mergesort_b.c \ ptsname.c \ qsort.c \ qsort_r.c \ qsort_r_compat.c \ qsort_s.c \ quick_exit.c \ radixsort.c \ rand.c \ random.c \ reallocarray.c \ reallocf.c \ realpath.c \ recallocarray.c \ remque.c \ set_constraint_handler_s.c \ strfmon.c \ strtoimax.c \ strtol.c \ strtold.c \ strtoll.c \ strtoq.c \ strtoul.c \ strtonum.c \ strtoull.c \ strtoumax.c \ strtouq.c \ system.c \ tdelete.c \ tdestroy.c \ tfind.c \ tsearch.c \ twalk.c CFLAGS.qsort.c+= -Wsign-compare # Work around an issue on case-insensitive file systems. # libc has both _Exit.c and _exit.s and they both yield # _exit.o (case insensitively speaking). CLEANFILES+=C99_Exit.c C99_Exit.c: ${LIBC_SRCTOP}/stdlib/_Exit.c .NOMETA ln -sf ${.ALLSRC} ${.TARGET} SYM_MAPS+= ${LIBC_SRCTOP}/stdlib/Symbol.map .include "${LIBC_SRCTOP}/stdlib/malloc/Makefile.inc" # machine-dependent stdlib sources .sinclude "${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib/Makefile.inc" MAN+= a64l.3 abort.3 abs.3 atexit.3 atof.3 \ atoi.3 atol.3 at_quick_exit.3 bsearch.3 \ div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 llabs.3 lldiv.3 \ lsearch.3 memalignment.3 memory.3 ptsname.3 qsort.3 \ quick_exit.3 \ radixsort.3 rand.3 random.3 reallocarray.3 reallocf.3 realpath.3 \ set_constraint_handler_s.3 \ strfmon.3 strtod.3 strtol.3 strtonum.3 strtoul.3 system.3 \ tsearch.3 MLINKS+=a64l.3 l64a.3 \ a64l.3 l64a_r.3 MLINKS+=atol.3 atoll.3 MLINKS+=exit.3 _Exit.3 MLINKS+=getenv.3 clearenv.3 \ getenv.3 putenv.3 \ getenv.3 secure_getenv.3 \ getenv.3 setenv.3 \ getenv.3 unsetenv.3 MLINKS+=getopt_long.3 getopt_long_only.3 MLINKS+=hcreate.3 hdestroy.3 \ hcreate.3 hsearch.3 MLINKS+=hcreate.3 hcreate_r.3 \ hcreate.3 hdestroy_r.3 \ hcreate.3 hsearch_r.3 MLINKS+=insque.3 remque.3 MLINKS+=lsearch.3 lfind.3 MLINKS+=ptsname.3 grantpt.3 \ ptsname.3 ptsname_r.3 \ ptsname.3 unlockpt.3 MLINKS+=qsort.3 heapsort.3 \ qsort.3 heapsort_b.3 \ qsort.3 mergesort.3 \ qsort.3 mergesort_b.3 \ qsort.3 qsort_b.3 \ qsort.3 qsort_r.3 \ qsort.3 qsort_s.3 MLINKS+=rand.3 rand_r.3 \ rand.3 srand.3 MLINKS+=random.3 initstate.3 \ random.3 setstate.3 \ random.3 srandom.3 \ random.3 srandomdev.3 MLINKS+=radixsort.3 sradixsort.3 MLINKS+=reallocarray.3 recallocarray.3 MLINKS+=set_constraint_handler_s.3 abort_handler_s.3 MLINKS+=set_constraint_handler_s.3 ignore_handler_s.3 MLINKS+=strfmon.3 strfmon_l.3 MLINKS+=strtod.3 strtof.3 \ strtod.3 strtold.3 MLINKS+=strtol.3 strtoll.3 \ strtol.3 strtoq.3 \ strtol.3 strtoimax.3 +MLINKS+=strtonum.3 strtonumx.3 MLINKS+=strtoul.3 strtoull.3 \ strtoul.3 strtouq.3 \ strtoul.3 strtoumax.3 MLINKS+=tsearch.3 tdelete.3 \ tsearch.3 tfind.3 \ tsearch.3 twalk.3 \ tsearch.3 tdestroy.3 diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index 03a6d0b543ac..373006b4a388 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -1,146 +1,147 @@ FBSD_1.0 { _Exit; a64l; abort; abs; atexit; __cxa_atexit; __cxa_finalize; atof; atoi; atol; atoll; bsearch; div; __isthreaded; exit; getenv; opterr; optind; optopt; optreset; optarg; getopt; getopt_long; getopt_long_only; suboptarg; getsubopt; grantpt; ptsname; unlockpt; hcreate; hdestroy; hsearch; heapsort; imaxabs; imaxdiv; insque; l64a; l64a_r; labs; ldiv; llabs; lldiv; lsearch; lfind; mergesort; putenv; qsort; radixsort; sradixsort; rand_r; srandom; srandomdev; initstate; setstate; random; reallocf; realpath; remque; setenv; unsetenv; strfmon; strtoimax; strtol; strtoll; strtonum; strtoq; strtoul; strtoull; strtoumax; strtouq; system; tdelete; tfind; tsearch; twalk; }; FBSD_1.3 { at_quick_exit; atof_l; atoi_l; atol_l; atoll_l; quick_exit; strtod_l; strtof_l; strtoimax_l; strtol_l; strtold_l; strtoll_l; strtoul_l; strtoull_l; strtoumax_l; }; FBSD_1.4 { atexit_b; bsearch_b; heapsort_b; mergesort_b; qsort_b; hcreate_r; hdestroy_r; hsearch_r; reallocarray; }; FBSD_1.5 { __cxa_thread_atexit; __cxa_thread_atexit_impl; abort_handler_s; ignore_handler_s; set_constraint_handler_s; }; FBSD_1.6 { ptsname_r; qsort_s; rand; srand; }; FBSD_1.7 { clearenv; qsort_r; secure_getenv; }; FBSD_1.8 { getenv_r; }; FBSD_1.9 { memalignment; recallocarray; + strtonumx; tdestroy; }; FBSDprivate_1.0 { __system; _system; __libc_system; __cxa_thread_call_dtors; __libc_atexit; }; diff --git a/lib/libc/stdlib/strtonum.3 b/lib/libc/stdlib/strtonum.3 index 2650d147e7cc..7e6111a6ff71 100644 --- a/lib/libc/stdlib/strtonum.3 +++ b/lib/libc/stdlib/strtonum.3 @@ -1,154 +1,194 @@ .\" Copyright (c) 2004 Ted Unangst +.\" Copyright 2023 Oxide Computer Company .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" $OpenBSD: strtonum.3,v 1.13 2006/04/25 05:15:42 tedu Exp $ .\" -.Dd April 29, 2004 +.Dd January 15, 2026 .Dt STRTONUM 3 .Os .Sh NAME -.Nm strtonum +.Nm strtonum , +.Nm strtonumx .Nd "reliably convert string value to an integer" .Sh SYNOPSIS .In stdlib.h .Ft long long .Fo strtonum .Fa "const char *nptr" .Fa "long long minval" .Fa "long long maxval" .Fa "const char **errstr" .Fc +.Ft long long +.Fo strtonumx +.Fa "const char *nptr" +.Fa "long long minval" +.Fa "long long maxval" +.Fa "const char **errstr" +.Fa "int base" +.Fc .Sh DESCRIPTION The .Fn strtonum -function converts the string in +and +.Fn strtonumx +functions convert the string in .Fa nptr to a .Vt "long long" value. -The -.Fn strtonum -function was designed to facilitate safe, robust programming -and overcome the shortcomings of the +These functions were designed to facilitate safe, robust programming and +overcome the shortcomings of the .Xr atoi 3 and .Xr strtol 3 family of interfaces. .Pp The string may begin with an arbitrary amount of whitespace -(as determined by -.Xr isspace 3 ) +.Pq as determined by Xr isspace 3 followed by a single optional .Ql + or .Ql - sign. .Pp The remainder of the string is converted to a .Vt "long long" -value according to base 10. +value according to base 10 +.Pq for Fn strtonum +or the provided base +.Pq for Fn strtonumx . .Pp The value obtained is then checked against the provided .Fa minval and .Fa maxval bounds. If .Fa errstr is non-null, .Fn strtonum -stores an error string in +and +.Fn strtonumx +store an error string in .Fa *errstr indicating the failure. +.Pp +For +.Fn strtonumx +the value of +.Ar base +is interpreted in the same way as described in +.Xr strtoll 3 . +In particular, if the value of +.Ar base +is 0, then the expected form of +.Ar nptr +is that of a decimal constant, octal constant or hexadecimal constant, any of +which may be preceded by a + or - sign. .Sh RETURN VALUES The .Fn strtonum -function returns the result of the conversion, +and +.Fn strtonumx +functions return the result of the conversion, unless the value would exceed the provided bounds or is invalid. On error, 0 is returned, .Va errno is set, and .Fa errstr will point to an error message. On success, .Fa *errstr will be set to .Dv NULL ; this fact can be used to differentiate a successful return of 0 from an error. .Sh EXAMPLES Using .Fn strtonum +and +.Fn strtonumx correctly is meant to be simpler than the alternative functions. .Bd -literal -offset indent int iterations; const char *errstr; iterations = strtonum(optarg, 1, 64, &errstr); if (errstr != NULL) errx(1, "number of iterations is %s: %s", errstr, optarg); .Ed .Pp The above example will guarantee that the value of iterations is between 1 and 64 (inclusive). .Sh ERRORS .Bl -tag -width Er .It Bq Er ERANGE The given string was out of range. .It Bq Er EINVAL -The given string did not consist solely of digit characters. +The given string did not consist solely of digit characters +.Pq for Fn strtonum , +or characters which are valid in the given base +.Pq for Fn strtonumx . .It Bq Er EINVAL The supplied .Fa minval was larger than .Fa maxval . .El .Pp If an error occurs, .Fa errstr will be set to one of the following strings: .Pp .Bl -tag -width ".Li too large" -compact -.It Li "too large" +.It Qq too large The result was larger than the provided maximum value. -.It Li "too small" +.It Qq too small The result was smaller than the provided minimum value. -.It Li invalid -The string did not consist solely of digit characters. +.It Qq invalid +The string did not consist solely of characters valid in the specified base +.Pq or base 10 for Fn strtonum . +.It Qq unparsable; invalid base specified +The specified base was outside the permitted range. .El .Sh SEE ALSO .Xr atof 3 , .Xr atoi 3 , .Xr atol 3 , .Xr atoll 3 , .Xr sscanf 3 , .Xr strtod 3 , .Xr strtol 3 , .Xr strtoul 3 .Sh STANDARDS The .Fn strtonum function is a .Bx extension. The existing alternatives, such as .Xr atoi 3 and .Xr strtol 3 , are either impossible or difficult to use safely. .Sh HISTORY The .Fn strtonum function first appeared in .Ox 3.6 . +The +.Fn strtonumx +function first appeared in illumos in 2023. diff --git a/lib/libc/stdlib/strtonum.c b/lib/libc/stdlib/strtonum.c index 0d0715bf39c1..44c27d6af3ad 100644 --- a/lib/libc/stdlib/strtonum.c +++ b/lib/libc/stdlib/strtonum.c @@ -1,65 +1,80 @@ /*- * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * + * Copyright 2023 Oxide Computer Company + * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ #include #include #include #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 +#define BADBASE 4 + +#define MBASE ('z' - 'a' + 1 + 10) long long -strtonum(const char *numstr, long long minval, long long maxval, - const char **errstrp) +strtonumx(const char *numstr, long long minval, long long maxval, + const char **errstrp, int base) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; - } ev[4] = { + } ev[5] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, + { "unparsable; invalid base specified", EINVAL }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; + } else if (base < 0 || base > MBASE || base == 1) { + error = BADBASE; } else { - ll = strtoll(numstr, &ep, 10); - if (errno == EINVAL || numstr == ep || *ep != '\0') + ll = strtoll(numstr, &ep, base); + if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; - if (error) + if (error != 0) ll = 0; return (ll); } + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + return (strtonumx(numstr, minval, maxval, errstrp, 10)); +}