I gave it a rudimentary test and it seems to do what I want. I will also need other related functions, though, Eg xasprintf, vsnformat, etc.
J' On Thu, Dec 06, 2012 at 10:59:44PM -0800, Ben Pfaff wrote: John Darrington <j...@darrington.wattle.id.au> writes: > Gnulib has a number of c-* variants of string processing functions, > eg c-strtod, c-strcasecmp etc But notably absent are any locale > independent printf routines. We could use some in PSPP. Here's some initial work on that. It only defines c_snprintf() so far. The test passes for me. Comments are welcome, from anyone. --- lib/c-snprintf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ lib/c-snprintf.h | 46 ++++++++++++++++++++++++++++ lib/c-vasnprintf.c | 43 ++++++++++++++++++++++++++ lib/c-vasnprintf.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++ modules/c-snprintf | 23 ++++++++++++++ modules/c-snprintf-tests | 17 +++++++++++ modules/c-vasnprintf | 55 +++++++++++++++++++++++++++++++++ tests/test-c-snprintf.c | 58 +++++++++++++++++++++++++++++++++++ tests/test-c-snprintf.sh | 15 +++++++++ 9 files changed, 407 insertions(+) create mode 100644 lib/c-snprintf.c create mode 100644 lib/c-snprintf.h create mode 100644 lib/c-vasnprintf.c create mode 100644 lib/c-vasnprintf.h create mode 100644 modules/c-snprintf create mode 100644 modules/c-snprintf-tests create mode 100644 modules/c-vasnprintf create mode 100644 tests/test-c-snprintf.c create mode 100755 tests/test-c-snprintf.sh diff --git a/lib/c-snprintf.c b/lib/c-snprintf.c new file mode 100644 index 0000000..2dadbfc --- /dev/null +++ b/lib/c-snprintf.c @@ -0,0 +1,74 @@ +/* Formatted output to strings in C locale. + Copyright (C) 2004, 2006-2012 Free Software Foundation, Inc. + Written by Simon Josefsson, Paul Eggert, and Ben Pfaff. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <stdio.h> + +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "c-vasnprintf.h" + +/* Print formatted output to string STR. Similar to sprintf, but + additional length SIZE limit how much is written into STR. Returns + string length of formatted string (which may be larger than SIZE). + STR may be NULL, in which case nothing will be written. On error, + return a negative value. + + Formatting takes place in the C locale, that is, the decimal point + used in floating-point formatting directives is always '.'. */ +int +c_snprintf (char *str, size_t size, const char *format, ...) +{ + char *output; + size_t len; + size_t lenbuf = size; + va_list args; + + va_start (args, format); + output = c_vasnprintf (str, &lenbuf, format, args); + len = lenbuf; + va_end (args); + + if (!output) + return -1; + + if (output != str) + { + if (size) + { + size_t pruned_len = (len < size ? len : size - 1); + memcpy (str, output, pruned_len); + str[pruned_len] = '\0'; + } + + free (output); + } + + if (INT_MAX < len) + { + errno = EOVERFLOW; + return -1; + } + + return len; +} diff --git a/lib/c-snprintf.h b/lib/c-snprintf.h new file mode 100644 index 0000000..3192a2d --- /dev/null +++ b/lib/c-snprintf.h @@ -0,0 +1,46 @@ +/* vsprintf with automatic memory allocation in C locale. + Copyright (C) 2002-2004, 2007-2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _C_SNPRINTF_H +#define _C_SNPRINTF_H + +/* Get size_t. */ +#include <stddef.h> + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable _GL_ATTRIBUTE_FORMAT only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#else +# define _GL_ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int c_snprintf (char *str, size_t size, const char *format, ...) + _GL_ATTRIBUTE_FORMAT ((__printf__, 3, 4)); + +#ifdef __cplusplus +} +#endif + +#endif /* _C_SNPRINTF_H */ diff --git a/lib/c-vasnprintf.c b/lib/c-vasnprintf.c new file mode 100644 index 0000000..af6ca08 --- /dev/null +++ b/lib/c-vasnprintf.c @@ -0,0 +1,43 @@ +/* Formatted output to strings in C locale. + Copyright (C) 2009-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <string.h> + +#include "printf-parse.h" + +#define VASNPRINTF c_vasnprintf +#define FCHAR_T char +#define DCHAR_T char +#define DIRECTIVE char_directive +#define DIRECTIVES char_directives +#define PRINTF_PARSE printf_parse +#define DCHAR_CPY memcpy +#define DCHAR_SET memset +#define DCHAR_IS_TCHAR 1 +#define TCHAR_T char + +#define NEED_PRINTF_DOUBLE 1 +#define NEED_PRINTF_LONG_DOUBLE 1 +#define decimal_point_char_defined 1 +static char +decimal_point_char (void) +{ + return '.'; +} + +#include "vasnprintf.c" diff --git a/lib/c-vasnprintf.h b/lib/c-vasnprintf.h new file mode 100644 index 0000000..8fc1724 --- /dev/null +++ b/lib/c-vasnprintf.h @@ -0,0 +1,76 @@ +/* vsprintf with automatic memory allocation in C locale. + Copyright (C) 2002-2004, 2007-2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _C_VASNPRINTF_H +#define _C_VASNPRINTF_H + +/* Get va_list. */ +#include <stdarg.h> + +/* Get size_t. */ +#include <stddef.h> + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable _GL_ATTRIBUTE_FORMAT only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#else +# define _GL_ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(). + You can pass a preallocated buffer for the result in RESULTBUF and its + size in *LENGTHP; otherwise you pass RESULTBUF = NULL. + If successful, return the address of the string (this may be = RESULTBUF + if no dynamic memory allocation was necessary) and set *LENGTHP to the + number of resulting bytes, excluding the trailing NUL. Upon error, set + errno and return NULL. + + When dynamic memory allocation occurs, the preallocated buffer is left + alone (with possibly modified contents). This makes it possible to use + a statically allocated or stack-allocated buffer, like this: + + char buf[100]; + size_t len = sizeof (buf); + char *output = vasnprintf (buf, &len, format, args); + if (output == NULL) + ... error handling ...; + else + { + ... use the output string ...; + if (output != buf) + free (output); + } + + Formatting takes place in the C locale, that is, the decimal point used in + floating-point formatting directives is always '.'. + */ +extern char *c_vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) + _GL_ATTRIBUTE_FORMAT ((__printf__, 3, 0)); + +#ifdef __cplusplus +} +#endif + +#endif /* _C_VASNPRINTF_H */ diff --git a/modules/c-snprintf b/modules/c-snprintf new file mode 100644 index 0000000..edebe2b --- /dev/null +++ b/modules/c-snprintf @@ -0,0 +1,23 @@ +Description: +c_snprintf(): print formatted output to a fixed length string in C locale + +Files: +lib/c-snprintf.h +lib/c-snprintf.c + +Depends-on: +c-vasnprintf + +configure.ac: + +Makefile.am: +lib_SOURCES += c-snprintf.c + +Include: +"c-snprintf.h" + +License: +GPL + +Maintainer: +Ben Pfaff diff --git a/modules/c-snprintf-tests b/modules/c-snprintf-tests new file mode 100644 index 0000000..86d6a14 --- /dev/null +++ b/modules/c-snprintf-tests @@ -0,0 +1,17 @@ +Files: +tests/test-c-snprintf.c +tests/test-c-snprintf.sh +m4/locale-fr.m4 +tests/macros.h + +Depends-on: +setlocale +snprintf + +configure.ac: +gt_LOCALE_FR + +Makefile.am: +TESTS += test-c-snprintf.sh +check_PROGRAMS += test-c-snprintf +TESTS_ENVIRONMENT += LOCALE_FR='@LOCALE_FR@' diff --git a/modules/c-vasnprintf b/modules/c-vasnprintf new file mode 100644 index 0000000..cd94193 --- /dev/null +++ b/modules/c-vasnprintf @@ -0,0 +1,55 @@ +Description: +Formatted output to strings in C locale. + +Files: +lib/c-vasnprintf.h +lib/c-vasnprintf.c +lib/float+.h +lib/printf-args.h +lib/printf-args.c +lib/printf-parse.h +lib/printf-parse.c +lib/vasnprintf.h +lib/vasnprintf.c +m4/wchar_t.m4 +m4/wint_t.m4 +m4/longlong.m4 +m4/intmax_t.m4 +m4/stdint_h.m4 +m4/inttypes_h.m4 +m4/vasnprintf.m4 +m4/printf.m4 +m4/math_h.m4 +m4/exponentd.m4 + +Depends-on: +isnand-nolibm +isnanl-nolibm +frexpl-nolibm +printf-frexp +printf-frexpl +signbit +fpucw +nocrash +printf-safe +alloca-opt +xsize +errno +memchr +multiarch +verify + +configure.ac: +gl_PREREQ_VASNPRINTF_WITH_EXTRAS + +Makefile.am: +lib_SOURCES += c-vasnprintf.c + +Include: +"c-vasnprintf.h" + +License: +GPL + +Maintainer: +Ben Pfaff diff --git a/tests/test-c-snprintf.c b/tests/test-c-snprintf.c new file mode 100644 index 0000000..9da5443 --- /dev/null +++ b/tests/test-c-snprintf.c @@ -0,0 +1,58 @@ +/* Test of snprintf() function. + Copyright (C) 2011-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "c-snprintf.h" + +#include <locale.h> +#include <stdio.h> +#include <string.h> + +#include "macros.h" + +int +main (int argc, char *argv[]) +{ + /* configure should already have checked that the locale is supported. */ + if (setlocale (LC_ALL, "") == NULL) + return 1; + + /* Test behaviour of snprintf() as a "control group". + (We should be running in a locale where ',' is the decimal point.) */ + { + char s[16]; + + snprintf (s, sizeof s, "%#.0f", 1.0); + if (!strcmp (s, "1.")) + { + /* Skip the test, since we're not in a useful locale for testing. */ + return 77; + } + ASSERT (!strcmp (s, "1,")); + } + + /* Test behaviour of c_snprintf(). + It should always use '.' as the decimal point. */ + { + char s[16]; + + c_snprintf (s, sizeof s, "%#.0f", 1.0); + ASSERT (!strcmp (s, "1.")); + } + + return 0; +} diff --git a/tests/test-c-snprintf.sh b/tests/test-c-snprintf.sh new file mode 100755 index 0000000..83051fc --- /dev/null +++ b/tests/test-c-snprintf.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Test in an ISO-8859-1 or ISO-8859-15 locale. +: ${LOCALE_FR=fr_FR} +if test $LOCALE_FR = none; then + if test -f /usr/bin/localedef; then + echo "Skipping test: no traditional french locale is installed" + else + echo "Skipping test: no traditional french locale is supported" + fi + exit 77 +fi + +LC_ALL=$LOCALE_FR \ +./test-c-snprintf${EXEEXT} 1 -- 1.7.10.4 -- PGP Public key ID: 1024D/2DE827B3 fingerprint = 8797 A26D 0854 2EAB 0285 A290 8A67 719C 2DE8 27B3 See http://keys.gnupg.net or any PGP keyserver for public key.
signature.asc
Description: Digital signature