diff --git a/MANIFEST b/MANIFEST index f8207316f..c8a37f75b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -157,6 +157,7 @@ install-sh ltmain.sh m4/ax_check_compile_flag.m4 m4/ax_check_link_flag.m4 +m4/ax_func_snprintf.m4 m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 diff --git a/aclocal.m4 b/aclocal.m4 index fe5f1629f..cc710d454 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -128,6 +128,7 @@ AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], m4_include([m4/ax_check_compile_flag.m4]) m4_include([m4/ax_check_link_flag.m4]) +m4_include([m4/ax_func_snprintf.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) diff --git a/compat/snprintf.c b/compat/snprintf.c index 93f49ed01..7384ecfa3 100644 --- a/compat/snprintf.c +++ b/compat/snprintf.c @@ -41,7 +41,9 @@ #include -#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_SNPRINTF) || !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_SNPRINTF) || \ + !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) || \ + defined(PREFER_PORTABLE_SNPRINTF) #include @@ -668,16 +670,16 @@ done: /* NOTREACHED */ } -#ifndef HAVE_VSNPRINTF +#if !defined(HAVE_VSNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int vsnprintf(char *str, size_t n, const char *fmt, va_list ap) { return xxxprintf(&str, n, 0, fmt, ap); } -#endif /* HAVE_VSNPRINTF */ +#endif /* !HAVE_VSNPRINTF || PREFER_PORTABLE_SNPRINTF */ -#ifndef HAVE_SNPRINTF +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int snprintf(char *str, size_t n, char const *fmt, ...) { @@ -689,18 +691,18 @@ snprintf(char *str, size_t n, char const *fmt, ...) va_end(ap); return ret; } -#endif /* HAVE_SNPRINTF */ +#endif /* !HAVE_SNPRINTF || PREFER_PORTABLE_SNPRINTF */ -#ifndef HAVE_VASPRINTF +#if !defined(HAVE_VASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int vasprintf(char **str, const char *fmt, va_list ap) { return xxxprintf(str, 0, 1, fmt, ap); } -#endif /* HAVE_VASPRINTF */ +#endif /* !HAVE_VASPRINTF || PREFER_PORTABLE_SNPRINTF */ -#ifndef HAVE_ASPRINTF +#if !defined(HAVE_ASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int asprintf(char **str, char const *fmt, ...) { @@ -712,6 +714,6 @@ asprintf(char **str, char const *fmt, ...) va_end(ap); return ret; } -#endif /* HAVE_ASPRINTF */ +#endif /* !HAVE_ASPRINTF || PREFER_PORTABLE_SNPRINTF */ -#endif /* !HAVE_VSNPRINTF || !HAVE_SNPRINTF || !HAVE_VASPRINTF || !HAVE_ASPRINTF */ +#endif /* !HAVE_VSNPRINTF || !HAVE_SNPRINTF || !HAVE_VASPRINTF || !HAVE_ASPRINTF || PREFER_PORTABLE_SNPRINTF */ diff --git a/config.h.in b/config.h.in index 9d42f063e..4dd539882 100644 --- a/config.h.in +++ b/config.h.in @@ -937,6 +937,9 @@ ones. */ #undef PC_INSULTS +/* Enable replacement (v)snprintf if system (v)snprintf is broken. */ +#undef PREFER_PORTABLE_SNPRINTF + /* The syslog priority sudo will use for unsuccessful attempts/errors. */ #undef PRI_FAILURE diff --git a/configure b/configure index 06cb7a6e6..38b16153e 100755 --- a/configure +++ b/configure @@ -17851,7 +17851,7 @@ esac fi done -for ac_func in snprintf vsnprintf asprintf vasprintf +for ac_func in snprintf vsnprintf do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -17860,11 +17860,134 @@ if eval test \"x\$"$as_ac_var"\" = x"yes"; then : #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF -else - NEED_SNPRINTF=1 fi done +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working snprintf" >&5 +$as_echo_n "checking for working snprintf... " >&6; } +if ${ac_cv_have_working_snprintf+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_have_working_snprintf=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = snprintf (bufs, 2, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = snprintf (bufd, 2, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_have_working_snprintf=yes +else + ac_cv_have_working_snprintf=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_working_snprintf" >&5 +$as_echo "$ac_cv_have_working_snprintf" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vsnprintf" >&5 +$as_echo_n "checking for working vsnprintf... " >&6; } +if ${ac_cv_have_working_vsnprintf+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_have_working_vsnprintf=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int my_vsnprintf (char *buf, const char *tmpl, ...) +{ + int i; + va_list args; + va_start (args, tmpl); + i = vsnprintf (buf, 2, tmpl, args); + va_end (args); + return i; +} + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = my_vsnprintf (bufs, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = my_vsnprintf (bufd, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_have_working_vsnprintf=yes +else + ac_cv_have_working_vsnprintf=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_working_vsnprintf" >&5 +$as_echo "$ac_cv_have_working_vsnprintf" >&6; } +if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; then + case " $LIBOBJS " in + *" snprintf.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Replacing missing/broken (v)snprintf() with sudo's version." >&5 +$as_echo "$as_me: WARNING: Replacing missing/broken (v)snprintf() with sudo's version." >&2;} + +$as_echo "#define PREFER_PORTABLE_SNPRINTF 1" >>confdefs.h + +fi +for ac_func in asprintf vasprintf +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test X"$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf" = X"yesyes"; then + # Don't add snprintf to LIBOBJS if it is already present. + if test X"$ac_cv_func_asprintf$ac_cv_func_vasprintf" != X"yesyes"; then + case " $LIBOBJS " in + *" snprintf.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" + ;; +esac + + fi +fi if test X"$ac_cv_type_struct_timespec" != X"no"; then ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim" "ac_cv_member_struct_stat_st_mtim" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_mtim" = xyes; then : @@ -18174,14 +18297,6 @@ _ACEOF fi -if test -n "$NEED_SNPRINTF"; then - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - -fi ac_fn_c_check_func "$LINENO" "socket" "ac_cv_func_socket" if test "x$ac_cv_func_socket" = xyes; then : diff --git a/configure.ac b/configure.ac index c9e3a6822..a55e23a9d 100644 --- a/configure.ac +++ b/configure.ac @@ -2401,7 +2401,14 @@ AC_CHECK_FUNCS(mkstemps mkdtemp, [], [ AC_CHECK_FUNCS(random lrand48, [break]) AC_LIBOBJ(mktemp) ]) -AC_CHECK_FUNCS(snprintf vsnprintf asprintf vasprintf, , [NEED_SNPRINTF=1]) +AX_FUNC_SNPRINTF +AC_CHECK_FUNCS(asprintf vasprintf) +if test X"$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf" = X"yesyes"; then + # Don't add snprintf to LIBOBJS if it is already present. + if test X"$ac_cv_func_asprintf$ac_cv_func_vasprintf" != X"yesyes"; then + AC_LIBOBJ(snprintf) + fi +fi if test X"$ac_cv_type_struct_timespec" != X"no"; then AC_CHECK_MEMBER([struct stat.st_mtim], [AC_DEFINE(HAVE_ST_MTIM)] [AC_CHECK_MEMBER([struct stat.st_mtim.st__tim], AC_DEFINE(HAVE_ST__TIM))], @@ -2466,13 +2473,6 @@ AC_INCLUDES_DEFAULT #include <$ac_header_dirent> ]) dnl -dnl If NEED_SNPRINTF is set, add snprintf.c to LIBOBJS -dnl (it contains snprintf, vsnprintf, asprintf, and vasprintf) -dnl -if test -n "$NEED_SNPRINTF"; then - AC_LIBOBJ(snprintf) -fi -dnl dnl If socket(2) not in libc, check -lsocket and -linet dnl May need to link with *both* -lnsl and -lsocket due to unresolved symbols dnl diff --git a/m4/ax_func_snprintf.m4 b/m4/ax_func_snprintf.m4 new file mode 100644 index 000000000..f0dccf0ea --- /dev/null +++ b/m4/ax_func_snprintf.m4 @@ -0,0 +1,82 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_func_snprintf.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FUNC_SNPRINTF +# +# DESCRIPTION +# +# Checks for a fully C99 compliant snprintf, in particular checks whether +# it does bounds checking and returns the correct string length; does the +# same check for vsnprintf. If no working snprintf or vsnprintf is found, +# request a replacement and warn the user about it. Note: the mentioned +# replacement is freely available and may be used in any project +# regardless of it's license. +# +# LICENSE +# +# Copyright (c) 2008 Ruediger Kuhlmann +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 5 + +AC_DEFUN([AX_FUNC_SNPRINTF], +[AC_CHECK_FUNCS(snprintf vsnprintf) +AC_MSG_CHECKING(for working snprintf) +AC_CACHE_VAL(ac_cv_have_working_snprintf, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = snprintf (bufs, 2, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = snprintf (bufd, 2, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +}]])],[ac_cv_have_working_snprintf=yes],[ac_cv_have_working_snprintf=no],[ac_cv_have_working_snprintf=cross])]) +AC_MSG_RESULT([$ac_cv_have_working_snprintf]) +AC_MSG_CHECKING(for working vsnprintf) +AC_CACHE_VAL(ac_cv_have_working_vsnprintf, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include +#include + +int my_vsnprintf (char *buf, const char *tmpl, ...) +{ + int i; + va_list args; + va_start (args, tmpl); + i = vsnprintf (buf, 2, tmpl, args); + va_end (args); + return i; +} + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = my_vsnprintf (bufs, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = my_vsnprintf (bufd, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +}]])],[ac_cv_have_working_vsnprintf=yes],[ac_cv_have_working_vsnprintf=no],[ac_cv_have_working_vsnprintf=cross])]) +AC_MSG_RESULT([$ac_cv_have_working_vsnprintf]) +if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; then + AC_LIBOBJ(snprintf) + AC_MSG_WARN([Replacing missing/broken (v)snprintf() with sudo's version.]) + AC_DEFINE(PREFER_PORTABLE_SNPRINTF, 1, [Enable replacement (v)snprintf if system (v)snprintf is broken.]) +fi])