]> granicus.if.org Git - sudo/commitdiff
Add strtonum.c to compat for simpler number parsing.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 10 Dec 2013 21:38:52 +0000 (14:38 -0700)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 10 Dec 2013 21:38:52 +0000 (14:38 -0700)
MANIFEST
compat/Makefile.in
compat/strtonum.c [new file with mode: 0644]
config.h.in
configure
configure.ac
include/missing.h
mkdep.pl

index 193a9868c799f64276ac187312c6d856e3bd29eb..bc93d7c0e3f6b7996b20c612bbe3e49c2df3a59b 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -94,6 +94,7 @@ compat/stdbool.h
 compat/strlcat.c
 compat/strlcpy.c
 compat/strsignal.c
+compat/strtonum.c
 compat/timespec.h
 compat/utime.h
 compat/utimes.c
index ef11f307e91e4de204b3fc1b7cd14e366b7fca91..874854b1e55a14df367531564506426e37580824 100644 (file)
@@ -222,6 +222,8 @@ strlcpy.lo: $(srcdir)/strlcpy.c $(incdir)/missing.h $(top_builddir)/config.h
 strsignal.lo: $(srcdir)/strsignal.c $(incdir)/gettext.h $(incdir)/missing.h \
               $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsignal.c
+strtonum.lo: $(srcdir)/strtonum.c $(incdir)/missing.h $(top_builddir)/config.h
+       $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strtonum.c
 utimes.lo: $(srcdir)/utimes.c $(incdir)/missing.h $(top_builddir)/config.h \
            $(top_srcdir)/compat/utime.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/utimes.c
diff --git a/compat/strtonum.c b/compat/strtonum.c
new file mode 100644 (file)
index 0000000..d1b5e91
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "missing.h"
+
+enum strtonum_err {
+    STN_VALID,
+    STN_INVALID,
+    STN_TOOSMALL,
+    STN_TOOBIG
+};
+
+/*
+ * Convert a string to a number in the range [minval, maxval]
+ */
+long long
+strtonum(const char *str, long long minval, long long maxval,
+    const char **errstrp)
+{
+    const unsigned char *ustr = (const unsigned char *)str;
+    enum strtonum_err errval = STN_VALID;
+    long long lastval, result = 0;
+    unsigned char dig, sign;
+    int remainder;
+
+    if (minval > maxval) {
+       errval = STN_INVALID;
+       goto done;
+    }
+
+    /* Trim leading space and check sign, if any. */
+    while (isspace(*ustr)) {
+       ustr++;
+    }
+    switch (*ustr) {
+    case '-':
+       sign = '-';
+       ustr++;
+       break;
+    case '+':
+       ustr++;
+       /* FALLTHROUGH */
+    default:
+       sign = '+';
+       break;
+    }
+
+    /*
+     * To prevent overflow we determine the highest (or lowest in
+     * the case of negative numbers) value result can have *before*
+     * if its multiplied (divided) by 10 as well as the remainder.
+     * If result matches this value and the next digit is larger than
+     * the remainder, we know the result is out of range.
+     * The remainder is always positive since it is compared against
+     * an unsigned digit.
+     */
+    if (sign == '-') {
+       lastval = minval / 10;
+       remainder = -(minval % 10);
+       if (remainder < 0) {
+           lastval += 1;
+           remainder += 10;
+       }
+       while ((dig = *ustr++) != '\0') {
+           if (!isdigit(dig)) {
+               errval = STN_INVALID;
+               break;
+           }
+           dig -= '0';
+           if (result < lastval || (result == lastval && dig > remainder)) {
+               errval = STN_TOOSMALL;
+               break;
+           } else {
+               result *= 10;
+               result -= dig;
+           }
+       }
+       if (result > maxval)
+           errval = STN_TOOBIG;
+    } else {
+       lastval = maxval / 10;
+       remainder = maxval % 10;
+       while ((dig = *ustr++) != '\0') {
+           if (!isdigit(dig)) {
+               errval = STN_INVALID;
+               break;
+           }
+           dig -= '0';
+           if (result > lastval || (result == lastval && dig > remainder)) {
+               errval = STN_TOOBIG;
+               break;
+           } else {
+               result *= 10;
+               result += dig;
+           }
+       }
+       if (result < minval)
+           errval = STN_TOOSMALL;
+    }
+
+done:
+    switch (errval) {
+    case STN_VALID:
+       if (errstrp != NULL)
+           *errstrp = NULL;
+       break;
+    case STN_INVALID:
+       result = 0;
+       errno = EINVAL;
+       if (errstrp != NULL)
+           *errstrp = "invalid";
+       break;
+    case STN_TOOSMALL:
+       result = 0;
+       errno = ERANGE;
+       if (errstrp != NULL)
+           *errstrp = "too small";
+       break;
+    case STN_TOOBIG:
+       result = 0;
+       errno = ERANGE;
+       if (errstrp != NULL)
+           *errstrp = "too large";
+       break;
+    }
+    return result;
+}
index 908852e2fbea1269b4c41466d724c9d7b35be280..87b2c6122eba30c068de67300a366ca97372ad9d 100644 (file)
 /* Define to 1 if you have the `strtoll' function. */
 #undef HAVE_STRTOLL
 
+/* Define to 1 if you have the `strtonum' function. */
+#undef HAVE_STRTONUM
+
 /* Define to 1 if `d_type' is a member of `struct dirent'. */
 #undef HAVE_STRUCT_DIRENT_D_TYPE
 
index 13c656a4a0750f8bd13a47c04c9361ce61bbd98d..f37737f8659e2bcf8876de31893d61b702f42bee 100755 (executable)
--- a/configure
+++ b/configure
@@ -17751,6 +17751,19 @@ esac
 
 fi
 
+ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum"
+if test "x$ac_cv_func_strtonum" = xyes; then :
+  $as_echo "#define HAVE_STRTONUM 1" >>confdefs.h
+
+else
+  case " $LIBOBJS " in
+  *" strtonum.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS strtonum.$ac_objext"
+ ;;
+esac
+
+fi
+
 
 for ac_func in getopt_long
 do :
index f972209e1bf17ac61bb37b7686173419eac91a6b..c5bf464e880f561139f64f0db1e2e5c10f347050 100644 (file)
@@ -2388,7 +2388,7 @@ SUDO_FUNC_FNMATCH([AC_DEFINE(HAVE_FNMATCH)], [AC_LIBOBJ(fnmatch)
     COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }fnm_test"
 ])
 SUDO_FUNC_ISBLANK
-AC_REPLACE_FUNCS(memrchr memset_s pw_dup strlcpy strlcat)
+AC_REPLACE_FUNCS(memrchr memset_s pw_dup strlcpy strlcat strtonum)
 AC_CHECK_FUNCS(getopt_long, [], [AC_LIBOBJ(getopt_long)
     AC_MSG_CHECKING([for optreset])
     AC_CACHE_VAL(sudo_cv_optreset, [
index 0c89f5e5af9289ed2df6046f2e333559be280dfe..a4051b3ad3549af9db1fb564e71c600bb2016863 100644 (file)
@@ -446,6 +446,9 @@ char *strsignal(int);
 #ifndef HAVE_SIG2STR
 int sig2str(int, char *);
 #endif
+#ifndef HAVE_STRTONUM
+long long strtonum(const char *, long long, long long, const char **);
+#endif
 void initprogname(const char *);
 
 #endif /* _SUDO_MISSING_H */
index 6b108f7836e5a998b5f77fea8d24bb7431d93d0d..e6c1fa58823582847820987c37a05a653d149f70 100755 (executable)
--- a/mkdep.pl
+++ b/mkdep.pl
@@ -70,7 +70,7 @@ sub mkdep {
     $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo sssd.lo:;
     # XXX - fill in AUTH_OBJS from contents of the auth dir instead
     $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
-    $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo utimes.lo globtest.o fnm_test.o:;
+    $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo strtonum.lo utimes.lo globtest.o fnm_test.o:;
 
     # Parse OBJS lines
     my %objs;