From bc54ea6edf251c7623c3afa797da8a67a882fbd2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 21 Nov 2002 00:35:15 +0000 Subject: [PATCH] Include a printf implementation supporting C99 snprintf and SUS positional parameters: (#79488) * glib/gstrfuncs.c: * glib/gspawn-win32.c: * glib/gscanner.c: * glib/gconvert.c: * glib/gbacktrace.c: Use _g_printf wrappers. * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 snprintf semantics now. * glib/gmessages.c (printf_string_upper_bound): No longer needed, since we can assume C99 snprintf semantics now. (g_logv): Simplify. * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether printf supports SUS positional parameters. * configure.in: New option --enable-included-printf to force compilation of trio; otherwise trio is compiled if the system printf misses either C99 snprintf semantics of SUS positional parameters. * glib/Makefile.am (SUBDIRS): Conditionally compile trio. (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. (glibsubinclude_HEADERS): Add gprintf.h. * glib/gprintfint.h: New private wrapping either system printf or trio printf variants in _g_printf wrappers for use inside glib. * glib/gprintf.h: New public header declaring g_printf variants. * glib/gprintf.c: Corresponding implementations. * glib/trio/*: New directory, containing the trio-1.9 sources. * glib/tmpl/string_utils.sgml: Add note on including gprintf.h, move some docs inline. * glib/glib-sections.txt: Add g_printf, g_vprintf, g_fprintf, g_vfprintf, g_sprintf, g_vsprintf. --- ChangeLog | 38 + ChangeLog.pre-2-10 | 38 + ChangeLog.pre-2-12 | 38 + ChangeLog.pre-2-2 | 38 + ChangeLog.pre-2-4 | 38 + ChangeLog.pre-2-6 | 38 + ChangeLog.pre-2-8 | 38 + acinclude.m4 | 34 + configure.in | 27 + docs/reference/ChangeLog | 8 + docs/reference/glib/glib-sections.txt | 6 + docs/reference/glib/tmpl/string_utils.sgml | 125 +- glib/Makefile.am | 16 +- glib/gbacktrace.c | 30 +- glib/gconvert.c | 3 +- glib/gmessages.c | 354 +- glib/gprintf.c | 253 ++ glib/gprintf.h | 60 + glib/gprintfint.h | 63 + glib/gscanner.c | 149 +- glib/gspawn-win32.c | 9 +- glib/gstrfuncs.c | 11 +- glib/gutils.c | 49 +- glib/trio/Makefile.am | 17 + glib/trio/README | 27 + glib/trio/trio.c | 6760 ++++++++++++++++++++++++++++ glib/trio/trio.h | 216 + glib/trio/triodef.h | 206 + glib/trio/trionan.c | 925 ++++ glib/trio/trionan.h | 81 + glib/trio/triop.h | 150 + glib/trio/triostr.c | 2024 +++++++++ glib/trio/triostr.h | 127 + 33 files changed, 11445 insertions(+), 551 deletions(-) create mode 100644 glib/gprintf.c create mode 100644 glib/gprintf.h create mode 100644 glib/gprintfint.h create mode 100644 glib/trio/Makefile.am create mode 100644 glib/trio/README create mode 100644 glib/trio/trio.c create mode 100644 glib/trio/trio.h create mode 100644 glib/trio/triodef.h create mode 100644 glib/trio/trionan.c create mode 100644 glib/trio/trionan.h create mode 100644 glib/trio/triop.h create mode 100644 glib/trio/triostr.c create mode 100644 glib/trio/triostr.h diff --git a/ChangeLog b/ChangeLog index db7f3ea..efc1f5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index db7f3ea..efc1f5c 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,41 @@ +2002-11-21 Matthias Clasen + + Include a printf implementation supporting C99 snprintf and SUS + positional parameters: (#79488) + + * glib/gstrfuncs.c: + * glib/gspawn-win32.c: + * glib/gscanner.c: + * glib/gconvert.c: + * glib/gbacktrace.c: Use _g_printf wrappers. + + * glib/gutils.c (g_vsnprintf): Simplify, since we can assume C99 + snprintf semantics now. + + * glib/gmessages.c (printf_string_upper_bound): No longer needed, + since we can assume C99 snprintf semantics now. + (g_logv): Simplify. + + * acinclude.m4 (AC_FUNC_PRINTF_UNIX98): New macro to check wether + printf supports SUS positional parameters. + + * configure.in: New option --enable-included-printf to force + compilation of trio; otherwise trio is compiled if the system + printf misses either C99 snprintf semantics of SUS positional + parameters. + + * glib/Makefile.am (SUBDIRS): Conditionally compile trio. + (libglib_2_0_la_SOURCES): Add gprintf.c and gprintfint.h. + (glibsubinclude_HEADERS): Add gprintf.h. + + * glib/gprintfint.h: New private wrapping either system printf + or trio printf variants in _g_printf wrappers for use inside glib. + + * glib/gprintf.h: New public header declaring g_printf variants. + * glib/gprintf.c: Corresponding implementations. + + * glib/trio/*: New directory, containing the trio-1.9 sources. + 2002-11-20 Matthias Clasen * glib/gmessages.h (g_return_if_fail): diff --git a/acinclude.m4 b/acinclude.m4 index 4fe1e02..a969c70 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -327,3 +327,37 @@ if test $ac_cv_func_vsnprintf_c99 = yes; then fi ])# AC_FUNC_VSNPRINTF_C99 + +dnl @synopsis AC_FUNC_PRINTF_UNIX98 +dnl +dnl Check whether the printf() family supports Unix98 %n$ positional parameters +dnl +AC_DEFUN([AC_FUNC_PRINTF_UNIX98], +[AC_CACHE_CHECK(whether printf supports positional parameters, + ac_cv_func_printf_unix98, +[AC_TRY_RUN( +[#include + +int +main (void) +{ + char buffer[128]; + + sprintf (buffer, "%2\$d %3\$d %1\$d", 1, 2, 3); + if (strcmp ("2 3 1", buffer) == 0) + exit (0); + exit (1); +}], ac_cv_func_printf_unix98=yes, ac_cv_func_printf_unix98=no, ac_cv_func_printf_unix98=no)]) +dnl Note that the default is to be pessimistic in the case of cross compilation. +dnl If you know that the target printf() supports positional parameters, you can get around +dnl this by setting ac_func_printf_unix98 to yes, as described in the Autoconf manual. +if test $ac_cv_func_printf_unix98 = yes; then + AC_DEFINE(HAVE_UNIX98_PRINTF, 1, + [Define if your printf function family supports positional parameters + as specified by Unix98.]) +fi +])# AC_FUNC_PRINTF_UNIX98 + + + + diff --git a/configure.in b/configure.in index ad6931a..65a6fc9 100644 --- a/configure.in +++ b/configure.in @@ -712,6 +712,32 @@ AC_MSG_RESULT(unsigned $glib_size_type) AC_CHECK_FUNCS(lstat strerror strsignal memmove mkstemp vsnprintf stpcpy strcasecmp strncasecmp poll getcwd nanosleep vasprintf unsetenv getc_unlocked) AC_FUNC_VSNPRINTF_C99 +AC_FUNC_PRINTF_UNIX98 + +# +# Check whether to use trio printf +# + +AC_ARG_ENABLE(included-printf, [ --enable-included-printf use included printf [default=auto]], enable_trio="$enableval") + +if test "$enable_trio" != "no" ; then + if test "$ac_cv_func_vsnprintf_c99" != "yes" ; then + enable_trio = yes + fi + if test "$ac_cv_func_printf_unix98" != "yes" ; then + enable_trio = yes + fi +fi + +AM_CONDITIONAL(HAVE_GOOD_PRINTF, test "$enable_trio" != "yes") +if test "$enable_trio" != "yes" ; then + AC_DEFINE(HAVE_GOOD_PRINTF,1,[define to use system printf]) + AC_DEFINE(HAVE_VASPRINTF,1) + AC_DEFINE(HAVE_C99_VSNPRINTF,1) + AC_DEFINE(HAVE_UNIX98_PRINTF,1) +else + AC_DEFINE(HAVE_GOOD_PRINTF,0) +fi # Check if bcopy can be used for overlapping copies, if memmove isn't found. # The check is borrowed from the PERL Configure script. @@ -2355,6 +2381,7 @@ build/win32/Makefile build/win32/dirent/Makefile glib/Makefile glib/libcharset/Makefile +glib/trio/Makefile gmodule/gmoduleconf.h gmodule/Makefile gobject/Makefile diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 9183d5c..e15adcd 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,11 @@ +2002-11-21 Matthias Clasen + + * glib/tmpl/string_utils.sgml: Add note on including gprintf.h, + move some docs inline. + + * glib/glib-sections.txt: Add g_printf, g_vprintf, g_fprintf, + g_vfprintf, g_sprintf, g_vsprintf. + 2002-11-20 Matthias Clasen * glib/tmpl/macros_misc.sgml: Document G_LIKELY, G_UNLIKELY. diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 80e4085..c2a2bca 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -896,6 +896,12 @@ g_strlcat g_strdup_printf g_strdup_vprintf +g_printf +g_vprintf +g_fprintf +g_vfprintf +g_sprintf +g_vsprintf g_snprintf g_vsnprintf g_printf_string_upper_bound diff --git a/docs/reference/glib/tmpl/string_utils.sgml b/docs/reference/glib/tmpl/string_utils.sgml index bbc32cc..027f33d 100644 --- a/docs/reference/glib/tmpl/string_utils.sgml +++ b/docs/reference/glib/tmpl/string_utils.sgml @@ -9,6 +9,15 @@ various string-related functions. This section describes a number of utility functions for creating, duplicating, and manipulating strings. + +Note that the functions g_printf(), g_fprintf(), g_sprintf(), g_snprintf(), +g_vprintf(), g_vfprintf(), g_vsprintf() and g_vsnprintf() are declared in +the header gprintf.h which is not +included in glib.h (otherwise using +glib.h would drag in stdio.h), so +you'll have to explicitly include <glib/gprintf.h> +in order to use the printf() functions. + @@ -180,78 +189,90 @@ The returned string should be freed when no longer needed. @Returns: a newly-allocated string holding the result. - + -A safer form of the standard sprintf() function. -The output is guaranteed to not exceed @n characters (including the -terminating nul character), so it is easy to ensure that a buffer overflow -cannot occur. + + +@format: +@Varargs: +@Returns: + + + -See also g_strdup_printf(). + - + +@format: +@args: +@Returns: + + + -In versions of GLib prior to 1.2.3, this function may return -1 if the output -was truncated, and the truncated string may not be nul-terminated. -In versions prior to 1.3.12, this function returns the length of the output -string. + - - + +@file: +@format: +@Varargs: +@Returns: + + + -The return value of g_snprintf() conforms to the snprintf() -function as standardized in ISO C99. Note that this is different from -traditional snprintf(), which returns the length of -the output string. + - -@string: the buffer to hold the output. -@n: the maximum number of characters to produce (including the terminating nul -character). -@format: the format string. See the sprintf(). -documentation. -@Varargs: the arguments to insert in the output. -@Returns: the number of characters which would be produced if the buffer was -large enough. +@file: +@format: +@args: +@Returns: - + -A safer form of the standard vsprintf() function. -The output is guaranteed to not exceed @n characters (including the -terminating nul character), so it is easy to ensure that a buffer overflow -cannot occur. + + +@string: +@format: +@Varargs: +@Returns: + + + -See also g_strdup_vprintf(). + - + +@string: +@format: +@args: +@Returns: + + + -In versions of GLib prior to 1.2.3, this function may return -1 if the output -was truncated, and the truncated string may not be nul-terminated. -In versions prior to 1.3.12, this function returns the length of the output -string. - - + +@string: +@n: +@format: +@Varargs: +@Returns: + + + -The return value of g_vsnprintf() conforms to the vsnprintf() -function as standardized in ISO C99. Note that this is different from -traditional vsnprintf(), which returns the length of -the output string. - -@string: the buffer to hold the output. -@n: the maximum number of characters to produce (including the terminating nul -character). -@format: the format string. See the sprintf() -documentation. -@args: the list of arguments to insert in the output. -@Returns: the number of characters which would be produced if the buffer was -large enough. +@string: +@n: +@format: +@args: +@Returns: diff --git a/glib/Makefile.am b/glib/Makefile.am index 848b4c4..c4cb662 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -1,6 +1,13 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS=libcharset +if HAVE_GOOD_PRINTF +else +TRIO_SUBDIR = trio +endif + +SUBDIRS = libcharset $(TRIO_SUBDIR) + +DIST_SUBDIRS = libcharset trio INCLUDES = -I$(top_srcdir) -DG_LOG_DOMAIN=\"GLib\" \ @GLIB_DEBUG_FLAGS@ -DG_DISABLE_DEPRECATED -DGLIB_COMPILATION @@ -82,7 +89,9 @@ libglib_2_0_la_SOURCES = \ gunidecomp.h \ gunidecomp.c \ gutils.c \ - gdebug.h + gdebug.h \ + gprintf.c \ + gprintfint.h EXTRA_libglib_2_0_la_SOURCES = \ giounix.c \ @@ -140,7 +149,8 @@ glibsubinclude_HEADERS = \ gtypes.h \ gunicode.h \ gutils.h \ - gwin32.h + gwin32.h \ + gprintf.h install-data-local: install-ms-lib install-libtool-import-lib @if test -f $(glibincludedir)/glist.h ; then \ diff --git a/glib/gbacktrace.c b/glib/gbacktrace.c index 7d131af..24e9a9e 100644 --- a/glib/gbacktrace.c +++ b/glib/gbacktrace.c @@ -38,6 +38,8 @@ #include #include #include "glib.h" +#include "gprintfint.h" + #ifdef HAVE_SYS_TIME_H #include #endif @@ -98,19 +100,19 @@ g_on_error_query (const gchar *prg_name) retry: if (prg_name) - fprintf (stdout, - "%s (pid:%u): %s%s%s: ", - prg_name, - (guint) getpid (), - query1, - query2, - query3); + _g_fprintf (stdout, + "%s (pid:%u): %s%s%s: ", + prg_name, + (guint) getpid (), + query1, + query2, + query3); else - fprintf (stdout, - "(process:%u): %s%s: ", - (guint) getpid (), - query1, - query3); + _g_fprintf (stdout, + "(process:%u): %s%s: ", + (guint) getpid (), + query1, + query3); fflush (stdout); if (isatty(0) && isatty(1)) @@ -163,7 +165,7 @@ g_on_error_stack_trace (const gchar *prg_name) if (!prg_name) return; - sprintf (buf, "%u", (guint) getpid ()); + _g_sprintf (buf, "%u", (guint) getpid ()); args[1] = (gchar*) prg_name; args[2] = buf; @@ -276,7 +278,7 @@ stack_trace (char **args) if ((c == '\n') || (c == '\r')) { buffer[index] = 0; - fprintf (stdout, "%s", buffer); + _g_fprintf (stdout, "%s", buffer); state = 0; index = 0; } diff --git a/glib/gconvert.c b/glib/gconvert.c index 545f402..4de433a 100644 --- a/glib/gconvert.c +++ b/glib/gconvert.c @@ -29,6 +29,7 @@ #include #include "glib.h" +#include "gprintfint.h" #ifdef G_PLATFORM_WIN32 #define STRICT @@ -338,7 +339,7 @@ open_converter (const gchar *to_codeset, /* create our key */ key = g_alloca (strlen (from_codeset) + strlen (to_codeset) + 2); - sprintf (key, "%s:%s", from_codeset, to_codeset); + _g_sprintf (key, "%s:%s", from_codeset, to_codeset); G_LOCK (iconv_cache_lock); diff --git a/glib/gmessages.c b/glib/gmessages.c index f3b664c..0fb2358 100644 --- a/glib/gmessages.c +++ b/glib/gmessages.c @@ -44,6 +44,7 @@ #include #include #include "gdebug.h" +#include "gprintfint.h" #ifdef G_OS_WIN32 typedef FILE* GFileDescriptor; @@ -71,14 +72,6 @@ struct _GLogHandler }; -/* --- prototypes --- */ -#ifndef HAVE_C99_VSNPRINTF -static gsize printf_string_upper_bound (const gchar *format, - gboolean may_warn, - va_list args); -#endif /* !HAVE_C99_VSNPRINTF */ - - /* --- variables --- */ static GMutex *g_messages_lock = NULL; static GLogDomain *g_log_domains = NULL; @@ -409,10 +402,6 @@ g_logv (const gchar *log_domain, gboolean was_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0; gint i; -#ifndef HAVE_VSNPRINTF - va_list args2; -#endif /* HAVE_VSNPRINTF */ - log_level &= G_LOG_LEVEL_MASK; if (!log_level) return; @@ -420,21 +409,7 @@ g_logv (const gchar *log_domain, /* we use a stack buffer of fixed size, because we might get called * recursively. */ -#ifdef HAVE_VSNPRINTF - vsnprintf (buffer, 1024, format, args1); -#else /* !HAVE_VSNPRINTF */ - G_VA_COPY (args2, args1); - if (printf_string_upper_bound (format, FALSE, args1) < 1024) - vsprintf (buffer, format, args2); - else - { - /* since we might be out of memory, we can't use g_vsnprintf(). */ - /* we are out of luck here */ - strncpy (buffer, format, 1024); - buffer[1024] = 0; - } - va_end (args2); -#endif /* !HAVE_VSNPRINTF */ + _g_vsnprintf (buffer, 1024, format, args1); for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i)) { @@ -548,7 +523,7 @@ strdup_convert (const gchar *string, if (!warned) { warned = TRUE; - fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message); + _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message); } g_error_free (err); @@ -909,333 +884,12 @@ g_printerr (const gchar *format, g_free (string); } -#ifndef MB_LEN_MAX -# define MB_LEN_MAX 8 -#endif - -#ifndef HAVE_C99_VSNPRINTF - -typedef struct -{ - guint min_width; - guint precision; - gboolean alternate_format, zero_padding, adjust_left, locale_grouping; - gboolean add_space, add_sign, possible_sign, seen_precision; - gboolean mod_half, mod_long, mod_extra_long; -} PrintfArgSpec; - -static gsize -printf_string_upper_bound (const gchar *format, - gboolean may_warn, - va_list args) -{ - static const gboolean honour_longs = SIZEOF_LONG > 4 || SIZEOF_VOID_P > 4; - gsize len = 1; - - if (!format) - return len; - - while (*format) - { - register gchar c = *format++; - - if (c != '%') - len += 1; - else /* (c == '%') */ - { - PrintfArgSpec spec = { 0, }; - gboolean seen_l = FALSE, conv_done = FALSE; - gsize conv_len = 0; - const gchar *spec_start = format; - - do - { - c = *format++; - switch (c) - { - GDoubleIEEE754 u_double; - guint v_uint; - gint v_int; - const gchar *v_string; - - /* beware of positional parameters - */ - case '$': - if (may_warn) - g_warning (G_GNUC_PRETTY_FUNCTION - "(): unable to handle positional parameters (%%n$)"); - len += 1024; /* try adding some safety padding */ - conv_done = TRUE; - break; - - /* parse flags - */ - case '#': - spec.alternate_format = TRUE; - break; - case '0': - spec.zero_padding = TRUE; - break; - case '-': - spec.adjust_left = TRUE; - break; - case ' ': - spec.add_space = TRUE; - break; - case '+': - spec.add_sign = TRUE; - break; - case '\'': - spec.locale_grouping = TRUE; - break; - - /* parse output size specifications - */ - case '.': - spec.seen_precision = TRUE; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - v_uint = c - '0'; - c = *format; - while (c >= '0' && c <= '9') - { - format++; - v_uint = v_uint * 10 + c - '0'; - c = *format; - } - if (spec.seen_precision) - spec.precision = MAX (spec.precision, v_uint); - else - spec.min_width = MAX (spec.min_width, v_uint); - break; - case '*': - v_int = va_arg (args, int); - if (spec.seen_precision) - { - /* forget about negative precision */ - if (v_int >= 0) - spec.precision = MAX (spec.precision, v_int); - } - else - { - if (v_int < 0) - { - v_int = - v_int; - spec.adjust_left = TRUE; - } - spec.min_width = MAX (spec.min_width, v_int); - } - break; - - /* parse type modifiers - */ - case 'h': - spec.mod_half = TRUE; - break; - case 'l': - if (!seen_l) - { - spec.mod_long = TRUE; - seen_l = TRUE; - break; - } - /* else, fall through */ - case 'L': - case 'q': - spec.mod_long = TRUE; - spec.mod_extra_long = TRUE; - break; - case 'z': - case 'Z': -#if GLIB_SIZEOF_SIZE_T > 4 - spec.mod_long = TRUE; - spec.mod_extra_long = TRUE; -#endif /* GLIB_SIZEOF_SIZE_T > 4 */ - break; - case 't': -#if GLIB_SIZEOF_PTRDIFF_T > 4 - spec.mod_long = TRUE; - spec.mod_extra_long = TRUE; -#endif /* GLIB_SIZEOF_PTRDIFF_T > 4 */ - break; - case 'j': -#if GLIB_SIZEOF_INTMAX_T > 4 - spec.mod_long = TRUE; - spec.mod_extra_long = TRUE; -#endif /* GLIB_SIZEOF_INTMAX_T > 4 */ - break; - - /* parse output conversions - */ - case '%': - conv_len += 1; - break; - case 'O': - case 'D': - case 'I': - case 'U': - /* some C libraries feature long variants for these as well? */ - spec.mod_long = TRUE; - /* fall through */ - case 'o': - conv_len += 2; - /* fall through */ - case 'd': - case 'i': - conv_len += 1; /* sign */ - /* fall through */ - case 'u': - conv_len += 4; - /* fall through */ - case 'x': - case 'X': - spec.possible_sign = TRUE; - conv_len += 10; - if (spec.mod_long && honour_longs) - conv_len *= 2; - if (spec.mod_extra_long) - conv_len *= 2; - if (spec.mod_extra_long) - { - (void) va_arg (args, gint64); - } - else if (spec.mod_long) - (void) va_arg (args, long); - else - (void) va_arg (args, int); - break; - case 'A': - case 'a': - /* 0x */ - conv_len += 2; - /* fall through */ - case 'g': - case 'G': - case 'e': - case 'E': - case 'f': - spec.possible_sign = TRUE; - /* n . dddddddddddddddddddddddd E +- eeee */ - conv_len += 1 + 1 + MAX (24, spec.precision) + 1 + 1 + 4; - if (may_warn && spec.mod_extra_long) - g_warning (G_GNUC_PRETTY_FUNCTION - "(): unable to handle long double, collecting double only"); -#ifdef HAVE_LONG_DOUBLE -#error need to implement special handling for long double -#endif - u_double.v_double = va_arg (args, double); - /* %f can expand up to all significant digits before '.' (308) */ - if (c == 'f' && - u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047) - { - gint exp = u_double.mpn.biased_exponent; - - exp -= G_IEEE754_DOUBLE_BIAS; - exp = exp * G_LOG_2_BASE_10 + 1; - conv_len += ABS (exp); /* exp can be <0 */ - } - /* some printf() implementations require extra padding for rounding */ - conv_len += 2; - /* we can't really handle locale specific grouping here */ - if (spec.locale_grouping) - conv_len *= 2; - break; - case 'C': - spec.mod_long = TRUE; - /* fall through */ - case 'c': - conv_len += spec.mod_long ? MB_LEN_MAX : 1; - (void) va_arg (args, int); - break; - case 'S': - spec.mod_long = TRUE; - /* fall through */ - case 's': - v_string = va_arg (args, char*); - if (!v_string) - conv_len += 8; /* hold "(null)" */ - else if (spec.seen_precision) - conv_len += spec.precision; - else - conv_len += strlen (v_string); - conv_done = TRUE; - if (spec.mod_long) - { - if (may_warn) - g_warning (G_GNUC_PRETTY_FUNCTION - "(): unable to handle wide char strings"); - len += 1024; /* try adding some safety padding */ - } - break; - case 'P': /* do we actually need this? */ - /* fall through */ - case 'p': - spec.alternate_format = TRUE; - conv_len += 10; - if (honour_longs) - conv_len *= 2; - /* fall through */ - case 'n': - conv_done = TRUE; - (void) va_arg (args, void*); - break; - case 'm': - /* there's not much we can do to be clever */ - v_string = g_strerror (errno); - v_uint = v_string ? strlen (v_string) : 0; - conv_len += MAX (256, v_uint); - break; - - /* handle invalid cases - */ - case '\000': - /* no conversion specification, bad bad */ - conv_len += format - spec_start; - break; - default: - if (may_warn) - g_warning (G_GNUC_PRETTY_FUNCTION - "(): unable to handle `%c' while parsing format", - c); - break; - } - conv_done |= conv_len > 0; - } - while (!conv_done); - /* handle width specifications */ - conv_len = MAX (conv_len, MAX (spec.precision, spec.min_width)); - /* handle flags */ - conv_len += spec.alternate_format ? 2 : 0; - conv_len += (spec.add_space || spec.add_sign || spec.possible_sign); - /* finally done */ - len += conv_len; - } /* else (c == '%') */ - } /* while (*format) */ - - return len; -} - -#endif /* !HAVE_C99_VSNPRINTF */ - - gsize g_printf_string_upper_bound (const gchar *format, va_list args) { -#if HAVE_C99_VSNPRINTF gchar c; - return vsnprintf (&c, 1, format, args) + 1; -#else - return printf_string_upper_bound (format, TRUE, args); -#endif + return _g_vsnprintf (&c, 1, format, args) + 1; } void diff --git a/glib/gprintf.c b/glib/gprintf.c new file mode 100644 index 0000000..7f3b012 --- /dev/null +++ b/glib/gprintf.c @@ -0,0 +1,253 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997, 2002 Peter Mattis, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glib.h" +#include "gprintf.h" +#include "gprintfint.h" + +/** + * g_printf: + * @format: the format string. See the printf() documentation. + * @Varargs: the arguments to insert in the output. + * + * An implementation of the standard printf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_printf (gchar const *fmt, + ...) +{ + va_list args; + gint retval; + + va_start (args, fmt); + retval = g_vprintf (fmt, args); + va_end (args); + + return retval; +} + +/** + * g_fprintf: + * @file: the stream to write to. + * @format: the format string. See the printf() documentation. + * @Varargs: the arguments to insert in the output. + * + * An implementation of the standard fprintf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_fprintf (FILE *file, + gchar const *fmt, + ...) +{ + va_list args; + gint retval; + + va_start (args, fmt); + retval = g_vfprintf (file, fmt, args); + va_end (args); + + return retval; +} + +/** + * g_sprintf: + * @string: the buffer to hold the output. + * @format: the format string. See the printf() documentation. + * @Varargs: the arguments to insert in the output. + * + * An implementation of the standard sprintf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_sprintf (gchar *str, + gchar const *fmt, + ...) +{ + va_list args; + gint retval; + + va_start (args, fmt); + retval = g_vsprintf (str, fmt, args); + va_end (args); + + return retval; +} + +/** + * g_snprintf: + * @string: the buffer to hold the output. + * @n: the maximum number of characters to produce (including the + * terminating nul character). + * @format: the format string. See the printf() documentation. + * @Varargs: the arguments to insert in the output. + * + * A safer form of the standard sprintf() function. The output is guaranteed + * to not exceed @n characters (including the terminating nul character), so + * it is easy to ensure that a buffer overflow cannot occur. + * + * See also g_strdup_printf(). + * + * In versions of GLib prior to 1.2.3, this function may return -1 if the + * output was truncated, and the truncated string may not be nul-terminated. + * In versions prior to 1.3.12, this function returns the length of the output + * string. + * + * The return value of g_snprintf() conforms to the snprintf() + * function as standardized in ISO C99. Note that this is different from + * traditional snprintf(), which returns the length of the output string. + * + * The format string may contain positional parameters, as specified in + * the Single Unix Specification. + * + * Returns: the number of characters which would be produced if the buffer + * was large enough. + **/ +gint +g_snprintf (gchar *str, + gulong n, + gchar const *fmt, + ...) +{ + va_list args; + gint retval; + + va_start (args, fmt); + retval = g_vsnprintf (str, n, fmt, args); + va_end (args); + + return retval; +} + +/** + * g_vprintf: + * @format: the format string. See the printf() documentation. + * @args: the list of arguments to insert in the output. + * + * An implementation of the standard vprintf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_vprintf (gchar const *fmt, + va_list args) +{ + g_return_val_if_fail (fmt != NULL, 0); + + return _g_vprintf (fmt, args); +} + +/** + * g_vfprintf: + * @file: the stream to write to. + * @format: the format string. See the printf() documentation. + * @args: the list of arguments to insert in the output. + * + * An implementation of the standard fprintf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_vfprintf (FILE *file, + gchar const *fmt, + va_list args) +{ + g_return_val_if_fail (fmt != NULL, 0); + + return _g_vfprintf (file, fmt, args); +} + +/** + * g_vsprintf: + * @string: the buffer to hold the output. + * @format: the format string. See the printf() documentation. + * @args: the list of arguments to insert in the output. + * + * An implementation of the standard vsprintf() function which supports + * positional parameters, as specified in the Single Unix Specification. + * + * Returns: the number of characters printed. + **/ +gint +g_vsprintf (gchar *str, + gchar const *fmt, + va_list args) +{ + g_return_val_if_fail (str != NULL, 0); + g_return_val_if_fail (fmt != NULL, 0); + + return _g_vsprintf (str, fmt, args); +} + +/** + * g_vsnprintf: + * @string: the buffer to hold the output. + * @n: the maximum number of characters to produce (including the + * terminating nul character). + * @format: the format string. See the printf() documentation. + * @args: the list of arguments to insert in the output. + * + * A safer form of the standard vsprintf() function. The output is guaranteed + * to not exceed @n characters (including the terminating nul character), so + * it is easy to ensure that a buffer overflow cannot occur. + * + * See also g_strdup_vprintf(). + * + * In versions of GLib prior to 1.2.3, this function may return -1 if the + * output was truncated, and the truncated string may not be nul-terminated. + * In versions prior to 1.3.12, this function returns the length of the output + * string. + * + * The return value of g_vsnprintf() conforms to the vsnprintf() function + * as standardized in ISO C99. Note that this is different from traditional + * vsnprintf(), which returns the length of the output string. + * + * The format string may contain positional parameters, as specified in + * the Single Unix Specification. + * + * Returns: the number of characters which would be produced if the buffer + * was large enough. + */ +gint +g_vsnprintf (gchar *str, + gulong n, + gchar const *fmt, + va_list args) +{ + g_return_val_if_fail (n == 0 || str != NULL, 0); + g_return_val_if_fail (fmt != NULL, 0); + + return _g_vsnprintf (str, n, fmt, args); +} + + + diff --git a/glib/gprintf.h b/glib/gprintf.h new file mode 100644 index 0000000..6fcacc2 --- /dev/null +++ b/glib/gprintf.h @@ -0,0 +1,60 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997, 2002 Peter Mattis, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __G_PRINTF_H__ +#define __G_PRINTF_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +gint g_printf (gchar const *format, + ...) G_GNUC_PRINTF (1, 2); +gint g_fprintf (FILE *file, + gchar const *format, + ...) G_GNUC_PRINTF (2, 3); +gint g_sprintf (gchar *string, + gchar const *format, + ...) G_GNUC_PRINTF (2, 3); +gint g_snprintf (gchar *string, + gulong n, + gchar const *format, + ...) G_GNUC_PRINTF (3, 4); + +gint g_vprintf (gchar const *format, + va_list args); +gint g_vfprintf (FILE *file, + gchar const *format, + va_list args); +gint g_vsprintf (gchar *string, + gchar const *format, + va_list args); +gint g_vsnprintf (gchar *string, + gulong n, + gchar const *format, + va_list args); + + +G_END_DECLS + +#endif /* __G_PRINTF_H__ */ + + + diff --git a/glib/gprintfint.h b/glib/gprintfint.h new file mode 100644 index 0000000..05535b4 --- /dev/null +++ b/glib/gprintfint.h @@ -0,0 +1,63 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 2002. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_PRINTFINT_H__ +#define __G_PRINTFINT_H__ + +#ifdef HAVE_GOOD_PRINTF + +#define _g_printf printf +#define _g_fprintf fprintf +#define _g_sprintf sprintf +#define _g_snprintf snprintf + +#define _g_vprintf vprintf +#define _g_vfprintf vfprintf +#define _g_vsprintf vsprintf +#define _g_vsnprintf vsnprintf + +#define _g_vasprintf vasprintf + +#else + +#include "trio/trio.h" + +#define _g_printf trio_printf +#define _g_fprintf trio_fprintf +#define _g_sprintf trio_sprintf +#define _g_snprintf trio_snprintf + +#define _g_vprintf trio_vprintf +#define _g_vfprintf trio_vfprintf +#define _g_vsprintf trio_vsprintf +#define _g_vsnprintf trio_vsnprintf + +#define _g_vasprintf trio_vasprintf + +#endif + +#endif /* __G_PRINTF_H__ */ + diff --git a/glib/gscanner.c b/glib/gscanner.c index 8055abf..d1fb245 100644 --- a/glib/gscanner.c +++ b/glib/gscanner.c @@ -40,6 +40,7 @@ #include #include #include "glib.h" +#include "gprintfint.h" #ifdef HAVE_UNISTD_H #include #endif @@ -290,12 +291,12 @@ g_scanner_msg_handler (GScanner *scanner, { g_return_if_fail (scanner != NULL); - fprintf (stderr, "%s:%d: ", - scanner->input_name ? scanner->input_name : "", - scanner->line); + _g_fprintf (stderr, "%s:%d: ", + scanner->input_name ? scanner->input_name : "", + scanner->line); if (is_error) - fprintf (stderr, "error: "); - fprintf (stderr, "%s\n", message); + _g_fprintf (stderr, "error: "); + _g_fprintf (stderr, "%s\n", message); } void @@ -856,7 +857,7 @@ g_scanner_unexp_token (GScanner *scanner, switch (scanner->token) { case G_TOKEN_EOF: - g_snprintf (token_string, token_string_len, "end of file"); + _g_snprintf (token_string, token_string_len, "end of file"); break; default: @@ -865,14 +866,14 @@ g_scanner_unexp_token (GScanner *scanner, if ((scanner->token >= ' ' && scanner->token <= '~') || strchr (scanner->config->cset_identifier_first, scanner->token) || strchr (scanner->config->cset_identifier_nth, scanner->token)) - g_snprintf (token_string, token_string_len, "character `%c'", scanner->token); + _g_snprintf (token_string, token_string_len, "character `%c'", scanner->token); else - g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token); + _g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token); break; } else if (!scanner->config->symbol_2_token) { - g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token); + _g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token); break; } /* fall through */ @@ -882,18 +883,18 @@ g_scanner_unexp_token (GScanner *scanner, expected_token > G_TOKEN_LAST)) print_unexp = FALSE; if (symbol_name) - g_snprintf (token_string, - token_string_len, - "%s%s `%s'", - print_unexp ? "" : "invalid ", - symbol_spec, - symbol_name); + _g_snprintf (token_string, + token_string_len, + "%s%s `%s'", + print_unexp ? "" : "invalid ", + symbol_spec, + symbol_name); else - g_snprintf (token_string, - token_string_len, - "%s%s", - print_unexp ? "" : "invalid ", - symbol_spec); + _g_snprintf (token_string, + token_string_len, + "%s%s", + print_unexp ? "" : "invalid ", + symbol_spec); break; case G_TOKEN_ERROR: @@ -902,42 +903,42 @@ g_scanner_unexp_token (GScanner *scanner, switch (scanner->value.v_error) { case G_ERR_UNEXP_EOF: - g_snprintf (token_string, token_string_len, "scanner: unexpected end of file"); + _g_snprintf (token_string, token_string_len, "scanner: unexpected end of file"); break; case G_ERR_UNEXP_EOF_IN_STRING: - g_snprintf (token_string, token_string_len, "scanner: unterminated string constant"); + _g_snprintf (token_string, token_string_len, "scanner: unterminated string constant"); break; case G_ERR_UNEXP_EOF_IN_COMMENT: - g_snprintf (token_string, token_string_len, "scanner: unterminated comment"); + _g_snprintf (token_string, token_string_len, "scanner: unterminated comment"); break; case G_ERR_NON_DIGIT_IN_CONST: - g_snprintf (token_string, token_string_len, "scanner: non digit in constant"); + _g_snprintf (token_string, token_string_len, "scanner: non digit in constant"); break; case G_ERR_FLOAT_RADIX: - g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant"); + _g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant"); break; case G_ERR_FLOAT_MALFORMED: - g_snprintf (token_string, token_string_len, "scanner: malformed floating constant"); + _g_snprintf (token_string, token_string_len, "scanner: malformed floating constant"); break; case G_ERR_DIGIT_RADIX: - g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix"); + _g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix"); break; case G_ERR_UNKNOWN: default: - g_snprintf (token_string, token_string_len, "scanner: unknown error"); + _g_snprintf (token_string, token_string_len, "scanner: unknown error"); break; } break; case G_TOKEN_CHAR: - g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char); + _g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char); break; case G_TOKEN_IDENTIFIER: @@ -945,7 +946,7 @@ g_scanner_unexp_token (GScanner *scanner, if (expected_token == G_TOKEN_IDENTIFIER || expected_token == G_TOKEN_IDENTIFIER_NULL) print_unexp = FALSE; - g_snprintf (token_string, + _g_snprintf (token_string, token_string_len, "%s%s `%s'", print_unexp ? "" : "invalid ", @@ -958,31 +959,31 @@ g_scanner_unexp_token (GScanner *scanner, case G_TOKEN_INT: case G_TOKEN_HEX: if (scanner->config->store_int64) - g_snprintf (token_string, token_string_len, "number `%llu'", scanner->value.v_int64); + _g_snprintf (token_string, token_string_len, "number `%llu'", scanner->value.v_int64); else - g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int); + _g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int); break; case G_TOKEN_FLOAT: - g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float); + _g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float); break; case G_TOKEN_STRING: if (expected_token == G_TOKEN_STRING) print_unexp = FALSE; - g_snprintf (token_string, - token_string_len, - "%s%sstring constant \"%s\"", - print_unexp ? "" : "invalid ", - scanner->value.v_string[0] == 0 ? "empty " : "", - scanner->value.v_string); + _g_snprintf (token_string, + token_string_len, + "%s%sstring constant \"%s\"", + print_unexp ? "" : "invalid ", + scanner->value.v_string[0] == 0 ? "empty " : "", + scanner->value.v_string); token_string[token_string_len - 2] = '"'; token_string[token_string_len - 1] = 0; break; case G_TOKEN_COMMENT_SINGLE: case G_TOKEN_COMMENT_MULTI: - g_snprintf (token_string, token_string_len, "comment"); + _g_snprintf (token_string, token_string_len, "comment"); break; case G_TOKEN_NONE: @@ -1002,7 +1003,7 @@ g_scanner_unexp_token (GScanner *scanner, gboolean need_valid; gchar *tstring; case G_TOKEN_EOF: - g_snprintf (expected_string, expected_string_len, "end of file"); + _g_snprintf (expected_string, expected_string_len, "end of file"); break; default: if (expected_token >= 1 && expected_token <= 255) @@ -1010,14 +1011,14 @@ g_scanner_unexp_token (GScanner *scanner, if ((expected_token >= ' ' && expected_token <= '~') || strchr (scanner->config->cset_identifier_first, expected_token) || strchr (scanner->config->cset_identifier_nth, expected_token)) - g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token); + _g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token); else - g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token); + _g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token); break; } else if (!scanner->config->symbol_2_token) { - g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token); + _g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token); break; } /* fall through */ @@ -1025,67 +1026,67 @@ g_scanner_unexp_token (GScanner *scanner, need_valid = (scanner->token == G_TOKEN_SYMBOL || (scanner->config->symbol_2_token && scanner->token > G_TOKEN_LAST)); - g_snprintf (expected_string, - expected_string_len, - "%s%s", - need_valid ? "valid " : "", - symbol_spec); + _g_snprintf (expected_string, + expected_string_len, + "%s%s", + need_valid ? "valid " : "", + symbol_spec); /* FIXME: should we attempt to lookup the symbol_name for symbol_2_token? */ break; case G_TOKEN_CHAR: - g_snprintf (expected_string, expected_string_len, "%scharacter", - scanner->token == G_TOKEN_CHAR ? "valid " : ""); + _g_snprintf (expected_string, expected_string_len, "%scharacter", + scanner->token == G_TOKEN_CHAR ? "valid " : ""); break; case G_TOKEN_BINARY: tstring = "binary"; - g_snprintf (expected_string, expected_string_len, "%snumber (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_OCTAL: tstring = "octal"; - g_snprintf (expected_string, expected_string_len, "%snumber (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_INT: tstring = "integer"; - g_snprintf (expected_string, expected_string_len, "%snumber (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_HEX: tstring = "hexadecimal"; - g_snprintf (expected_string, expected_string_len, "%snumber (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_FLOAT: tstring = "float"; - g_snprintf (expected_string, expected_string_len, "%snumber (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_STRING: - g_snprintf (expected_string, - expected_string_len, - "%sstring constant", - scanner->token == G_TOKEN_STRING ? "valid " : ""); + _g_snprintf (expected_string, + expected_string_len, + "%sstring constant", + scanner->token == G_TOKEN_STRING ? "valid " : ""); break; case G_TOKEN_IDENTIFIER: case G_TOKEN_IDENTIFIER_NULL: need_valid = (scanner->token == G_TOKEN_IDENTIFIER_NULL || scanner->token == G_TOKEN_IDENTIFIER); - g_snprintf (expected_string, - expected_string_len, - "%s%s", - need_valid ? "valid " : "", - identifier_spec); + _g_snprintf (expected_string, + expected_string_len, + "%s%s", + need_valid ? "valid " : "", + identifier_spec); break; case G_TOKEN_COMMENT_SINGLE: tstring = "single-line"; - g_snprintf (expected_string, expected_string_len, "%scomment (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%scomment (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_COMMENT_MULTI: tstring = "multi-line"; - g_snprintf (expected_string, expected_string_len, "%scomment (%s)", - scanner->token == expected_token ? "valid " : "", tstring); + _g_snprintf (expected_string, expected_string_len, "%scomment (%s)", + scanner->token == expected_token ? "valid " : "", tstring); break; case G_TOKEN_NONE: case G_TOKEN_ERROR: diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c index 8222fef..3950063 100644 --- a/glib/gspawn-win32.c +++ b/glib/gspawn-win32.c @@ -40,6 +40,7 @@ #include #include "glib.h" +#include "gprintfint.h" #include #include @@ -555,12 +556,12 @@ do_spawn (gboolean dont_wait, new_argv = g_new (gchar *, argc + 1 + ARG_COUNT); new_argv[0] = "gspawn-win32-helper"; - sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd); + _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd); new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT]; if (stdin_fd >= 0) { - sprintf (args[ARG_STDIN], "%d", stdin_fd); + _g_sprintf (args[ARG_STDIN], "%d", stdin_fd); new_argv[ARG_STDIN] = args[ARG_STDIN]; } else if (child_inherits_stdin) @@ -576,7 +577,7 @@ do_spawn (gboolean dont_wait, if (stdout_fd >= 0) { - sprintf (args[ARG_STDOUT], "%d", stdout_fd); + _g_sprintf (args[ARG_STDOUT], "%d", stdout_fd); new_argv[ARG_STDOUT] = args[ARG_STDOUT]; } else if (stdout_to_null) @@ -590,7 +591,7 @@ do_spawn (gboolean dont_wait, if (stderr_fd >= 0) { - sprintf (args[ARG_STDERR], "%d", stderr_fd); + _g_sprintf (args[ARG_STDERR], "%d", stderr_fd); new_argv[ARG_STDERR] = args[ARG_STDERR]; } else if (stderr_to_null) diff --git a/glib/gstrfuncs.c b/glib/gstrfuncs.c index ebe9981..579ef0f 100644 --- a/glib/gstrfuncs.c +++ b/glib/gstrfuncs.c @@ -45,6 +45,7 @@ #include #endif #include "glib.h" +#include "gprintfint.h" #ifdef G_OS_WIN32 #include @@ -183,7 +184,7 @@ g_strdup_vprintf (const gchar *format, { gchar *buffer; #ifdef HAVE_VASPRINTF - if (vasprintf (&buffer, format, args1) < 0) + if (_g_vasprintf (&buffer, format, args1) < 0) buffer = NULL; else if (!g_mem_is_system_malloc ()) { @@ -198,7 +199,7 @@ g_strdup_vprintf (const gchar *format, buffer = g_new (gchar, g_printf_string_upper_bound (format, args1)); - vsprintf (buffer, format, args2); + _g_vsprintf (buffer, format, args2); va_end (args2); #endif return buffer; @@ -544,7 +545,7 @@ g_ascii_formatd (gchar *buffer, return NULL; - g_snprintf (buffer, buf_len, format, d); + _g_snprintf (buffer, buf_len, format, d); locale_data = localeconv (); decimal_point = locale_data->decimal_point; @@ -1187,7 +1188,7 @@ g_strerror (gint errnum) g_static_private_set (&msg_private, msg, g_free); } - sprintf (msg, "unknown error (%d)", errnum); + _g_sprintf (msg, "unknown error (%d)", errnum); return msg; } @@ -1336,7 +1337,7 @@ extern const char *strsignal(int); g_static_private_set (&msg_private, msg, g_free); } - sprintf (msg, "unknown signal (%d)", signum); + _g_sprintf (msg, "unknown signal (%d)", signum); return msg; } diff --git a/glib/gutils.c b/glib/gutils.c index 692d387..344947d 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -53,6 +53,7 @@ #define G_IMPLEMENT_INLINES 1 #define __G_UTILS_C__ #include "glib.h" +#include "gprintfint.h" #ifdef MAXPATHLEN #define G_PATH_LENGTH MAXPATHLEN @@ -340,54 +341,6 @@ g_find_program_in_path (const gchar *program) return NULL; } -gint -g_snprintf (gchar *str, - gulong n, - gchar const *fmt, - ...) -{ - va_list args; - gint retval; - - va_start (args, fmt); - retval = g_vsnprintf (str, n, fmt, args); - va_end (args); - - return retval; -} - -gint -g_vsnprintf (gchar *str, - gulong n, - gchar const *fmt, - va_list args) -{ -#ifdef HAVE_VSNPRINTF_C99 - g_return_val_if_fail (n == 0 || str != NULL, 0); - g_return_val_if_fail (fmt != NULL, 0); - - return vsnprintf (str, n, fmt, args); -#else /* !HAVE_VSNPRINTF_C99 */ - gchar *printed; - gint retval; - - g_return_val_if_fail (n == 0 || str != NULL, 0); - g_return_val_if_fail (fmt != NULL, 0); - - printed = g_strdup_vprintf (fmt, args); - retval = strlen (printed); - if (n > 0) - { - strncpy (str, printed, n - 1); - str[n-1] = '\0'; - } - - g_free (printed); - - return retval; -#endif /* !HAVE_VSNPRINTF_C99 */ -} - guint g_parse_debug_string (const gchar *string, const GDebugKey *keys, diff --git a/glib/trio/Makefile.am b/glib/trio/Makefile.am new file mode 100644 index 0000000..29c6980 --- /dev/null +++ b/glib/trio/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in + +noinst_LTLIBRARIES = libtrio.la + +libtrio_la_SOURCES = \ + trio.c \ + trionan.c \ + triostr.c \ + trio.h \ + triodef.h \ + trionan.h \ + triop.h \ + triostr.h + +INCLUDES = -DTRIO_EXTENSION=0 + +EXTRA_DIST = README diff --git a/glib/trio/README b/glib/trio/README new file mode 100644 index 0000000..19f9355 --- /dev/null +++ b/glib/trio/README @@ -0,0 +1,27 @@ +The sources are derived from the trio package, version 1.9, by +Bjorn Reese and Daniel Stenberg, which can be found at: + + http://daniel.haxx.se/trio/ + http://sourceforge.net/projects/ctrio/ + +The license on the files taken from the trio package is reproduced +below. + +Matthias Clasen +20 November 2002 + +==== + + Copyright (C) 1998-2001 by Bjorn Reese and Daniel Stenberg. + + 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. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + + + diff --git a/glib/trio/trio.c b/glib/trio/trio.c new file mode 100644 index 0000000..7593e58 --- /dev/null +++ b/glib/trio/trio.c @@ -0,0 +1,6760 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************* + * + * A note to trio contributors: + * + * Avoid heap allocation at all costs to ensure that the trio functions + * are async-safe. The exceptions are the printf/fprintf functions, which + * uses fputc, and the asprintf functions and the modifier, which + * by design are required to allocate form the heap. + * + ************************************************************************/ + +/* + * TODO: + * - Scan is probably too permissive about its modifiers. + * - C escapes in %#[] ? + * - Multibyte characters (done for format parsing, except scan groups) + * - Complex numbers? (C99 _Complex) + * - Boolean values? (C99 _Bool) + * - C99 NaN(n-char-sequence) missing + * - Should we support the GNU %a alloc modifier? GNU has an ugly hack + * for %a, because C99 used %a for other purposes. If specified as + * %as or %a[ it is interpreted as the alloc modifier, otherwise as + * the C99 hex-float. This means that you cannot scan %as as a hex-float + * immediately followed by an 's'. + * - Scanning of collating symbols. + */ + +/************************************************************************* + * Trio include files + */ +#include "triodef.h" +#include "trio.h" +#include "triop.h" +#include "trionan.h" +#if !defined(TRIO_MINIMAL) +# include "triostr.h" +#endif + +/************************************************************************** + * + * Definitions + * + *************************************************************************/ + +#if defined(__STDC_ISO_10646__) || defined(MB_LEN_MAX) || defined(USE_MULTIBYTE) || TRIO_WIDECHAR +# define TRIO_COMPILER_SUPPORTS_MULTIBYTE +# if !defined(MB_LEN_MAX) +# define MB_LEN_MAX 6 +# endif +#endif + +/************************************************************************* + * Generic definitions + */ + +#if !(defined(DEBUG) || defined(NDEBUG)) +# define NDEBUG +#endif +#include +#include +#if !defined(TRIO_COMPILER_SUPPORTS_C99) +# define isblank(x) (((x)==32) || ((x)==9)) +#endif +#include +#include +#include +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif +#include +#include + +#ifndef NULL +# define NULL 0 +#endif +#define NIL ((char)0) +#ifndef FALSE +# define FALSE (1 == 0) +# define TRUE (! FALSE) +#endif +#define BOOLEAN_T int + +/* mincore() can be used for debugging purposes */ +#define VALID(x) (NULL != (x)) + +#if TRIO_ERRORS + /* + * Encode the error code and the position. This is decoded + * with TRIO_ERROR_CODE and TRIO_ERROR_POSITION. + */ +# define TRIO_ERROR_RETURN(x,y) (- ((x) + ((y) << 8))) +#else +# define TRIO_ERROR_RETURN(x,y) (-1) +#endif + + +/************************************************************************* + * Platform specific definitions + */ +#if defined(TRIO_PLATFORM_UNIX) +# include +# include +# include +# define USE_LOCALE +#endif /* TRIO_PLATFORM_UNIX */ +#if defined(TRIO_PLATFORM_VMS) +# include +#endif +#if defined(TRIO_PLATFORM_WIN32) +# include +# define read _read +# define write _write +#endif /* TRIO_PLATFORM_WIN32 */ + +#define TRIO_MSVC_VERSION_5 1100 + +#if TRIO_WIDECHAR +# if defined(TRIO_COMPILER_SUPPORTS_ISO94) +# include +# include +typedef wchar_t trio_wchar_t; +typedef wint_t trio_wint_t; +# else +typedef char trio_wchar_t; +typedef int trio_wint_t; +# define WCONST(x) L ## x +# define WEOF EOF +# define iswalnum(x) isalnum(x) +# define iswalpha(x) isalpha(x) +# define iswblank(x) isblank(x) +# define iswcntrl(x) iscntrl(x) +# define iswdigit(x) isdigit(x) +# define iswgraph(x) isgraph(x) +# define iswlower(x) islower(x) +# define iswprint(x) isprint(x) +# define iswpunct(x) ispunct(x) +# define iswspace(x) isspace(x) +# define iswupper(x) isupper(x) +# define iswxdigit(x) isxdigit(x) +# endif +#endif + + +/************************************************************************* + * Compiler dependent definitions + */ + +/* Support for long long */ +#ifndef __cplusplus +# if !defined(USE_LONGLONG) +# if defined(TRIO_COMPILER_GCC) && !defined(__STRICT_ANSI__) +# define USE_LONGLONG +# elif defined(TRIO_COMPILER_SUNPRO) +# define USE_LONGLONG +# elif defined(_LONG_LONG) || defined(_LONGLONG) +# define USE_LONGLONG +# endif +# endif +#endif + +/* The extra long numbers */ +#if defined(USE_LONGLONG) +typedef signed long long int trio_longlong_t; +typedef unsigned long long int trio_ulonglong_t; +#elif defined(TRIO_COMPILER_MSVC) +# if (_MSC_VER >= TRIO_MSVC_VERSION_5) +typedef signed __int64 trio_longlong_t; +typedef unsigned __int64 trio_ulonglong_t; +# else +typedef signed long int trio_longlong_t; +typedef unsigned long int trio_ulonglong_t; +# endif +#else +typedef TRIO_SIGNED long int trio_longlong_t; +typedef unsigned long int trio_ulonglong_t; +#endif + +/* Maximal and fixed integer types */ +#if defined(TRIO_COMPILER_SUPPORTS_C99) +# include +typedef intmax_t trio_intmax_t; +typedef uintmax_t trio_uintmax_t; +typedef int8_t trio_int8_t; +typedef int16_t trio_int16_t; +typedef int32_t trio_int32_t; +typedef int64_t trio_int64_t; +#elif defined(TRIO_COMPILER_SUPPORTS_UNIX98) +# include +typedef intmax_t trio_intmax_t; +typedef uintmax_t trio_uintmax_t; +typedef int8_t trio_int8_t; +typedef int16_t trio_int16_t; +typedef int32_t trio_int32_t; +typedef int64_t trio_int64_t; +#elif defined(TRIO_COMPILER_MSVC) && (_MSC_VER >= TRIO_MSVC_VERSION_5) +typedef trio_longlong_t trio_intmax_t; +typedef trio_ulonglong_t trio_uintmax_t; +typedef __int8 trio_int8_t; +typedef __int16 trio_int16_t; +typedef __int32 trio_int32_t; +typedef __int64 trio_int64_t; +#else +typedef trio_longlong_t trio_intmax_t; +typedef trio_ulonglong_t trio_uintmax_t; +# if defined(TRIO_INT8_T) +typedef TRIO_INT8_T trio_int8_t; +# else +typedef TRIO_SIGNED char trio_int8_t; +# endif +# if defined(TRIO_INT16_T) +typedef TRIO_INT16_T trio_int16_t; +# else +typedef TRIO_SIGNED short trio_int16_t; +# endif +# if defined(TRIO_INT32_T) +typedef TRIO_INT32_T trio_int32_t; +# else +typedef TRIO_SIGNED int trio_int32_t; +# endif +# if defined(TRIO_INT64_T) +typedef TRIO_INT64_T trio_int64_t; +# else +typedef trio_longlong_t trio_int64_t; +# endif +#endif + +#if !(defined(TRIO_COMPILER_SUPPORTS_C99) \ + || defined(TRIO_COMPILER_SUPPORTS_UNIX01)) +# define floorl(x) floor((double)(x)) +# define fmodl(x,y) fmod((double)(x),(double)(y)) +# define powl(x,y) pow((double)(x),(double)(y)) +#endif + +#define TRIO_FABS(x) (((x) < 0.0) ? -(x) : (x)) + +/************************************************************************* + * Internal Definitions + */ + +#ifndef DECIMAL_DIG +# define DECIMAL_DIG DBL_DIG +#endif + +/* Long double sizes */ +#ifdef LDBL_DIG +# define MAX_MANTISSA_DIGITS LDBL_DIG +# define MAX_EXPONENT_DIGITS 4 +# define MAX_DOUBLE_DIGITS LDBL_MAX_10_EXP +#else +# define MAX_MANTISSA_DIGITS DECIMAL_DIG +# define MAX_EXPONENT_DIGITS 3 +# define MAX_DOUBLE_DIGITS DBL_MAX_10_EXP +#endif + +#if defined(TRIO_COMPILER_ANCIENT) || !defined(LDBL_DIG) +# undef LDBL_DIG +# undef LDBL_MANT_DIG +# undef LDBL_EPSILON +# define LDBL_DIG DBL_DIG +# define LDBL_MANT_DIG DBL_MANT_DIG +# define LDBL_EPSILON DBL_EPSILON +#endif + +/* The maximal number of digits is for base 2 */ +#define MAX_CHARS_IN(x) (sizeof(x) * CHAR_BIT) +/* The width of a pointer. The number of bits in a hex digit is 4 */ +#define POINTER_WIDTH ((sizeof("0x") - 1) + sizeof(trio_pointer_t) * CHAR_BIT / 4) + +/* Infinite and Not-A-Number for floating-point */ +#define INFINITE_LOWER "inf" +#define INFINITE_UPPER "INF" +#define LONG_INFINITE_LOWER "infinite" +#define LONG_INFINITE_UPPER "INFINITE" +#define NAN_LOWER "nan" +#define NAN_UPPER "NAN" + +/* Various constants */ +enum { + TYPE_PRINT = 1, + TYPE_SCAN = 2, + + /* Flags. Use maximum 32 */ + FLAGS_NEW = 0, + FLAGS_STICKY = 1, + FLAGS_SPACE = 2 * FLAGS_STICKY, + FLAGS_SHOWSIGN = 2 * FLAGS_SPACE, + FLAGS_LEFTADJUST = 2 * FLAGS_SHOWSIGN, + FLAGS_ALTERNATIVE = 2 * FLAGS_LEFTADJUST, + FLAGS_SHORT = 2 * FLAGS_ALTERNATIVE, + FLAGS_SHORTSHORT = 2 * FLAGS_SHORT, + FLAGS_LONG = 2 * FLAGS_SHORTSHORT, + FLAGS_QUAD = 2 * FLAGS_LONG, + FLAGS_LONGDOUBLE = 2 * FLAGS_QUAD, + FLAGS_SIZE_T = 2 * FLAGS_LONGDOUBLE, + FLAGS_PTRDIFF_T = 2 * FLAGS_SIZE_T, + FLAGS_INTMAX_T = 2 * FLAGS_PTRDIFF_T, + FLAGS_NILPADDING = 2 * FLAGS_INTMAX_T, + FLAGS_UNSIGNED = 2 * FLAGS_NILPADDING, + FLAGS_UPPER = 2 * FLAGS_UNSIGNED, + FLAGS_WIDTH = 2 * FLAGS_UPPER, + FLAGS_WIDTH_PARAMETER = 2 * FLAGS_WIDTH, + FLAGS_PRECISION = 2 * FLAGS_WIDTH_PARAMETER, + FLAGS_PRECISION_PARAMETER = 2 * FLAGS_PRECISION, + FLAGS_BASE = 2 * FLAGS_PRECISION_PARAMETER, + FLAGS_BASE_PARAMETER = 2 * FLAGS_BASE, + FLAGS_FLOAT_E = 2 * FLAGS_BASE_PARAMETER, + FLAGS_FLOAT_G = 2 * FLAGS_FLOAT_E, + FLAGS_QUOTE = 2 * FLAGS_FLOAT_G, + FLAGS_WIDECHAR = 2 * FLAGS_QUOTE, + FLAGS_ALLOC = 2 * FLAGS_WIDECHAR, + FLAGS_IGNORE = 2 * FLAGS_ALLOC, + FLAGS_IGNORE_PARAMETER = 2 * FLAGS_IGNORE, + FLAGS_VARSIZE_PARAMETER = 2 * FLAGS_IGNORE_PARAMETER, + FLAGS_FIXED_SIZE = 2 * FLAGS_VARSIZE_PARAMETER, + /* Reused flags */ + FLAGS_EXCLUDE = FLAGS_SHORT, + FLAGS_USER_DEFINED = FLAGS_IGNORE, + FLAGS_ROUNDING = FLAGS_INTMAX_T, + /* Compounded flags */ + FLAGS_ALL_VARSIZES = FLAGS_LONG | FLAGS_QUAD | FLAGS_INTMAX_T | FLAGS_PTRDIFF_T | FLAGS_SIZE_T, + FLAGS_ALL_SIZES = FLAGS_ALL_VARSIZES | FLAGS_SHORTSHORT | FLAGS_SHORT, + + NO_POSITION = -1, + NO_WIDTH = 0, + NO_PRECISION = -1, + NO_SIZE = -1, + + /* Do not change these */ + NO_BASE = -1, + MIN_BASE = 2, + MAX_BASE = 36, + BASE_BINARY = 2, + BASE_OCTAL = 8, + BASE_DECIMAL = 10, + BASE_HEX = 16, + + /* Maximal number of allowed parameters */ + MAX_PARAMETERS = 64, + /* Maximal number of characters in class */ + MAX_CHARACTER_CLASS = UCHAR_MAX + 1, + + /* Maximal string lengths for user-defined specifiers */ + MAX_USER_NAME = 64, + MAX_USER_DATA = 256, + + /* Maximal length of locale separator strings */ + MAX_LOCALE_SEPARATOR_LENGTH = MB_LEN_MAX, + /* Maximal number of integers in grouping */ + MAX_LOCALE_GROUPS = 64, + + /* Initial size of asprintf buffer */ + DYNAMIC_START_SIZE = 32 +}; + +#define NO_GROUPING ((int)CHAR_MAX) + +/* Fundamental formatting parameter types */ +#define FORMAT_UNKNOWN 0 +#define FORMAT_INT 1 +#define FORMAT_DOUBLE 2 +#define FORMAT_CHAR 3 +#define FORMAT_STRING 4 +#define FORMAT_POINTER 5 +#define FORMAT_COUNT 6 +#define FORMAT_PARAMETER 7 +#define FORMAT_GROUP 8 +#if TRIO_GNU +# define FORMAT_ERRNO 9 +#endif +#if TRIO_EXTENSION +# define FORMAT_USER_DEFINED 10 +#endif + +/* Character constants */ +#define CHAR_IDENTIFIER '%' +#define CHAR_BACKSLASH '\\' +#define CHAR_QUOTE '\"' +#define CHAR_ADJUST ' ' + +/* Character class expressions */ +#define CLASS_ALNUM "[:alnum:]" +#define CLASS_ALPHA "[:alpha:]" +#define CLASS_BLANK "[:blank:]" +#define CLASS_CNTRL "[:cntrl:]" +#define CLASS_DIGIT "[:digit:]" +#define CLASS_GRAPH "[:graph:]" +#define CLASS_LOWER "[:lower:]" +#define CLASS_PRINT "[:print:]" +#define CLASS_PUNCT "[:punct:]" +#define CLASS_SPACE "[:space:]" +#define CLASS_UPPER "[:upper:]" +#define CLASS_XDIGIT "[:xdigit:]" + +/* + * SPECIFIERS: + * + * + * a Hex-float + * A Hex-float + * c Character + * C Widechar character (wint_t) + * d Decimal + * e Float + * E Float + * F Float + * F Float + * g Float + * G Float + * i Integer + * m Error message + * n Count + * o Octal + * p Pointer + * s String + * S Widechar string (wchar_t *) + * u Unsigned + * x Hex + * X Hex + * [] Group + * <> User-defined + * + * Reserved: + * + * D Binary Coded Decimal %D(length,precision) (OS/390) + */ +#define SPECIFIER_CHAR 'c' +#define SPECIFIER_STRING 's' +#define SPECIFIER_DECIMAL 'd' +#define SPECIFIER_INTEGER 'i' +#define SPECIFIER_UNSIGNED 'u' +#define SPECIFIER_OCTAL 'o' +#define SPECIFIER_HEX 'x' +#define SPECIFIER_HEX_UPPER 'X' +#define SPECIFIER_FLOAT_E 'e' +#define SPECIFIER_FLOAT_E_UPPER 'E' +#define SPECIFIER_FLOAT_F 'f' +#define SPECIFIER_FLOAT_F_UPPER 'F' +#define SPECIFIER_FLOAT_G 'g' +#define SPECIFIER_FLOAT_G_UPPER 'G' +#define SPECIFIER_POINTER 'p' +#define SPECIFIER_GROUP '[' +#define SPECIFIER_UNGROUP ']' +#define SPECIFIER_COUNT 'n' +#if TRIO_UNIX98 +# define SPECIFIER_CHAR_UPPER 'C' +# define SPECIFIER_STRING_UPPER 'S' +#endif +#if TRIO_C99 +# define SPECIFIER_HEXFLOAT 'a' +# define SPECIFIER_HEXFLOAT_UPPER 'A' +#endif +#if TRIO_GNU +# define SPECIFIER_ERRNO 'm' +#endif +#if TRIO_EXTENSION +# define SPECIFIER_BINARY 'b' +# define SPECIFIER_BINARY_UPPER 'B' +# define SPECIFIER_USER_DEFINED_BEGIN '<' +# define SPECIFIER_USER_DEFINED_END '>' +# define SPECIFIER_USER_DEFINED_SEPARATOR ':' +#endif + +/* + * QUALIFIERS: + * + * + * Numbers = d,i,o,u,x,X + * Float = a,A,e,E,f,F,g,G + * String = s + * Char = c + * + * + * 9$ Position + * Use the 9th parameter. 9 can be any number between 1 and + * the maximal argument + * + * 9 Width + * Set width to 9. 9 can be any number, but must not be postfixed + * by '$' + * + * h Short + * Numbers: + * (unsigned) short int + * + * hh Short short + * Numbers: + * (unsigned) char + * + * l Long + * Numbers: + * (unsigned) long int + * String: + * as the S specifier + * Char: + * as the C specifier + * + * ll Long Long + * Numbers: + * (unsigned) long long int + * + * L Long Double + * Float + * long double + * + * # Alternative + * Float: + * Decimal-point is always present + * String: + * non-printable characters are handled as \number + * + * Spacing + * + * + Sign + * + * - Alignment + * + * . Precision + * + * * Parameter + * print: use parameter + * scan: no parameter (ignore) + * + * q Quad + * + * Z size_t + * + * w Widechar + * + * ' Thousands/quote + * Numbers: + * Integer part grouped in thousands + * Binary numbers: + * Number grouped in nibbles (4 bits) + * String: + * Quoted string + * + * j intmax_t + * t prtdiff_t + * z size_t + * + * ! Sticky + * @ Parameter (for both print and scan) + * + * I n-bit Integer + * Numbers: + * The following options exists + * I8 = 8-bit integer + * I16 = 16-bit integer + * I32 = 32-bit integer + * I64 = 64-bit integer + */ +#define QUALIFIER_POSITION '$' +#define QUALIFIER_SHORT 'h' +#define QUALIFIER_LONG 'l' +#define QUALIFIER_LONG_UPPER 'L' +#define QUALIFIER_ALTERNATIVE '#' +#define QUALIFIER_SPACE ' ' +#define QUALIFIER_PLUS '+' +#define QUALIFIER_MINUS '-' +#define QUALIFIER_DOT '.' +#define QUALIFIER_STAR '*' +#define QUALIFIER_CIRCUMFLEX '^' /* For scanlists */ +#if TRIO_C99 +# define QUALIFIER_SIZE_T 'z' +# define QUALIFIER_PTRDIFF_T 't' +# define QUALIFIER_INTMAX_T 'j' +#endif +#if TRIO_BSD || TRIO_GNU +# define QUALIFIER_QUAD 'q' +#endif +#if TRIO_GNU +# define QUALIFIER_SIZE_T_UPPER 'Z' +#endif +#if TRIO_MISC +# define QUALIFIER_WIDECHAR 'w' +#endif +#if TRIO_MICROSOFT +# define QUALIFIER_FIXED_SIZE 'I' +#endif +#if TRIO_EXTENSION +# define QUALIFIER_QUOTE '\'' +# define QUALIFIER_STICKY '!' +# define QUALIFIER_VARSIZE '&' /* This should remain undocumented */ +# define QUALIFIER_PARAM '@' /* Experimental */ +# define QUALIFIER_COLON ':' /* For scanlists */ +# define QUALIFIER_EQUAL '=' /* For scanlists */ +# define QUALIFIER_ROUNDING_UPPER 'R' +#endif + + +/************************************************************************* + * + * Internal Structures + * + *************************************************************************/ + +/* Parameters */ +typedef struct { + /* An indication of which entry in the data union is used */ + int type; + /* The flags */ + unsigned long flags; + /* The width qualifier */ + int width; + /* The precision qualifier */ + int precision; + /* The base qualifier */ + int base; + /* The size for the variable size qualifier */ + int varsize; + /* The marker of the end of the specifier */ + int indexAfterSpecifier; + /* The data from the argument list */ + union { + char *string; +#if TRIO_WIDECHAR + trio_wchar_t *wstring; +#endif + trio_pointer_t pointer; + union { + trio_intmax_t as_signed; + trio_uintmax_t as_unsigned; + } number; + double doubleNumber; + double *doublePointer; + trio_long_double_t longdoubleNumber; + trio_long_double_t *longdoublePointer; + int errorNumber; + } data; + /* For the user-defined specifier */ + char user_name[MAX_USER_NAME]; + char user_data[MAX_USER_DATA]; +} trio_parameter_t; + +/* Container for customized functions */ +typedef struct { + union { + trio_outstream_t out; + trio_instream_t in; + } stream; + trio_pointer_t closure; +} trio_custom_t; + +/* General trio "class" */ +typedef struct _trio_class_t { + /* + * The function to write characters to a stream. + */ + void (*OutStream) TRIO_PROTO((struct _trio_class_t *, int)); + /* + * The function to read characters from a stream. + */ + void (*InStream) TRIO_PROTO((struct _trio_class_t *, int *)); + /* + * The current location in the stream. + */ + trio_pointer_t location; + /* + * The character currently being processed. + */ + int current; + /* + * The number of characters that would have been written/read + * if there had been sufficient space. + */ + int processed; + /* + * The number of characters that are actually written/read. + * Processed and committed will only differ for the *nprintf + * and *nscanf functions. + */ + int committed; + /* + * The upper limit of characters that may be written/read. + */ + int max; + /* + * The last output error that was detected. + */ + int error; +} trio_class_t; + +/* References (for user-defined callbacks) */ +typedef struct _trio_reference_t { + trio_class_t *data; + trio_parameter_t *parameter; +} trio_reference_t; + +/* Registered entries (for user-defined callbacks) */ +typedef struct _trio_userdef_t { + struct _trio_userdef_t *next; + trio_callback_t callback; + char *name; +} trio_userdef_t; + + +/************************************************************************* + * + * Internal Variables + * + *************************************************************************/ + +static TRIO_CONST char rcsid[] = "@(#)$Id$"; + +/* + * Need this to workaround a parser bug in HP C/iX compiler that fails + * to resolves macro definitions that includes type 'long double', + * e.g: va_arg(arg_ptr, long double) + */ +#if defined(TRIO_PLATFORM_MPEIX) +static TRIO_CONST trio_long_double_t ___dummy_long_double = 0; +#endif + +static TRIO_CONST char internalNullString[] = "(nil)"; + +#if defined(USE_LOCALE) +static struct lconv *internalLocaleValues = NULL; +#endif + +/* + * UNIX98 says "in a locale where the radix character is not defined, + * the radix character defaults to a period (.)" + */ +static int internalDecimalPointLength = 1; +static int internalThousandSeparatorLength = 1; +static char internalDecimalPoint = '.'; +static char internalDecimalPointString[MAX_LOCALE_SEPARATOR_LENGTH + 1] = "."; +static char internalThousandSeparator[MAX_LOCALE_SEPARATOR_LENGTH + 1] = ","; +static char internalGrouping[MAX_LOCALE_GROUPS] = { (char)NO_GROUPING }; + +static TRIO_CONST char internalDigitsLower[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +static TRIO_CONST char internalDigitsUpper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static BOOLEAN_T internalDigitsUnconverted = TRUE; +static int internalDigitArray[128]; +#if TRIO_EXTENSION +static BOOLEAN_T internalCollationUnconverted = TRUE; +static char internalCollationArray[MAX_CHARACTER_CLASS][MAX_CHARACTER_CLASS]; +#endif + +#if TRIO_EXTENSION +static TRIO_VOLATILE trio_callback_t internalEnterCriticalRegion = NULL; +static TRIO_VOLATILE trio_callback_t internalLeaveCriticalRegion = NULL; +static trio_userdef_t *internalUserDef = NULL; +#endif + + +/************************************************************************* + * + * Internal Functions + * + ************************************************************************/ + +#if defined(TRIO_MINIMAL) +# define TRIO_STRING_PUBLIC static +# include "triostr.c" +#endif /* defined(TRIO_MINIMAL) */ + +/************************************************************************* + * TrioIsQualifier + * + * Description: + * Remember to add all new qualifiers to this function. + * QUALIFIER_POSITION must not be added. + */ +TRIO_PRIVATE BOOLEAN_T +TrioIsQualifier +TRIO_ARGS1((character), + TRIO_CONST char character) +{ + /* QUALIFIER_POSITION is not included */ + switch (character) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case QUALIFIER_PLUS: + case QUALIFIER_MINUS: + case QUALIFIER_SPACE: + case QUALIFIER_DOT: + case QUALIFIER_STAR: + case QUALIFIER_ALTERNATIVE: + case QUALIFIER_SHORT: + case QUALIFIER_LONG: + case QUALIFIER_LONG_UPPER: + case QUALIFIER_CIRCUMFLEX: +#if defined(QUALIFIER_SIZE_T) + case QUALIFIER_SIZE_T: +#endif +#if defined(QUALIFIER_PTRDIFF_T) + case QUALIFIER_PTRDIFF_T: +#endif +#if defined(QUALIFIER_INTMAX_T) + case QUALIFIER_INTMAX_T: +#endif +#if defined(QUALIFIER_QUAD) + case QUALIFIER_QUAD: +#endif +#if defined(QUALIFIER_SIZE_T_UPPER) + case QUALIFIER_SIZE_T_UPPER: +#endif +#if defined(QUALIFIER_WIDECHAR) + case QUALIFIER_WIDECHAR: +#endif +#if defined(QUALIFIER_QUOTE) + case QUALIFIER_QUOTE: +#endif +#if defined(QUALIFIER_STICKY) + case QUALIFIER_STICKY: +#endif +#if defined(QUALIFIER_VARSIZE) + case QUALIFIER_VARSIZE: +#endif +#if defined(QUALIFIER_PARAM) + case QUALIFIER_PARAM: +#endif +#if defined(QUALIFIER_FIXED_SIZE) + case QUALIFIER_FIXED_SIZE: +#endif +#if defined(QUALIFIER_ROUNDING_UPPER) + case QUALIFIER_ROUNDING_UPPER: +#endif + return TRUE; + default: + return FALSE; + } +} + +/************************************************************************* + * TrioSetLocale + */ +#if defined(USE_LOCALE) +TRIO_PRIVATE void +TrioSetLocale(TRIO_NOARGS) +{ + internalLocaleValues = (struct lconv *)localeconv(); + if (internalLocaleValues) + { + if ((internalLocaleValues->decimal_point) && + (internalLocaleValues->decimal_point[0] != NIL)) + { + internalDecimalPointLength = trio_length(internalLocaleValues->decimal_point); + if (internalDecimalPointLength == 1) + { + internalDecimalPoint = internalLocaleValues->decimal_point[0]; + } + else + { + internalDecimalPoint = NIL; + trio_copy_max(internalDecimalPointString, + sizeof(internalDecimalPointString), + internalLocaleValues->decimal_point); + } + } + if ((internalLocaleValues->thousands_sep) && + (internalLocaleValues->thousands_sep[0] != NIL)) + { + trio_copy_max(internalThousandSeparator, + sizeof(internalThousandSeparator), + internalLocaleValues->thousands_sep); + internalThousandSeparatorLength = trio_length(internalThousandSeparator); + } + if ((internalLocaleValues->grouping) && + (internalLocaleValues->grouping[0] != NIL)) + { + trio_copy_max(internalGrouping, + sizeof(internalGrouping), + internalLocaleValues->grouping); + } + } +} +#endif /* defined(USE_LOCALE) */ + +TRIO_PRIVATE int +TrioCalcThousandSeparatorLength +TRIO_ARGS1((digits), + int digits) +{ +#if TRIO_EXTENSION + int count = 0; + int step = NO_GROUPING; + char *groupingPointer = internalGrouping; + + while (digits > 0) + { + if (*groupingPointer == CHAR_MAX) + { + /* Disable grouping */ + break; /* while */ + } + else if (*groupingPointer == 0) + { + /* Repeat last group */ + if (step == NO_GROUPING) + { + /* Error in locale */ + break; /* while */ + } + } + else + { + step = *groupingPointer++; + } + if (digits > step) + count += internalThousandSeparatorLength; + digits -= step; + } + return count; +#else + return 0; +#endif +} + +TRIO_PRIVATE BOOLEAN_T +TrioFollowedBySeparator +TRIO_ARGS1((position), + int position) +{ +#if TRIO_EXTENSION + int step = 0; + char *groupingPointer = internalGrouping; + + position--; + if (position == 0) + return FALSE; + while (position > 0) + { + if (*groupingPointer == CHAR_MAX) + { + /* Disable grouping */ + break; /* while */ + } + else if (*groupingPointer != 0) + { + step = *groupingPointer++; + } + if (step == 0) + break; + position -= step; + } + return (position == 0); +#else + return FALSE; +#endif +} + +/************************************************************************* + * TrioGetPosition + * + * Get the %n$ position. + */ +TRIO_PRIVATE int +TrioGetPosition +TRIO_ARGS2((format, indexPointer), + TRIO_CONST char *format, + int *indexPointer) +{ +#if TRIO_UNIX98 + char *tmpformat; + int number = 0; + int index = *indexPointer; + + number = (int)trio_to_long(&format[index], &tmpformat, BASE_DECIMAL); + index = (int)(tmpformat - format); + if ((number != 0) && (QUALIFIER_POSITION == format[index++])) + { + *indexPointer = index; + /* + * number is decreased by 1, because n$ starts from 1, whereas + * the array it is indexing starts from 0. + */ + return number - 1; + } +#endif + return NO_POSITION; +} + +#if TRIO_EXTENSION +/************************************************************************* + * TrioFindNamespace + * + * Find registered user-defined specifier. + * The prev argument is used for optimization only. + */ +TRIO_PRIVATE trio_userdef_t * +TrioFindNamespace +TRIO_ARGS2((name, prev), + TRIO_CONST char *name, + trio_userdef_t **prev) +{ + trio_userdef_t *def; + + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + for (def = internalUserDef; def; def = def->next) + { + /* Case-sensitive string comparison */ + if (trio_equal_case(def->name, name)) + break; + + if (prev) + *prev = def; + } + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + + return def; +} +#endif + +/************************************************************************* + * TrioPower + * + * Description: + * Calculate pow(base, exponent), where number and exponent are integers. + */ +TRIO_PRIVATE trio_long_double_t +TrioPower +TRIO_ARGS2((number, exponent), + int number, + int exponent) +{ + trio_long_double_t result; + + if (number == 10) + { + switch (exponent) + { + /* Speed up calculation of common cases */ + case 0: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E-1); + break; + case 1: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+0); + break; + case 2: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+1); + break; + case 3: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+2); + break; + case 4: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+3); + break; + case 5: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+4); + break; + case 6: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+5); + break; + case 7: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+6); + break; + case 8: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+7); + break; + case 9: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+8); + break; + default: + result = powl((trio_long_double_t)number, + (trio_long_double_t)exponent); + break; + } + } + else + { + return powl((trio_long_double_t)number, (trio_long_double_t)exponent); + } + return result; +} + +/************************************************************************* + * TrioLogarithm + */ +TRIO_PRIVATE double +TrioLogarithm +TRIO_ARGS2((number, base), + double number, + int base) +{ + double result; + + if (number <= 0.0) + { + /* xlC crashes on log(0) */ + result = (number == 0.0) ? trio_ninf() : trio_nan(); + } + else + { + if (base == 10) + { + result = log10(number); + } + else + { + result = log10(number) / log10((double)base); + } + } + return result; +} + +/************************************************************************* + * TrioLogarithmBase + */ +TRIO_PRIVATE double +TrioLogarithmBase +TRIO_ARGS1((base), + int base) +{ + switch (base) + { + case BASE_BINARY : return 1.0; + case BASE_OCTAL : return 3.0; + case BASE_DECIMAL: return 3.321928094887362345; + case BASE_HEX : return 4.0; + default : return TrioLogarithm((double)base, 2); + } +} + +/************************************************************************* + * TrioParse + * + * Description: + * Parse the format string + */ +TRIO_PRIVATE int +TrioParse +TRIO_ARGS5((type, format, parameters, arglist, argarray), + int type, + TRIO_CONST char *format, + trio_parameter_t *parameters, + va_list *arglist, + trio_pointer_t *argarray) +{ + /* Count the number of times a parameter is referenced */ + unsigned short usedEntries[MAX_PARAMETERS]; + /* Parameter counters */ + int parameterPosition; + int currentParam; + int maxParam = -1; + /* Utility variables */ + unsigned long flags; + int width; + int precision; + int varsize; + int base; + int index; /* Index into formatting string */ + int dots; /* Count number of dots in modifier part */ + BOOLEAN_T positional; /* Does the specifier have a positional? */ + BOOLEAN_T gotSticky = FALSE; /* Are there any sticky modifiers at all? */ + /* + * indices specifies the order in which the parameters must be + * read from the va_args (this is necessary to handle positionals) + */ + int indices[MAX_PARAMETERS]; + int pos = 0; + /* Various variables */ + char ch; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; +#endif + int save_errno; + int i = -1; + int num; + char *tmpformat; + + /* One and only one of arglist and argarray must be used */ + assert((arglist != NULL) ^ (argarray != NULL)); + + /* + * The 'parameters' array is not initialized, but we need to + * know which entries we have used. + */ + memset(usedEntries, 0, sizeof(usedEntries)); + + save_errno = errno; + index = 0; + parameterPosition = 0; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + /* + * Multibyte characters cannot be legal specifiers or + * modifiers, so we skip over them. + */ + charlen = mblen(&format[index], MB_LEN_MAX); + index += (charlen > 0) ? charlen : 1; + continue; /* while */ + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + if (CHAR_IDENTIFIER == format[index++]) + { + if (CHAR_IDENTIFIER == format[index]) + { + index++; + continue; /* while */ + } + + flags = FLAGS_NEW; + dots = 0; + currentParam = TrioGetPosition(format, &index); + positional = (NO_POSITION != currentParam); + if (!positional) + { + /* We have no positional, get the next counter */ + currentParam = parameterPosition; + } + if(currentParam >= MAX_PARAMETERS) + { + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_ETOOMANY, index); + } + + if (currentParam > maxParam) + maxParam = currentParam; + + /* Default values */ + width = NO_WIDTH; + precision = NO_PRECISION; + base = NO_BASE; + varsize = NO_SIZE; + + while (TrioIsQualifier(format[index])) + { + ch = format[index++]; + + switch (ch) + { + case QUALIFIER_SPACE: + flags |= FLAGS_SPACE; + break; + + case QUALIFIER_PLUS: + flags |= FLAGS_SHOWSIGN; + break; + + case QUALIFIER_MINUS: + flags |= FLAGS_LEFTADJUST; + flags &= ~FLAGS_NILPADDING; + break; + + case QUALIFIER_ALTERNATIVE: + flags |= FLAGS_ALTERNATIVE; + break; + + case QUALIFIER_DOT: + if (dots == 0) /* Precision */ + { + dots++; + + /* Skip if no precision */ + if (QUALIFIER_DOT == format[index]) + break; + + /* After the first dot we have the precision */ + flags |= FLAGS_PRECISION; + if ((QUALIFIER_STAR == format[index]) +#if defined(QUALIFIER_PARAM) + || (QUALIFIER_PARAM == format[index]) +#endif + ) + { + index++; + flags |= FLAGS_PRECISION_PARAMETER; + + precision = TrioGetPosition(format, &index); + if (precision == NO_POSITION) + { + parameterPosition++; + if (positional) + precision = parameterPosition; + else + { + precision = currentParam; + currentParam = precision + 1; + } + } + else + { + if (! positional) + currentParam = precision + 1; + if (width > maxParam) + maxParam = precision; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + precision = trio_to_long(&format[index], + &tmpformat, + BASE_DECIMAL); + index = (int)(tmpformat - format); + } + } + else if (dots == 1) /* Base */ + { + dots++; + + /* After the second dot we have the base */ + flags |= FLAGS_BASE; + if ((QUALIFIER_STAR == format[index]) +#if defined(QUALIFIER_PARAM) + || (QUALIFIER_PARAM == format[index]) +#endif + ) + { + index++; + flags |= FLAGS_BASE_PARAMETER; + base = TrioGetPosition(format, &index); + if (base == NO_POSITION) + { + parameterPosition++; + if (positional) + base = parameterPosition; + else + { + base = currentParam; + currentParam = base + 1; + } + } + else + { + if (! positional) + currentParam = base + 1; + if (base > maxParam) + maxParam = base; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + base = trio_to_long(&format[index], + &tmpformat, + BASE_DECIMAL); + if (base > MAX_BASE) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + index = (int)(tmpformat - format); + } + } + else + { + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + break; /* QUALIFIER_DOT */ + +#if defined(QUALIFIER_PARAM) + case QUALIFIER_PARAM: + type = TYPE_PRINT; + /* FALLTHROUGH */ +#endif + case QUALIFIER_STAR: + /* This has different meanings for print and scan */ + if (TYPE_PRINT == type) + { + /* Read with from parameter */ + flags |= (FLAGS_WIDTH | FLAGS_WIDTH_PARAMETER); + width = TrioGetPosition(format, &index); + if (width == NO_POSITION) + { + parameterPosition++; + if (positional) + width = parameterPosition; + else + { + width = currentParam; + currentParam = width + 1; + } + } + else + { + if (! positional) + currentParam = width + 1; + if (width > maxParam) + maxParam = width; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + /* Scan, but do not store result */ + flags |= FLAGS_IGNORE; + } + + break; /* QUALIFIER_STAR */ + + case '0': + if (! (flags & FLAGS_LEFTADJUST)) + flags |= FLAGS_NILPADDING; + /* FALLTHROUGH */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + flags |= FLAGS_WIDTH; + /* &format[index - 1] is used to "rewind" the read + * character from format + */ + width = trio_to_long(&format[index - 1], + &tmpformat, + BASE_DECIMAL); + index = (int)(tmpformat - format); + break; + + case QUALIFIER_SHORT: + if (flags & FLAGS_SHORTSHORT) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + else if (flags & FLAGS_SHORT) + flags |= FLAGS_SHORTSHORT; + else + flags |= FLAGS_SHORT; + break; + + case QUALIFIER_LONG: + if (flags & FLAGS_QUAD) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + else if (flags & FLAGS_LONG) + flags |= FLAGS_QUAD; + else + flags |= FLAGS_LONG; + break; + + case QUALIFIER_LONG_UPPER: + flags |= FLAGS_LONGDOUBLE; + break; + +#if defined(QUALIFIER_SIZE_T) + case QUALIFIER_SIZE_T: + flags |= FLAGS_SIZE_T; + /* Modify flags for later truncation of number */ + if (sizeof(size_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(size_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_PTRDIFF_T) + case QUALIFIER_PTRDIFF_T: + flags |= FLAGS_PTRDIFF_T; + if (sizeof(ptrdiff_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(ptrdiff_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_INTMAX_T) + case QUALIFIER_INTMAX_T: + flags |= FLAGS_INTMAX_T; + if (sizeof(trio_intmax_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(trio_intmax_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_QUAD) + case QUALIFIER_QUAD: + flags |= FLAGS_QUAD; + break; +#endif + +#if defined(QUALIFIER_FIXED_SIZE) + case QUALIFIER_FIXED_SIZE: + if (flags & FLAGS_FIXED_SIZE) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + if (flags & (FLAGS_ALL_SIZES | FLAGS_LONGDOUBLE | + FLAGS_WIDECHAR | FLAGS_VARSIZE_PARAMETER)) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + if ((format[index] == '6') && + (format[index + 1] == '4')) + { + varsize = sizeof(trio_int64_t); + index += 2; + } + else if ((format[index] == '3') && + (format[index + 1] == '2')) + { + varsize = sizeof(trio_int32_t); + index += 2; + } + else if ((format[index] == '1') && + (format[index + 1] == '6')) + { + varsize = sizeof(trio_int16_t); + index += 2; + } + else if (format[index] == '8') + { + varsize = sizeof(trio_int8_t); + index++; + } + else + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + flags |= FLAGS_FIXED_SIZE; + break; +#endif + +#if defined(QUALIFIER_WIDECHAR) + case QUALIFIER_WIDECHAR: + flags |= FLAGS_WIDECHAR; + break; +#endif + +#if defined(QUALIFIER_SIZE_T_UPPER) + case QUALIFIER_SIZE_T_UPPER: + break; +#endif + +#if defined(QUALIFIER_QUOTE) + case QUALIFIER_QUOTE: + flags |= FLAGS_QUOTE; + break; +#endif + +#if defined(QUALIFIER_STICKY) + case QUALIFIER_STICKY: + flags |= FLAGS_STICKY; + gotSticky = TRUE; + break; +#endif + +#if defined(QUALIFIER_VARSIZE) + case QUALIFIER_VARSIZE: + flags |= FLAGS_VARSIZE_PARAMETER; + parameterPosition++; + if (positional) + varsize = parameterPosition; + else + { + varsize = currentParam; + currentParam = varsize + 1; + } + if (currentParam > maxParam) + maxParam = currentParam; + break; +#endif + +#if defined(QUALIFIER_ROUNDING_UPPER) + case QUALIFIER_ROUNDING_UPPER: + flags |= FLAGS_ROUNDING; + break; +#endif + + default: + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + } /* while qualifier */ + + /* + * Parameters only need the type and value. The value is + * read later. + */ + if (flags & FLAGS_WIDTH_PARAMETER) + { + usedEntries[width] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[width] = pos; + width = pos++; + } + if (flags & FLAGS_PRECISION_PARAMETER) + { + usedEntries[precision] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[precision] = pos; + precision = pos++; + } + if (flags & FLAGS_BASE_PARAMETER) + { + usedEntries[base] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[base] = pos; + base = pos++; + } + if (flags & FLAGS_VARSIZE_PARAMETER) + { + usedEntries[varsize] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[varsize] = pos; + varsize = pos++; + } + + indices[currentParam] = pos; + + switch (format[index++]) + { +#if defined(SPECIFIER_CHAR_UPPER) + case SPECIFIER_CHAR_UPPER: + flags |= FLAGS_WIDECHAR; + /* FALLTHROUGH */ +#endif + case SPECIFIER_CHAR: + if (flags & FLAGS_LONG) + flags |= FLAGS_WIDECHAR; + else if (flags & FLAGS_SHORT) + flags &= ~FLAGS_WIDECHAR; + parameters[pos].type = FORMAT_CHAR; + break; + +#if defined(SPECIFIER_STRING_UPPER) + case SPECIFIER_STRING_UPPER: + flags |= FLAGS_WIDECHAR; + /* FALLTHROUGH */ +#endif + case SPECIFIER_STRING: + if (flags & FLAGS_LONG) + flags |= FLAGS_WIDECHAR; + else if (flags & FLAGS_SHORT) + flags &= ~FLAGS_WIDECHAR; + parameters[pos].type = FORMAT_STRING; + break; + + case SPECIFIER_GROUP: + if (TYPE_SCAN == type) + { + int depth = 1; + parameters[pos].type = FORMAT_GROUP; + if (format[index] == QUALIFIER_CIRCUMFLEX) + index++; + if (format[index] == SPECIFIER_UNGROUP) + index++; + if (format[index] == QUALIFIER_MINUS) + index++; + /* Skip nested brackets */ + while (format[index] != NIL) + { + if (format[index] == SPECIFIER_GROUP) + { + depth++; + } + else if (format[index] == SPECIFIER_UNGROUP) + { + if (--depth <= 0) + { + index++; + break; + } + } + index++; + } + } + break; + + case SPECIFIER_INTEGER: + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_UNSIGNED: + flags |= FLAGS_UNSIGNED; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_DECIMAL: + /* Disable base modifier */ + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_DECIMAL; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_OCTAL: + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_OCTAL; + parameters[pos].type = FORMAT_INT; + break; + +#if defined(SPECIFIER_BINARY) + case SPECIFIER_BINARY_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_BINARY: + flags |= FLAGS_NILPADDING; + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_BINARY; + parameters[pos].type = FORMAT_INT; + break; +#endif + + case SPECIFIER_HEX_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_HEX: + flags |= FLAGS_UNSIGNED; + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_HEX; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_FLOAT_E_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_E: + flags |= FLAGS_FLOAT_E; + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_FLOAT_G_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_G: + flags |= FLAGS_FLOAT_G; + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_FLOAT_F_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_F: + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_POINTER: + if (sizeof(trio_pointer_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(trio_pointer_t) == sizeof(long)) + flags |= FLAGS_LONG; + parameters[pos].type = FORMAT_POINTER; + break; + + case SPECIFIER_COUNT: + parameters[pos].type = FORMAT_COUNT; + break; + +#if defined(SPECIFIER_HEXFLOAT) +# if defined(SPECIFIER_HEXFLOAT_UPPER) + case SPECIFIER_HEXFLOAT_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ +# endif + case SPECIFIER_HEXFLOAT: + base = BASE_HEX; + parameters[pos].type = FORMAT_DOUBLE; + break; +#endif + +#if defined(FORMAT_ERRNO) + case SPECIFIER_ERRNO: + parameters[pos].type = FORMAT_ERRNO; + break; +#endif + +#if defined(SPECIFIER_USER_DEFINED_BEGIN) + case SPECIFIER_USER_DEFINED_BEGIN: + { + unsigned int max; + int without_namespace = TRUE; + + parameters[pos].type = FORMAT_USER_DEFINED; + parameters[pos].user_name[0] = NIL; + tmpformat = (char *)&format[index]; + + while ((ch = format[index])) + { + index++; + if (ch == SPECIFIER_USER_DEFINED_END) + { + if (without_namespace) + { + /* We must get the handle first */ + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].indexAfterSpecifier = index; + parameters[pos].flags = FLAGS_USER_DEFINED; + /* Adjust parameters for insertion of new one */ + pos++; + usedEntries[currentParam] += 1; + parameters[pos].type = FORMAT_USER_DEFINED; + currentParam++; + indices[currentParam] = pos; + if (currentParam > maxParam) + maxParam = currentParam; + } + /* Copy the user data */ + max = (unsigned int)(&format[index] - tmpformat); + if (max > MAX_USER_DATA) + max = MAX_USER_DATA; + trio_copy_max(parameters[pos].user_data, + max, + tmpformat); + break; /* while */ + } + if (ch == SPECIFIER_USER_DEFINED_SEPARATOR) + { + without_namespace = FALSE; + /* Copy the namespace for later looking-up */ + max = (int)(&format[index] - tmpformat); + if (max > MAX_USER_NAME) + max = MAX_USER_NAME; + trio_copy_max(parameters[pos].user_name, + max, + tmpformat); + tmpformat = (char *)&format[index]; + } + } + if (ch != SPECIFIER_USER_DEFINED_END) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + break; +#endif /* defined(SPECIFIER_USER_DEFINED_BEGIN) */ + + default: + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + + /* Count the number of times this entry has been used */ + usedEntries[currentParam] += 1; + + /* Find last sticky parameters */ + if (gotSticky && !(flags & FLAGS_STICKY)) + { + for (i = pos - 1; i >= 0; i--) + { + if (parameters[i].type == FORMAT_PARAMETER) + continue; + if ((parameters[i].flags & FLAGS_STICKY) && + (parameters[i].type == parameters[pos].type)) + { + /* Do not overwrite current qualifiers */ + flags |= (parameters[i].flags & (unsigned long)~FLAGS_STICKY); + if (width == NO_WIDTH) + width = parameters[i].width; + if (precision == NO_PRECISION) + precision = parameters[i].precision; + if (base == NO_BASE) + base = parameters[i].base; + break; + } + } + } + + parameters[pos].indexAfterSpecifier = index; + parameters[pos].flags = flags; + parameters[pos].width = width; + parameters[pos].precision = precision; + parameters[pos].base = (base == NO_BASE) ? BASE_DECIMAL : base; + parameters[pos].varsize = varsize; + pos++; + + if (! positional) + parameterPosition++; + + } /* if identifier */ + + } /* while format characters left */ + + for (num = 0; num <= maxParam; num++) + { + if (usedEntries[num] != 1) + { + if (usedEntries[num] == 0) /* gap detected */ + return TRIO_ERROR_RETURN(TRIO_EGAP, num); + else /* double references detected */ + return TRIO_ERROR_RETURN(TRIO_EDBLREF, num); + } + + i = indices[num]; + + /* + * FORMAT_PARAMETERS are only present if they must be read, + * so it makes no sense to check the ignore flag (besides, + * the flags variable is not set for that particular type) + */ + if ((parameters[i].type != FORMAT_PARAMETER) && + (parameters[i].flags & FLAGS_IGNORE)) + continue; /* for all arguments */ + + /* + * The stack arguments are read according to ANSI C89 + * default argument promotions: + * + * char = int + * short = int + * unsigned char = unsigned int + * unsigned short = unsigned int + * float = double + * + * In addition to the ANSI C89 these types are read (the + * default argument promotions of C99 has not been + * considered yet) + * + * long long + * long double + * size_t + * ptrdiff_t + * intmax_t + */ + switch (parameters[i].type) + { + case FORMAT_GROUP: + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + parameters[i].data.wstring = (argarray == NULL) + ? va_arg(*arglist, trio_wchar_t *) + : (trio_wchar_t *)(argarray[num]); + } + else +#endif + { + parameters[i].data.string = (argarray == NULL) + ? va_arg(*arglist, char *) + : (char *)(argarray[num]); + } + break; + +#if defined(FORMAT_USER_DEFINED) + case FORMAT_USER_DEFINED: +#endif + case FORMAT_POINTER: + case FORMAT_COUNT: + case FORMAT_UNKNOWN: + parameters[i].data.pointer = (argarray == NULL) + ? va_arg(*arglist, trio_pointer_t ) + : argarray[num]; + break; + + case FORMAT_CHAR: + case FORMAT_INT: + if (TYPE_SCAN == type) + { + if (argarray == NULL) + parameters[i].data.pointer = + (trio_pointer_t)va_arg(*arglist, trio_pointer_t); + else + { + if (parameters[i].type == FORMAT_CHAR) + parameters[i].data.pointer = + (trio_pointer_t)((char *)argarray[num]); + else if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.pointer = + (trio_pointer_t)((short *)argarray[num]); + else + parameters[i].data.pointer = + (trio_pointer_t)((int *)argarray[num]); + } + } + else + { +#if defined(QUALIFIER_VARSIZE) || defined(QUALIFIER_FIXED_SIZE) + if (parameters[i].flags + & (FLAGS_VARSIZE_PARAMETER | FLAGS_FIXED_SIZE)) + { + if (parameters[i].flags & FLAGS_VARSIZE_PARAMETER) + { + /* + * Variable sizes are mapped onto the fixed sizes, in + * accordance with integer promotion. + * + * Please note that this may not be portable, as we + * only guess the size, not the layout of the numbers. + * For example, if int is little-endian, and long is + * big-endian, then this will fail. + */ + varsize = (int)parameters[parameters[i].varsize].data.number.as_unsigned; + } + else + { + /* Used for the I modifiers */ + varsize = parameters[i].varsize; + } + parameters[i].flags &= ~FLAGS_ALL_VARSIZES; + + if (varsize <= (int)sizeof(int)) + ; + else if (varsize <= (int)sizeof(long)) + parameters[i].flags |= FLAGS_LONG; +#if defined(QUALIFIER_INTMAX_T) + else if (varsize <= (int)sizeof(trio_longlong_t)) + parameters[i].flags |= FLAGS_QUAD; + else + parameters[i].flags |= FLAGS_INTMAX_T; +#else + else + parameters[i].flags |= FLAGS_QUAD; +#endif + } +#endif /* defined(QUALIFIER_VARSIZE) */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (parameters[i].flags & FLAGS_SIZE_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, size_t) + : (trio_uintmax_t)(*((size_t *)argarray[num])); + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (parameters[i].flags & FLAGS_PTRDIFF_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, ptrdiff_t) + : (trio_uintmax_t)(*((ptrdiff_t *)argarray[num])); + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (parameters[i].flags & FLAGS_INTMAX_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, trio_intmax_t) + : (trio_uintmax_t)(*((trio_intmax_t *)argarray[num])); + else +#endif + if (parameters[i].flags & FLAGS_QUAD) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, trio_ulonglong_t) + : (trio_uintmax_t)(*((trio_ulonglong_t *)argarray[num])); + else if (parameters[i].flags & FLAGS_LONG) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, long) + : (trio_uintmax_t)(*((long *)argarray[num])); + else + { + if (argarray == NULL) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)va_arg(*arglist, int); + else + { + if (parameters[i].type == FORMAT_CHAR) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((char *)argarray[num])); + else if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((short *)argarray[num])); + else + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((int *)argarray[num])); + } + } + } + break; + + case FORMAT_PARAMETER: + /* + * The parameter for the user-defined specifier is a pointer, + * whereas the rest (width, precision, base) uses an integer. + */ + if (parameters[i].flags & FLAGS_USER_DEFINED) + parameters[i].data.pointer = (argarray == NULL) + ? va_arg(*arglist, trio_pointer_t ) + : argarray[num]; + else + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, int) + : (trio_uintmax_t)(*((int *)argarray[num])); + break; + + case FORMAT_DOUBLE: + if (TYPE_SCAN == type) + { + if (parameters[i].flags & FLAGS_LONGDOUBLE) + parameters[i].data.longdoublePointer = (argarray == NULL) + ? va_arg(*arglist, trio_long_double_t *) + : (trio_long_double_t *)argarray[num]; + else + { + if (parameters[i].flags & FLAGS_LONG) + parameters[i].data.doublePointer = (argarray == NULL) + ? va_arg(*arglist, double *) + : (double *)argarray[num]; + else + parameters[i].data.doublePointer = (argarray == NULL) + ? (double *)va_arg(*arglist, float *) + : (double *)((float *)argarray[num]); + } + } + else + { + if (parameters[i].flags & FLAGS_LONGDOUBLE) + parameters[i].data.longdoubleNumber = (argarray == NULL) + ? va_arg(*arglist, trio_long_double_t) + : (trio_long_double_t)(*((trio_long_double_t *)argarray[num])); + else + { + if (argarray == NULL) + parameters[i].data.longdoubleNumber = + (trio_long_double_t)va_arg(*arglist, double); + else + { + if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.longdoubleNumber = + (trio_long_double_t)(*((float *)argarray[num])); + else + parameters[i].data.longdoubleNumber = + (trio_long_double_t)(*((double *)argarray[num])); + } + } + } + break; + +#if defined(FORMAT_ERRNO) + case FORMAT_ERRNO: + parameters[i].data.errorNumber = save_errno; + break; +#endif + + default: + break; + } + } /* for all specifiers */ + return num; +} + + +/************************************************************************* + * + * FORMATTING + * + ************************************************************************/ + + +/************************************************************************* + * TrioWriteNumber + * + * Description: + * Output a number. + * The complexity of this function is a result of the complexity + * of the dependencies of the flags. + */ +TRIO_PRIVATE void +TrioWriteNumber +TRIO_ARGS6((self, number, flags, width, precision, base), + trio_class_t *self, + trio_uintmax_t number, + unsigned long flags, + int width, + int precision, + int base) +{ + BOOLEAN_T isNegative; + char buffer[MAX_CHARS_IN(trio_uintmax_t) * (1 + MAX_LOCALE_SEPARATOR_LENGTH) + 1]; + char *bufferend; + char *pointer; + TRIO_CONST char *digits; + int i; + int length; + char *p; + int count; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + assert(((base >= MIN_BASE) && (base <= MAX_BASE)) || (base == NO_BASE)); + + digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower; + if (base == NO_BASE) + base = BASE_DECIMAL; + + isNegative = (flags & FLAGS_UNSIGNED) + ? FALSE + : ((trio_intmax_t)number < 0); + if (isNegative) + number = -((trio_intmax_t)number); + + if (flags & FLAGS_QUAD) + number &= (trio_ulonglong_t)-1; + else if (flags & FLAGS_LONG) + number &= (unsigned long)-1; + else + number &= (unsigned int)-1; + + /* Build number */ + pointer = bufferend = &buffer[sizeof(buffer) - 1]; + *pointer-- = NIL; + for (i = 1; i < (int)sizeof(buffer); i++) + { + *pointer-- = digits[number % base]; + number /= base; + if (number == 0) + break; + + if ((flags & FLAGS_QUOTE) && TrioFollowedBySeparator(i + 1)) + { + /* + * We are building the number from the least significant + * to the most significant digit, so we have to copy the + * thousand separator backwards + */ + length = internalThousandSeparatorLength; + if (((int)(pointer - buffer) - length) > 0) + { + p = &internalThousandSeparator[length - 1]; + while (length-- > 0) + *pointer-- = *p--; + } + } + } + + /* Adjust width */ + width -= (bufferend - pointer) - 1; + + /* Adjust precision */ + if (NO_PRECISION != precision) + { + precision -= (bufferend - pointer) - 1; + if (precision < 0) + precision = 0; + flags |= FLAGS_NILPADDING; + } + + /* Adjust width further */ + if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) + width--; + if (flags & FLAGS_ALTERNATIVE) + { + switch (base) + { + case BASE_BINARY: + case BASE_HEX: + width -= 2; + break; + case BASE_OCTAL: + width--; + break; + default: + break; + } + } + + /* Output prefixes spaces if needed */ + if (! ((flags & FLAGS_LEFTADJUST) || + ((flags & FLAGS_NILPADDING) && (precision == NO_PRECISION)))) + { + count = (precision == NO_PRECISION) ? 0 : precision; + while (width-- > count) + self->OutStream(self, CHAR_ADJUST); + } + + /* width has been adjusted for signs and alternatives */ + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + + if (flags & FLAGS_ALTERNATIVE) + { + switch (base) + { + case BASE_BINARY: + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'B' : 'b'); + break; + + case BASE_OCTAL: + self->OutStream(self, '0'); + break; + + case BASE_HEX: + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + break; + + default: + break; + } /* switch base */ + } + + /* Output prefixed zero padding if needed */ + if (flags & FLAGS_NILPADDING) + { + if (precision == NO_PRECISION) + precision = width; + while (precision-- > 0) + { + self->OutStream(self, '0'); + width--; + } + } + + /* Output the number itself */ + while (*(++pointer)) + { + self->OutStream(self, *pointer); + } + + /* Output trailing spaces if needed */ + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } +} + +/************************************************************************* + * TrioWriteStringCharacter + * + * Description: + * Output a single character of a string + */ +TRIO_PRIVATE void +TrioWriteStringCharacter +TRIO_ARGS3((self, ch, flags), + trio_class_t *self, + int ch, + unsigned long flags) +{ + if (flags & FLAGS_ALTERNATIVE) + { + if (! isprint(ch)) + { + /* + * Non-printable characters are converted to C escapes or + * \number, if no C escape exists. + */ + self->OutStream(self, CHAR_BACKSLASH); + switch (ch) + { + case '\007': self->OutStream(self, 'a'); break; + case '\b': self->OutStream(self, 'b'); break; + case '\f': self->OutStream(self, 'f'); break; + case '\n': self->OutStream(self, 'n'); break; + case '\r': self->OutStream(self, 'r'); break; + case '\t': self->OutStream(self, 't'); break; + case '\v': self->OutStream(self, 'v'); break; + case '\\': self->OutStream(self, '\\'); break; + default: + self->OutStream(self, 'x'); + TrioWriteNumber(self, (trio_uintmax_t)ch, + FLAGS_UNSIGNED | FLAGS_NILPADDING, + 2, 2, BASE_HEX); + break; + } + } + else if (ch == CHAR_BACKSLASH) + { + self->OutStream(self, CHAR_BACKSLASH); + self->OutStream(self, CHAR_BACKSLASH); + } + else + { + self->OutStream(self, ch); + } + } + else + { + self->OutStream(self, ch); + } +} + +/************************************************************************* + * TrioWriteString + * + * Description: + * Output a string + */ +TRIO_PRIVATE void +TrioWriteString +TRIO_ARGS5((self, string, flags, width, precision), + trio_class_t *self, + TRIO_CONST char *string, + unsigned long flags, + int width, + int precision) +{ + int length; + int ch; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + + if (string == NULL) + { + string = internalNullString; + length = sizeof(internalNullString) - 1; + /* Disable quoting for the null pointer */ + flags &= (~FLAGS_QUOTE); + width = 0; + } + else + { + length = trio_length(string); + } + if ((NO_PRECISION != precision) && + (precision < length)) + { + length = precision; + } + width -= length; + + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); + + if (! (flags & FLAGS_LEFTADJUST)) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + + while (length-- > 0) + { + /* The ctype parameters must be an unsigned char (or EOF) */ + ch = (int)((unsigned char)(*string++)); + TrioWriteStringCharacter(self, ch, flags); + } + + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); +} + +/************************************************************************* + * TrioWriteWideStringCharacter + * + * Description: + * Output a wide string as a multi-byte sequence + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE int +TrioWriteWideStringCharacter +TRIO_ARGS4((self, wch, flags, width), + trio_class_t *self, + trio_wchar_t wch, + unsigned long flags, + int width) +{ + int size; + int i; + int ch; + char *string; + char buffer[MB_LEN_MAX + 1]; + + if (width == NO_WIDTH) + width = sizeof(buffer); + + size = wctomb(buffer, wch); + if ((size <= 0) || (size > width) || (buffer[0] == NIL)) + return 0; + + string = buffer; + i = size; + while ((width >= i) && (width-- > 0) && (i-- > 0)) + { + /* The ctype parameters must be an unsigned char (or EOF) */ + ch = (int)((unsigned char)(*string++)); + TrioWriteStringCharacter(self, ch, flags); + } + return size; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioWriteWideString + * + * Description: + * Output a wide character string as a multi-byte string + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE void +TrioWriteWideString +TRIO_ARGS5((self, wstring, flags, width, precision), + trio_class_t *self, + TRIO_CONST trio_wchar_t *wstring, + unsigned long flags, + int width, + int precision) +{ + int length; + int size; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + if (wstring == NULL) + { + TrioWriteString(self, NULL, flags, width, precision); + return; + } + + if (NO_PRECISION == precision) + { + length = INT_MAX; + } + else + { + length = precision; + width -= length; + } + + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); + + if (! (flags & FLAGS_LEFTADJUST)) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + + while (length > 0) + { + size = TrioWriteWideStringCharacter(self, *wstring++, flags, length); + if (size == 0) + break; /* while */ + length -= size; + } + + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioWriteDouble + * + * http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/dr_211.htm + * + * "5.2.4.2.2 paragraph #4 + * + * The accuracy [...] is implementation defined, as is the accuracy + * of the conversion between floating-point internal representations + * and string representations performed by the libray routine in + * " + */ +/* FIXME: handle all instances of constant long-double number (L) + * and *l() math functions. + */ +TRIO_PRIVATE void +TrioWriteDouble +TRIO_ARGS6((self, number, flags, width, precision, base), + trio_class_t *self, + trio_long_double_t number, + unsigned long flags, + int width, + int precision, + int base) +{ + trio_long_double_t integerNumber; + trio_long_double_t fractionNumber; + trio_long_double_t workNumber; + int integerDigits; + int fractionDigits; + int exponentDigits; + int baseDigits; + int integerThreshold; + int fractionThreshold; + int expectedWidth; + int exponent = 0; + unsigned int uExponent = 0; + int exponentBase; + trio_long_double_t dblBase; + trio_long_double_t dblIntegerBase; + trio_long_double_t dblFractionBase; + trio_long_double_t integerAdjust; + trio_long_double_t fractionAdjust; + BOOLEAN_T isNegative; + BOOLEAN_T isExponentNegative = FALSE; + BOOLEAN_T requireTwoDigitExponent; + BOOLEAN_T isHex; + TRIO_CONST char *digits; + char *groupingPointer; + int i; + int index; + BOOLEAN_T hasOnlyZeroes; + int zeroes = 0; + register int trailingZeroes; + BOOLEAN_T keepTrailingZeroes; + BOOLEAN_T keepDecimalPoint; + trio_long_double_t epsilon; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + assert(((base >= MIN_BASE) && (base <= MAX_BASE)) || (base == NO_BASE)); + + /* Determine sign and look for special quantities */ + switch (trio_fpclassify_and_signbit(number, &isNegative)) + { + case TRIO_FP_NAN: + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? NAN_UPPER + : NAN_LOWER, + flags, width, precision); + return; + + case TRIO_FP_INFINITE: + if (isNegative) + { + /* Negative infinity */ + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? "-" INFINITE_UPPER + : "-" INFINITE_LOWER, + flags, width, precision); + return; + } + else + { + /* Positive infinity */ + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? INFINITE_UPPER + : INFINITE_LOWER, + flags, width, precision); + return; + } + + default: + /* Finitude */ + break; + } + + /* Normal numbers */ + if (flags & FLAGS_LONGDOUBLE) + { + baseDigits = (base == 10) + ? LDBL_DIG + : (int)floor(LDBL_MANT_DIG / TrioLogarithmBase(base)); + epsilon = LDBL_EPSILON; + } + else if (flags & FLAGS_SHORT) + { + baseDigits = (base == BASE_DECIMAL) + ? FLT_DIG + : (int)floor(FLT_MANT_DIG / TrioLogarithmBase(base)); + epsilon = FLT_EPSILON; + } + else + { + baseDigits = (base == BASE_DECIMAL) + ? DBL_DIG + : (int)floor(DBL_MANT_DIG / TrioLogarithmBase(base)); + epsilon = DBL_EPSILON; + } + + digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower; + isHex = (base == BASE_HEX); + if (base == NO_BASE) + base = BASE_DECIMAL; + dblBase = (trio_long_double_t)base; + keepTrailingZeroes = !( (flags & FLAGS_ROUNDING) || + ( (flags & FLAGS_FLOAT_G) && + !(flags & FLAGS_ALTERNATIVE) ) ); + + if (flags & FLAGS_ROUNDING) + precision = baseDigits; + + if (precision == NO_PRECISION) + precision = FLT_DIG; + + if (isNegative) + number = -number; + + if (isHex) + flags |= FLAGS_FLOAT_E; + + if (flags & FLAGS_FLOAT_G) + { + if (precision == 0) + precision = 1; + + if ((number < 1.0E-4) || (number > powl(base, + (trio_long_double_t)precision))) + { + /* Use scientific notation */ + flags |= FLAGS_FLOAT_E; + } + else if (number < 1.0) + { + /* + * Use normal notation. If the integer part of the number is + * zero, then adjust the precision to include leading fractional + * zeros. + */ + workNumber = TrioLogarithm(number, base); + workNumber = TRIO_FABS(workNumber); + if (workNumber - floorl(workNumber) < 0.001) + workNumber--; + zeroes = (int)floorl(workNumber); + } + } + + if (flags & FLAGS_FLOAT_E) + { + /* Scale the number */ + workNumber = TrioLogarithm(number, base); + if (trio_isinf(workNumber) == -1) + { + exponent = 0; + /* Undo setting */ + if (flags & FLAGS_FLOAT_G) + flags &= ~FLAGS_FLOAT_E; + } + else + { + exponent = (int)floorl(workNumber); + number /= powl(dblBase, (trio_long_double_t)exponent); + isExponentNegative = (exponent < 0); + uExponent = (isExponentNegative) ? -exponent : exponent; + /* No thousand separators */ + flags &= ~FLAGS_QUOTE; + } + } + + integerNumber = floorl(number); + fractionNumber = number - integerNumber; + + /* + * Truncated number. + * + * Precision is number of significant digits for FLOAT_G + * and number of fractional digits for others. + */ + integerDigits = (integerNumber > epsilon) + ? 1 + (int)TrioLogarithm(integerNumber, base) + : 1; + fractionDigits = ((flags & FLAGS_FLOAT_G) && (zeroes == 0)) + ? precision - integerDigits + : zeroes + precision; + + dblFractionBase = TrioPower(base, fractionDigits); + + workNumber = number + 0.5 / dblFractionBase; + if (floorl(number) != floorl(workNumber)) + { + if (flags & FLAGS_FLOAT_E) + { + /* Adjust if number was rounded up one digit (ie. 0.99 to 1.00) */ + exponent++; + isExponentNegative = (exponent < 0); + uExponent = (isExponentNegative) ? -exponent : exponent; + workNumber = (number + 0.5 / dblFractionBase) / dblBase; + integerNumber = floorl(workNumber); + fractionNumber = workNumber - integerNumber; + } + else + { + /* Adjust if number was rounded up one digit (ie. 99 to 100) */ + integerNumber = floorl(number + 0.5); + fractionNumber = 0.0; + integerDigits = (integerNumber > epsilon) + ? 1 + (int)TrioLogarithm(integerNumber, base) + : 1; + } + } + + /* Estimate accuracy */ + integerAdjust = fractionAdjust = 0.5; + if (flags & FLAGS_ROUNDING) + { + if (integerDigits > baseDigits) + { + integerThreshold = baseDigits; + fractionDigits = 0; + dblFractionBase = 1.0; + fractionThreshold = 0; + precision = 0; /* Disable decimal-point */ + integerAdjust = TrioPower(base, integerDigits - integerThreshold - 1); + fractionAdjust = 0.0; + } + else + { + integerThreshold = integerDigits; + fractionThreshold = fractionDigits - integerThreshold; + fractionAdjust = 1.0; + } + } + else + { + integerThreshold = INT_MAX; + fractionThreshold = INT_MAX; + } + + /* + * Calculate expected width. + * sign + integer part + thousands separators + decimal point + * + fraction + exponent + */ + fractionAdjust /= dblFractionBase; + hasOnlyZeroes = (floorl((fractionNumber + fractionAdjust) * dblFractionBase) < epsilon); + keepDecimalPoint = ( (flags & FLAGS_ALTERNATIVE) || + !((precision == 0) || + (!keepTrailingZeroes && hasOnlyZeroes)) ); + if (flags & FLAGS_FLOAT_E) + { + exponentDigits = (uExponent == 0) + ? 1 + : (int)ceil(TrioLogarithm((double)(uExponent + 1), base)); + } + else + exponentDigits = 0; + requireTwoDigitExponent = ((base == BASE_DECIMAL) && (exponentDigits == 1)); + + expectedWidth = integerDigits + fractionDigits + + (keepDecimalPoint + ? internalDecimalPointLength + : 0) + + ((flags & FLAGS_QUOTE) + ? TrioCalcThousandSeparatorLength(integerDigits) + : 0); + if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) + expectedWidth += sizeof("-") - 1; + if (exponentDigits > 0) + expectedWidth += exponentDigits + + ((requireTwoDigitExponent ? sizeof("E+0") : sizeof("E+")) - 1); + if (isHex) + expectedWidth += sizeof("0X") - 1; + + /* Output prefixing */ + if (flags & FLAGS_NILPADDING) + { + /* Leading zeros must be after sign */ + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + if (isHex) + { + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + } + if (!(flags & FLAGS_LEFTADJUST)) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, '0'); + } + } + } + else + { + /* Leading spaces must be before sign */ + if (!(flags & FLAGS_LEFTADJUST)) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, CHAR_ADJUST); + } + } + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + if (isHex) + { + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + } + } + + /* Output the integer part and thousand separators */ + dblIntegerBase = 1.0 / TrioPower(base, integerDigits - 1); + for (i = 0; i < integerDigits; i++) + { + workNumber = floorl(((integerNumber + integerAdjust) * dblIntegerBase)); + if (i > integerThreshold) + { + /* Beyond accuracy */ + self->OutStream(self, digits[0]); + } + else + { + self->OutStream(self, digits[(int)fmodl(workNumber, dblBase)]); + } + dblIntegerBase *= dblBase; + + if (((flags & (FLAGS_FLOAT_E | FLAGS_QUOTE)) == FLAGS_QUOTE) + && TrioFollowedBySeparator(integerDigits - i)) + { + for (groupingPointer = internalThousandSeparator; + *groupingPointer != NIL; + groupingPointer++) + { + self->OutStream(self, *groupingPointer); + } + } + } + + /* Insert decimal point and build the fraction part */ + trailingZeroes = 0; + + if (keepDecimalPoint) + { + if (internalDecimalPoint) + { + self->OutStream(self, internalDecimalPoint); + } + else + { + for (i = 0; i < internalDecimalPointLength; i++) + { + self->OutStream(self, internalDecimalPointString[i]); + } + } + } + + for (i = 0; i < fractionDigits; i++) + { + if ((integerDigits > integerThreshold) || (i > fractionThreshold)) + { + /* Beyond accuracy */ + trailingZeroes++; + } + else + { + fractionNumber *= dblBase; + fractionAdjust *= dblBase; + workNumber = floorl(fractionNumber + fractionAdjust); + fractionNumber -= workNumber; + index = (int)fmodl(workNumber, dblBase); + if (index == 0) + { + trailingZeroes++; + } + else + { + while (trailingZeroes > 0) + { + /* Not trailing zeroes after all */ + self->OutStream(self, digits[0]); + trailingZeroes--; + } + self->OutStream(self, digits[index]); + } + } + } + + if (keepTrailingZeroes) + { + while (trailingZeroes > 0) + { + self->OutStream(self, digits[0]); + trailingZeroes--; + } + } + + /* Output exponent */ + if (exponentDigits > 0) + { + self->OutStream(self, + isHex + ? ((flags & FLAGS_UPPER) ? 'P' : 'p') + : ((flags & FLAGS_UPPER) ? 'E' : 'e')); + self->OutStream(self, (isExponentNegative) ? '-' : '+'); + + /* The exponent must contain at least two digits */ + if (requireTwoDigitExponent) + self->OutStream(self, '0'); + + exponentBase = (int)TrioPower(base, exponentDigits - 1); + for (i = 0; i < exponentDigits; i++) + { + self->OutStream(self, digits[(uExponent / exponentBase) % base]); + exponentBase /= base; + } + } + /* Output trailing spaces */ + if (flags & FLAGS_LEFTADJUST) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, CHAR_ADJUST); + } + } +} + +/************************************************************************* + * TrioFormatProcess + * + * Description: + * This is the main engine for formatting output + */ +TRIO_PRIVATE int +TrioFormatProcess +TRIO_ARGS3((data, format, parameters), + trio_class_t *data, + TRIO_CONST char *format, + trio_parameter_t *parameters) +{ +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; +#endif + int i; + TRIO_CONST char *string; + trio_pointer_t pointer; + unsigned long flags; + int width; + int precision; + int base; + int index; + + index = 0; + i = 0; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + charlen = mblen(&format[index], MB_LEN_MAX); + /* + * Only valid multibyte characters are handled here. Invalid + * multibyte characters (charlen == -1) are handled as normal + * characters. + */ + if (charlen != -1) + { + while (charlen-- > 0) + { + data->OutStream(data, format[index++]); + } + continue; /* while characters left in formatting string */ + } + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + if (CHAR_IDENTIFIER == format[index]) + { + if (CHAR_IDENTIFIER == format[index + 1]) + { + data->OutStream(data, CHAR_IDENTIFIER); + index += 2; + } + else + { + /* Skip the parameter entries */ + while (parameters[i].type == FORMAT_PARAMETER) + i++; + + flags = parameters[i].flags; + + /* Find width */ + width = parameters[i].width; + if (flags & FLAGS_WIDTH_PARAMETER) + { + /* Get width from parameter list */ + width = (int)parameters[width].data.number.as_signed; + } + + /* Find precision */ + if (flags & FLAGS_PRECISION) + { + precision = parameters[i].precision; + if (flags & FLAGS_PRECISION_PARAMETER) + { + /* Get precision from parameter list */ + precision = (int)parameters[precision].data.number.as_signed; + } + } + else + { + precision = NO_PRECISION; + } + + /* Find base */ + base = parameters[i].base; + if (flags & FLAGS_BASE_PARAMETER) + { + /* Get base from parameter list */ + base = (int)parameters[base].data.number.as_signed; + } + + switch (parameters[i].type) + { + case FORMAT_CHAR: + if (flags & FLAGS_QUOTE) + data->OutStream(data, CHAR_QUOTE); + if (! (flags & FLAGS_LEFTADJUST)) + { + while (--width > 0) + data->OutStream(data, CHAR_ADJUST); + } +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + TrioWriteWideStringCharacter(data, + (trio_wchar_t)parameters[i].data.number.as_signed, + flags, + NO_WIDTH); + } + else +#endif + { + TrioWriteStringCharacter(data, + (int)parameters[i].data.number.as_signed, + flags); + } + + if (flags & FLAGS_LEFTADJUST) + { + while(--width > 0) + data->OutStream(data, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + data->OutStream(data, CHAR_QUOTE); + + break; /* FORMAT_CHAR */ + + case FORMAT_INT: + TrioWriteNumber(data, + parameters[i].data.number.as_unsigned, + flags, + width, + precision, + base); + + break; /* FORMAT_INT */ + + case FORMAT_DOUBLE: + TrioWriteDouble(data, + parameters[i].data.longdoubleNumber, + flags, + width, + precision, + base); + break; /* FORMAT_DOUBLE */ + + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + TrioWriteWideString(data, + parameters[i].data.wstring, + flags, + width, + precision); + } + else +#endif + { + TrioWriteString(data, + parameters[i].data.string, + flags, + width, + precision); + } + break; /* FORMAT_STRING */ + + case FORMAT_POINTER: + { + trio_reference_t reference; + + reference.data = data; + reference.parameter = ¶meters[i]; + trio_print_pointer(&reference, parameters[i].data.pointer); + } + break; /* FORMAT_POINTER */ + + case FORMAT_COUNT: + pointer = parameters[i].data.pointer; + if (NULL != pointer) + { + /* + * C99 paragraph 7.19.6.1.8 says "the number of + * characters written to the output stream so far by + * this call", which is data->committed + */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)data->committed; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)data->committed; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)data->committed; + else +#endif + if (flags & FLAGS_QUAD) + { + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)data->committed; + } + else if (flags & FLAGS_LONG) + { + *(long int *)pointer = (long int)data->committed; + } + else if (flags & FLAGS_SHORT) + { + *(short int *)pointer = (short int)data->committed; + } + else + { + *(int *)pointer = (int)data->committed; + } + } + break; /* FORMAT_COUNT */ + + case FORMAT_PARAMETER: + break; /* FORMAT_PARAMETER */ + +#if defined(FORMAT_ERRNO) + case FORMAT_ERRNO: + string = trio_error(parameters[i].data.errorNumber); + if (string) + { + TrioWriteString(data, + string, + flags, + width, + precision); + } + else + { + data->OutStream(data, '#'); + TrioWriteNumber(data, + (trio_uintmax_t)parameters[i].data.errorNumber, + flags, + width, + precision, + BASE_DECIMAL); + } + break; /* FORMAT_ERRNO */ +#endif /* defined(FORMAT_ERRNO) */ + +#if defined(FORMAT_USER_DEFINED) + case FORMAT_USER_DEFINED: + { + trio_reference_t reference; + trio_userdef_t *def = NULL; + + if (parameters[i].user_name[0] == NIL) + { + /* Use handle */ + if ((i > 0) || + (parameters[i - 1].type == FORMAT_PARAMETER)) + def = (trio_userdef_t *)parameters[i - 1].data.pointer; + } + else + { + /* Look up namespace */ + def = TrioFindNamespace(parameters[i].user_name, NULL); + } + if (def) { + reference.data = data; + reference.parameter = ¶meters[i]; + def->callback(&reference); + } + } + break; +#endif /* defined(FORMAT_USER_DEFINED) */ + + default: + break; + } /* switch parameter type */ + + /* Prepare for next */ + index = parameters[i].indexAfterSpecifier; + i++; + } + } + else /* not identifier */ + { + data->OutStream(data, format[index++]); + } + } + return data->processed; +} + +/************************************************************************* + * TrioFormatRef + */ +TRIO_PRIVATE int +TrioFormatRef +TRIO_ARGS4((reference, format, arglist, argarray), + trio_reference_t *reference, + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_parameter_t parameters[MAX_PARAMETERS]; + + status = TrioParse(TYPE_PRINT, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioFormatProcess(reference->data, format, parameters); + if (reference->data->error != 0) + { + status = reference->data->error; + } + return status; +} + +/************************************************************************* + * TrioFormat + */ +TRIO_PRIVATE int +TrioFormat +TRIO_ARGS6((destination, destinationSize, OutStream, format, arglist, argarray), + trio_pointer_t destination, + size_t destinationSize, + void (*OutStream) TRIO_PROTO((trio_class_t *, int)), + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_class_t data; + trio_parameter_t parameters[MAX_PARAMETERS]; + + assert(VALID(OutStream)); + assert(VALID(format)); + + memset(&data, 0, sizeof(data)); + data.OutStream = OutStream; + data.location = destination; + data.max = destinationSize; + data.error = 0; + +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + + status = TrioParse(TYPE_PRINT, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioFormatProcess(&data, format, parameters); + if (data.error != 0) + { + status = data.error; + } + return status; +} + +/************************************************************************* + * TrioOutStreamFile + */ +TRIO_PRIVATE void +TrioOutStreamFile +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + FILE *file; + + assert(VALID(self)); + assert(VALID(self->location)); + + file = (FILE *)self->location; + self->processed++; + if (fputc(output, file) == EOF) + { + self->error = TRIO_ERROR_RETURN(TRIO_EOF, 0); + } + else + { + self->committed++; + } +} + +/************************************************************************* + * TrioOutStreamFileDescriptor + */ +TRIO_PRIVATE void +TrioOutStreamFileDescriptor +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + int fd; + char ch; + + assert(VALID(self)); + + fd = *((int *)self->location); + ch = (char)output; + self->processed++; + if (write(fd, &ch, sizeof(char)) == -1) + { + self->error = TRIO_ERROR_RETURN(TRIO_ERRNO, 0); + } + else + { + self->committed++; + } +} + +/************************************************************************* + * TrioOutStreamCustom + */ +TRIO_PRIVATE void +TrioOutStreamCustom +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + int status; + trio_custom_t *data; + + assert(VALID(self)); + assert(VALID(self->location)); + + data = (trio_custom_t *)self->location; + if (data->stream.out) + { + status = (data->stream.out)(data->closure, output); + if (status >= 0) + { + self->committed++; + } + else + { + if (self->error == 0) + { + self->error = TRIO_ERROR_RETURN(TRIO_ECUSTOM, -status); + } + } + } + self->processed++; +} + +/************************************************************************* + * TrioOutStreamString + */ +TRIO_PRIVATE void +TrioOutStreamString +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (char **)self->location; + **buffer = (char)output; + (*buffer)++; + self->processed++; + self->committed++; +} + +/************************************************************************* + * TrioOutStreamStringMax + */ +TRIO_PRIVATE void +TrioOutStreamStringMax +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (char **)self->location; + + if (self->processed < self->max) + { + **buffer = (char)output; + (*buffer)++; + self->committed++; + } + self->processed++; +} + +/************************************************************************* + * TrioOutStreamStringDynamic + */ +TRIO_PRIVATE void +TrioOutStreamStringDynamic +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + assert(VALID(self)); + assert(VALID(self->location)); + + if (self->error == 0) + { + trio_xstring_append_char((trio_string_t *)self->location, + (char)output); + self->committed++; + } + /* The processed variable must always be increased */ + self->processed++; +} + +/************************************************************************* + * + * Formatted printing functions + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_printf.h" +#endif +/** @addtogroup Printf + @{ +*/ + +/************************************************************************* + * printf + */ + +/** + Print to standard output stream. + + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_printf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(stdout, 0, TrioOutStreamFile, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to standard output stream. + + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vprintf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioFormat(stdout, 0, TrioOutStreamFile, format, &args, NULL); +} + +/** + Print to standard output stream. + + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_printfv +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + trio_pointer_t * args) +{ + assert(VALID(format)); + + return TrioFormat(stdout, 0, TrioOutStreamFile, format, NULL, args); +} + +/************************************************************************* + * fprintf + */ + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_fprintf +TRIO_VARGS3((file, format, va_alist), + FILE *file, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(file)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(file, 0, TrioOutStreamFile, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vfprintf +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioFormat(file, 0, TrioOutStreamFile, format, &args, NULL); +} + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_fprintfv +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + trio_pointer_t * args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioFormat(file, 0, TrioOutStreamFile, format, NULL, args); +} + +/************************************************************************* + * dprintf + */ + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_dprintf +TRIO_VARGS3((fd, format, va_alist), + int fd, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vdprintf +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, &args, NULL); +} + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_dprintfv +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, NULL, args); +} + +/************************************************************************* + * cprintf + */ +TRIO_PUBLIC int +trio_cprintf +TRIO_VARGS4((stream, closure, format, va_alist), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + data.stream.out = stream; + data.closure = closure; + status = TrioFormat(&data, 0, TrioOutStreamCustom, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vcprintf +TRIO_ARGS4((stream, closure, format, args), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + va_list args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.out = stream; + data.closure = closure; + return TrioFormat(&data, 0, TrioOutStreamCustom, format, &args, NULL); +} + +TRIO_PUBLIC int +trio_cprintfv +TRIO_ARGS4((stream, closure, format, args), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + void **args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.out = stream; + data.closure = closure; + return TrioFormat(&data, 0, TrioOutStreamCustom, format, NULL, args); +} + +/************************************************************************* + * sprintf + */ + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_sprintf +TRIO_VARGS3((buffer, format, va_alist), + char *buffer, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, &args, NULL); + *buffer = NIL; /* Terminate with NIL character */ + TRIO_VA_END(args); + return status; +} + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vsprintf +TRIO_ARGS3((buffer, format, args), + char *buffer, + TRIO_CONST char *format, + va_list args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, &args, NULL); + *buffer = NIL; + return status; +} + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_sprintfv +TRIO_ARGS3((buffer, format, args), + char *buffer, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, NULL, args); + *buffer = NIL; + return status; +} + +/************************************************************************* + * snprintf + */ + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_snprintf +TRIO_VARGS4((buffer, max, format, va_alist), + char *buffer, + size_t max, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, &args, NULL); + if (max > 0) + *buffer = NIL; + TRIO_VA_END(args); + return status; +} + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vsnprintf +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + va_list args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, &args, NULL); + if (max > 0) + *buffer = NIL; + return status; +} + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_snprintfv +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, NULL, args); + if (max > 0) + *buffer = NIL; + return status; +} + +/************************************************************************* + * snprintfcat + * Appends the new string to the buffer string overwriting the '\0' + * character at the end of buffer. + */ +TRIO_PUBLIC int +trio_snprintfcat +TRIO_VARGS4((buffer, max, format, va_alist), + char *buffer, + size_t max, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + size_t buf_len; + + TRIO_VA_START(args, format); + + assert(VALID(buffer)); + assert(VALID(format)); + + buf_len = trio_length(buffer); + buffer = &buffer[buf_len]; + + status = TrioFormat(&buffer, max - 1 - buf_len, + TrioOutStreamStringMax, format, &args, NULL); + TRIO_VA_END(args); + *buffer = NIL; + return status; +} + +TRIO_PUBLIC int +trio_vsnprintfcat +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + va_list args) +{ + int status; + size_t buf_len; + + assert(VALID(buffer)); + assert(VALID(format)); + + buf_len = trio_length(buffer); + buffer = &buffer[buf_len]; + status = TrioFormat(&buffer, max - 1 - buf_len, + TrioOutStreamStringMax, format, &args, NULL); + *buffer = NIL; + return status; +} + +/************************************************************************* + * trio_aprintf + */ + +/* Deprecated */ +TRIO_PUBLIC char * +trio_aprintf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + va_list args; + trio_string_t *info; + char *result = NULL; + + assert(VALID(format)); + + info = trio_xstring_duplicate(""); + if (info) + { + TRIO_VA_START(args, format); + (void)TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + TRIO_VA_END(args); + + trio_string_terminate(info); + result = trio_string_extract(info); + trio_string_destroy(info); + } + return result; +} + +/* Deprecated */ +TRIO_PUBLIC char * +trio_vaprintf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + trio_string_t *info; + char *result = NULL; + + assert(VALID(format)); + + info = trio_xstring_duplicate(""); + if (info) + { + (void)TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + trio_string_terminate(info); + result = trio_string_extract(info); + trio_string_destroy(info); + } + return result; +} + +TRIO_PUBLIC int +trio_asprintf +TRIO_VARGS3((result, format, va_alist), + char **result, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + va_list args; + int status; + trio_string_t *info; + + assert(VALID(format)); + + *result = NULL; + + info = trio_xstring_duplicate(""); + if (info == NULL) + { + status = TRIO_ERROR_RETURN(TRIO_ENOMEM, 0); + } + else + { + TRIO_VA_START(args, format); + status = TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + TRIO_VA_END(args); + if (status >= 0) + { + trio_string_terminate(info); + *result = trio_string_extract(info); + } + trio_string_destroy(info); + } + return status; +} + +TRIO_PUBLIC int +trio_vasprintf +TRIO_ARGS3((result, format, args), + char **result, + TRIO_CONST char *format, + va_list args) +{ + int status; + trio_string_t *info; + + assert(VALID(format)); + + *result = NULL; + + info = trio_xstring_duplicate(""); + if (info == NULL) + { + status = TRIO_ERROR_RETURN(TRIO_ENOMEM, 0); + } + else + { + status = TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + if (status >= 0) + { + trio_string_terminate(info); + *result = trio_string_extract(info); + } + trio_string_destroy(info); + } + return status; +} + +/** @} End of Printf documentation module */ + +/************************************************************************* + * + * CALLBACK + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_register.h" +#endif +/** + @addtogroup UserDefined + @{ +*/ + +#if TRIO_EXTENSION + +/************************************************************************* + * trio_register + */ + +/** + Register new user-defined specifier. + + @param callback + @param name + @return Handle. + */ +TRIO_PUBLIC trio_pointer_t +trio_register +TRIO_ARGS2((callback, name), + trio_callback_t callback, + TRIO_CONST char *name) +{ + trio_userdef_t *def; + trio_userdef_t *prev = NULL; + + if (callback == NULL) + return NULL; + + if (name) + { + /* Handle built-in namespaces */ + if (name[0] == ':') + { + if (trio_equal(name, ":enter")) + { + internalEnterCriticalRegion = callback; + } + else if (trio_equal(name, ":leave")) + { + internalLeaveCriticalRegion = callback; + } + return NULL; + } + + /* Bail out if namespace is too long */ + if (trio_length(name) >= MAX_USER_NAME) + return NULL; + + /* Bail out if namespace already is registered */ + def = TrioFindNamespace(name, &prev); + if (def) + return NULL; + } + + def = (trio_userdef_t *)TRIO_MALLOC(sizeof(trio_userdef_t)); + if (def) + { + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + if (name) + { + /* Link into internal list */ + if (prev == NULL) + internalUserDef = def; + else + prev->next = def; + } + /* Initialize */ + def->callback = callback; + def->name = (name == NULL) + ? NULL + : trio_duplicate(name); + def->next = NULL; + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + } + return (trio_pointer_t)def; +} + +/** + Unregister an existing user-defined specifier. + + @param handle + */ +void +trio_unregister +TRIO_ARGS1((handle), + trio_pointer_t handle) +{ + trio_userdef_t *self = (trio_userdef_t *)handle; + trio_userdef_t *def; + trio_userdef_t *prev = NULL; + + assert(VALID(self)); + + if (self->name) + { + def = TrioFindNamespace(self->name, &prev); + if (def) + { + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + if (prev == NULL) + internalUserDef = NULL; + else + prev->next = def->next; + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + } + trio_destroy(self->name); + } + TRIO_FREE(self); +} + +/************************************************************************* + * trio_get_format [public] + */ +TRIO_CONST char * +trio_get_format +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ +#if defined(FORMAT_USER_DEFINED) + assert(((trio_reference_t *)ref)->parameter->type == FORMAT_USER_DEFINED); +#endif + + return (((trio_reference_t *)ref)->parameter->user_data); +} + +/************************************************************************* + * trio_get_argument [public] + */ +trio_pointer_t +trio_get_argument +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ +#if defined(FORMAT_USER_DEFINED) + assert(((trio_reference_t *)ref)->parameter->type == FORMAT_USER_DEFINED); +#endif + + return ((trio_reference_t *)ref)->parameter->data.pointer; +} + +/************************************************************************* + * trio_get_width / trio_set_width [public] + */ +int +trio_get_width +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return ((trio_reference_t *)ref)->parameter->width; +} + +void +trio_set_width +TRIO_ARGS2((ref, width), + trio_pointer_t ref, + int width) +{ + ((trio_reference_t *)ref)->parameter->width = width; +} + +/************************************************************************* + * trio_get_precision / trio_set_precision [public] + */ +int +trio_get_precision +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->precision); +} + +void +trio_set_precision +TRIO_ARGS2((ref, precision), + trio_pointer_t ref, + int precision) +{ + ((trio_reference_t *)ref)->parameter->precision = precision; +} + +/************************************************************************* + * trio_get_base / trio_set_base [public] + */ +int +trio_get_base +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->base); +} + +void +trio_set_base +TRIO_ARGS2((ref, base), + trio_pointer_t ref, + int base) +{ + ((trio_reference_t *)ref)->parameter->base = base; +} + +/************************************************************************* + * trio_get_long / trio_set_long [public] + */ +int +trio_get_long +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LONG); +} + +void +trio_set_long +TRIO_ARGS2((ref, is_long), + trio_pointer_t ref, + int is_long) +{ + if (is_long) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LONG; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LONG; +} + +/************************************************************************* + * trio_get_longlong / trio_set_longlong [public] + */ +int +trio_get_longlong +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_QUAD); +} + +void +trio_set_longlong +TRIO_ARGS2((ref, is_longlong), + trio_pointer_t ref, + int is_longlong) +{ + if (is_longlong) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_QUAD; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_QUAD; +} + +/************************************************************************* + * trio_get_longdouble / trio_set_longdouble [public] + */ +int +trio_get_longdouble +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LONGDOUBLE); +} + +void +trio_set_longdouble +TRIO_ARGS2((ref, is_longdouble), + trio_pointer_t ref, + int is_longdouble) +{ + if (is_longdouble) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LONGDOUBLE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LONGDOUBLE; +} + +/************************************************************************* + * trio_get_short / trio_set_short [public] + */ +int +trio_get_short +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHORT); +} + +void +trio_set_short +TRIO_ARGS2((ref, is_short), + trio_pointer_t ref, + int is_short) +{ + if (is_short) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHORT; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHORT; +} + +/************************************************************************* + * trio_get_shortshort / trio_set_shortshort [public] + */ +int +trio_get_shortshort +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHORTSHORT); +} + +void +trio_set_shortshort +TRIO_ARGS2((ref, is_shortshort), + trio_pointer_t ref, + int is_shortshort) +{ + if (is_shortshort) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHORTSHORT; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHORTSHORT; +} + +/************************************************************************* + * trio_get_alternative / trio_set_alternative [public] + */ +int +trio_get_alternative +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_ALTERNATIVE); +} + +void +trio_set_alternative +TRIO_ARGS2((ref, is_alternative), + trio_pointer_t ref, + int is_alternative) +{ + if (is_alternative) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_ALTERNATIVE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_ALTERNATIVE; +} + +/************************************************************************* + * trio_get_alignment / trio_set_alignment [public] + */ +int +trio_get_alignment +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LEFTADJUST); +} + +void +trio_set_alignment +TRIO_ARGS2((ref, is_leftaligned), + trio_pointer_t ref, + int is_leftaligned) +{ + if (is_leftaligned) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LEFTADJUST; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LEFTADJUST; +} + +/************************************************************************* + * trio_get_spacing /trio_set_spacing [public] + */ +int +trio_get_spacing +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SPACE); +} + +void +trio_set_spacing +TRIO_ARGS2((ref, is_space), + trio_pointer_t ref, + int is_space) +{ + if (is_space) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SPACE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SPACE; +} + +/************************************************************************* + * trio_get_sign / trio_set_sign [public] + */ +int +trio_get_sign +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHOWSIGN); +} + +void +trio_set_sign +TRIO_ARGS2((ref, is_sign), + trio_pointer_t ref, + int is_sign) +{ + if (is_sign) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHOWSIGN; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHOWSIGN; +} + +/************************************************************************* + * trio_get_padding / trio_set_padding [public] + */ +int +trio_get_padding +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_NILPADDING); +} + +void +trio_set_padding +TRIO_ARGS2((ref, is_padding), + trio_pointer_t ref, + int is_padding) +{ + if (is_padding) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_NILPADDING; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_NILPADDING; +} + +/************************************************************************* + * trio_get_quote / trio_set_quote [public] + */ +int +trio_get_quote +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_QUOTE); +} + +void +trio_set_quote +TRIO_ARGS2((ref, is_quote), + trio_pointer_t ref, + int is_quote) +{ + if (is_quote) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_QUOTE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_QUOTE; +} + +/************************************************************************* + * trio_get_upper / trio_set_upper [public] + */ +int +trio_get_upper +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_UPPER); +} + +void +trio_set_upper +TRIO_ARGS2((ref, is_upper), + trio_pointer_t ref, + int is_upper) +{ + if (is_upper) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_UPPER; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_UPPER; +} + +/************************************************************************* + * trio_get_largest / trio_set_largest [public] + */ +#if TRIO_C99 +int +trio_get_largest +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_INTMAX_T); +} + +void +trio_set_largest +TRIO_ARGS2((ref, is_largest), + trio_pointer_t ref, + int is_largest) +{ + if (is_largest) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_INTMAX_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_INTMAX_T; +} +#endif + +/************************************************************************* + * trio_get_ptrdiff / trio_set_ptrdiff [public] + */ +int +trio_get_ptrdiff +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_PTRDIFF_T); +} + +void +trio_set_ptrdiff +TRIO_ARGS2((ref, is_ptrdiff), + trio_pointer_t ref, + int is_ptrdiff) +{ + if (is_ptrdiff) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_PTRDIFF_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_PTRDIFF_T; +} + +/************************************************************************* + * trio_get_size / trio_set_size [public] + */ +#if TRIO_C99 +int +trio_get_size +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SIZE_T); +} + +void +trio_set_size +TRIO_ARGS2((ref, is_size), + trio_pointer_t ref, + int is_size) +{ + if (is_size) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SIZE_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SIZE_T; +} +#endif + +/************************************************************************* + * trio_print_int [public] + */ +void +trio_print_int +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + int number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteNumber(self->data, + (trio_uintmax_t)number, + self->parameter->flags, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_uint [public] + */ +void +trio_print_uint +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + unsigned int number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteNumber(self->data, + (trio_uintmax_t)number, + self->parameter->flags | FLAGS_UNSIGNED, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_double [public] + */ +void +trio_print_double +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + double number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteDouble(self->data, + number, + self->parameter->flags, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_string [public] + */ +void +trio_print_string +TRIO_ARGS2((ref, string), + trio_pointer_t ref, + char *string) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteString(self->data, + string, + self->parameter->flags, + self->parameter->width, + self->parameter->precision); +} + +/************************************************************************* + * trio_print_ref [public] + */ +int +trio_print_ref +TRIO_VARGS3((ref, format, va_alist), + trio_pointer_t ref, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list arglist; + + assert(VALID(format)); + + TRIO_VA_START(arglist, format); + status = TrioFormatRef((trio_reference_t *)ref, format, &arglist, NULL); + TRIO_VA_END(arglist); + return status; +} + +/************************************************************************* + * trio_vprint_ref [public] + */ +int +trio_vprint_ref +TRIO_ARGS3((ref, format, arglist), + trio_pointer_t ref, + TRIO_CONST char *format, + va_list arglist) +{ + assert(VALID(format)); + + return TrioFormatRef((trio_reference_t *)ref, format, &arglist, NULL); +} + +/************************************************************************* + * trio_printv_ref [public] + */ +int +trio_printv_ref +TRIO_ARGS3((ref, format, argarray), + trio_pointer_t ref, + TRIO_CONST char *format, + trio_pointer_t *argarray) +{ + assert(VALID(format)); + + return TrioFormatRef((trio_reference_t *)ref, format, NULL, argarray); +} + +#endif /* TRIO_EXTENSION */ + +/************************************************************************* + * trio_print_pointer [public] + */ +void +trio_print_pointer +TRIO_ARGS2((ref, pointer), + trio_pointer_t ref, + trio_pointer_t pointer) +{ + trio_reference_t *self = (trio_reference_t *)ref; + unsigned long flags; + trio_uintmax_t number; + + if (NULL == pointer) + { + TRIO_CONST char *string = internalNullString; + while (*string) + self->data->OutStream(self->data, *string++); + } + else + { + /* + * The subtraction of the null pointer is a workaround + * to avoid a compiler warning. The performance overhead + * is negligible (and likely to be removed by an + * optimizing compiler). The (char *) casting is done + * to please ANSI C++. + */ + number = (trio_uintmax_t)((char *)pointer - (char *)0); + /* Shrink to size of pointer */ + number &= (trio_uintmax_t)-1; + flags = self->parameter->flags; + flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE | + FLAGS_NILPADDING); + TrioWriteNumber(self->data, + number, + flags, + POINTER_WIDTH, + NO_PRECISION, + BASE_HEX); + } +} + +/** @} End of UserDefined documentation module */ + +/************************************************************************* + * + * LOCALES + * + ************************************************************************/ + +/************************************************************************* + * trio_locale_set_decimal_point + * + * Decimal point can only be one character. The input argument is a + * string to enable multibyte characters. At most MB_LEN_MAX characters + * will be used. + */ +TRIO_PUBLIC void +trio_locale_set_decimal_point +TRIO_ARGS1((decimalPoint), + char *decimalPoint) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + internalDecimalPointLength = trio_length(decimalPoint); + if (internalDecimalPointLength == 1) + { + internalDecimalPoint = *decimalPoint; + } + else + { + internalDecimalPoint = NIL; + trio_copy_max(internalDecimalPointString, + sizeof(internalDecimalPointString), + decimalPoint); + } +} + +/************************************************************************* + * trio_locale_set_thousand_separator + * + * See trio_locale_set_decimal_point + */ +TRIO_PUBLIC void +trio_locale_set_thousand_separator +TRIO_ARGS1((thousandSeparator), + char *thousandSeparator) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + trio_copy_max(internalThousandSeparator, + sizeof(internalThousandSeparator), + thousandSeparator); + internalThousandSeparatorLength = trio_length(internalThousandSeparator); +} + +/************************************************************************* + * trio_locale_set_grouping + * + * Array of bytes. Reversed order. + * + * CHAR_MAX : No further grouping + * 0 : Repeat last group for the remaining digits (not necessary + * as C strings are zero-terminated) + * n : Set current group to n + * + * Same order as the grouping attribute in LC_NUMERIC. + */ +TRIO_PUBLIC void +trio_locale_set_grouping +TRIO_ARGS1((grouping), + char *grouping) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + trio_copy_max(internalGrouping, + sizeof(internalGrouping), + grouping); +} + + +/************************************************************************* + * + * SCANNING + * + ************************************************************************/ + +/************************************************************************* + * TrioSkipWhitespaces + */ +TRIO_PRIVATE int +TrioSkipWhitespaces +TRIO_ARGS1((self), + trio_class_t *self) +{ + int ch; + + ch = self->current; + while (isspace(ch)) + { + self->InStream(self, &ch); + } + return ch; +} + +/************************************************************************* + * TrioGetCollation + */ +#if TRIO_EXTENSION +TRIO_PRIVATE void +TrioGetCollation(TRIO_NOARGS) +{ + int i; + int j; + int k; + char first[2]; + char second[2]; + + /* This is computational expensive */ + first[1] = NIL; + second[1] = NIL; + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + { + k = 0; + first[0] = (char)i; + for (j = 0; j < MAX_CHARACTER_CLASS; j++) + { + second[0] = (char)j; + if (trio_equal_locale(first, second)) + internalCollationArray[i][k++] = (char)j; + } + internalCollationArray[i][k] = NIL; + } +} +#endif + +/************************************************************************* + * TrioGetCharacterClass + * + * FIXME: + * multibyte + */ +TRIO_PRIVATE int +TrioGetCharacterClass +TRIO_ARGS4((format, indexPointer, flagsPointer, characterclass), + TRIO_CONST char *format, + int *indexPointer, + unsigned long *flagsPointer, + int *characterclass) +{ + int index = *indexPointer; + int i; + char ch; + char range_begin; + char range_end; + + *flagsPointer &= ~FLAGS_EXCLUDE; + + if (format[index] == QUALIFIER_CIRCUMFLEX) + { + *flagsPointer |= FLAGS_EXCLUDE; + index++; + } + /* + * If the ungroup character is at the beginning of the scanlist, + * it will be part of the class, and a second ungroup character + * must follow to end the group. + */ + if (format[index] == SPECIFIER_UNGROUP) + { + characterclass[(int)SPECIFIER_UNGROUP]++; + index++; + } + /* + * Minus is used to specify ranges. To include minus in the class, + * it must be at the beginning of the list + */ + if (format[index] == QUALIFIER_MINUS) + { + characterclass[(int)QUALIFIER_MINUS]++; + index++; + } + /* Collect characters */ + for (ch = format[index]; + (ch != SPECIFIER_UNGROUP) && (ch != NIL); + ch = format[++index]) + { + switch (ch) + { + case QUALIFIER_MINUS: /* Scanlist ranges */ + + /* + * Both C99 and UNIX98 describes ranges as implementation- + * defined. + * + * We support the following behaviour (although this may + * change as we become wiser) + * - only increasing ranges, ie. [a-b] but not [b-a] + * - transitive ranges, ie. [a-b-c] == [a-c] + * - trailing minus, ie. [a-] is interpreted as an 'a' + * and a '-' + * - duplicates (although we can easily convert these + * into errors) + */ + range_begin = format[index - 1]; + range_end = format[++index]; + if (range_end == SPECIFIER_UNGROUP) + { + /* Trailing minus is included */ + characterclass[(int)ch]++; + ch = range_end; + break; /* for */ + } + if (range_end == NIL) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + if (range_begin > range_end) + return TRIO_ERROR_RETURN(TRIO_ERANGE, index); + + for (i = (int)range_begin; i <= (int)range_end; i++) + characterclass[i]++; + + ch = range_end; + break; + +#if TRIO_EXTENSION + + case SPECIFIER_GROUP: + + switch (format[index + 1]) + { + case QUALIFIER_DOT: /* Collating symbol */ + /* + * FIXME: This will be easier to implement when multibyte + * characters have been implemented. Until now, we ignore + * this feature. + */ + for (i = index + 2; ; i++) + { + if (format[i] == NIL) + /* Error in syntax */ + return -1; + else if (format[i] == QUALIFIER_DOT) + break; /* for */ + } + if (format[++i] != SPECIFIER_UNGROUP) + return -1; + + index = i; + break; + + case QUALIFIER_EQUAL: /* Equivalence class expressions */ + { + unsigned int j; + unsigned int k; + + if (internalCollationUnconverted) + { + /* Lazy evaluation of collation array */ + TrioGetCollation(); + internalCollationUnconverted = FALSE; + } + for (i = index + 2; ; i++) + { + if (format[i] == NIL) + /* Error in syntax */ + return -1; + else if (format[i] == QUALIFIER_EQUAL) + break; /* for */ + else + { + /* Mark any equivalent character */ + k = (unsigned int)format[i]; + for (j = 0; internalCollationArray[k][j] != NIL; j++) + characterclass[(int)internalCollationArray[k][j]]++; + } + } + if (format[++i] != SPECIFIER_UNGROUP) + return -1; + + index = i; + } + break; + + case QUALIFIER_COLON: /* Character class expressions */ + + if (trio_equal_max(CLASS_ALNUM, sizeof(CLASS_ALNUM) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isalnum(i)) + characterclass[i]++; + index += sizeof(CLASS_ALNUM) - 1; + } + else if (trio_equal_max(CLASS_ALPHA, sizeof(CLASS_ALPHA) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isalpha(i)) + characterclass[i]++; + index += sizeof(CLASS_ALPHA) - 1; + } + else if (trio_equal_max(CLASS_CNTRL, sizeof(CLASS_CNTRL) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (iscntrl(i)) + characterclass[i]++; + index += sizeof(CLASS_CNTRL) - 1; + } + else if (trio_equal_max(CLASS_DIGIT, sizeof(CLASS_DIGIT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isdigit(i)) + characterclass[i]++; + index += sizeof(CLASS_DIGIT) - 1; + } + else if (trio_equal_max(CLASS_GRAPH, sizeof(CLASS_GRAPH) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isgraph(i)) + characterclass[i]++; + index += sizeof(CLASS_GRAPH) - 1; + } + else if (trio_equal_max(CLASS_LOWER, sizeof(CLASS_LOWER) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (islower(i)) + characterclass[i]++; + index += sizeof(CLASS_LOWER) - 1; + } + else if (trio_equal_max(CLASS_PRINT, sizeof(CLASS_PRINT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isprint(i)) + characterclass[i]++; + index += sizeof(CLASS_PRINT) - 1; + } + else if (trio_equal_max(CLASS_PUNCT, sizeof(CLASS_PUNCT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (ispunct(i)) + characterclass[i]++; + index += sizeof(CLASS_PUNCT) - 1; + } + else if (trio_equal_max(CLASS_SPACE, sizeof(CLASS_SPACE) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isspace(i)) + characterclass[i]++; + index += sizeof(CLASS_SPACE) - 1; + } + else if (trio_equal_max(CLASS_UPPER, sizeof(CLASS_UPPER) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isupper(i)) + characterclass[i]++; + index += sizeof(CLASS_UPPER) - 1; + } + else if (trio_equal_max(CLASS_XDIGIT, sizeof(CLASS_XDIGIT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isxdigit(i)) + characterclass[i]++; + index += sizeof(CLASS_XDIGIT) - 1; + } + else + { + characterclass[(int)ch]++; + } + break; + + default: + characterclass[(int)ch]++; + break; + } + break; + +#endif /* TRIO_EXTENSION */ + + default: + characterclass[(int)ch]++; + break; + } + } + return 0; +} + +/************************************************************************* + * TrioReadNumber + * + * We implement our own number conversion in preference of strtol and + * strtoul, because we must handle 'long long' and thousand separators. + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadNumber +TRIO_ARGS5((self, target, flags, width, base), + trio_class_t *self, + trio_uintmax_t *target, + unsigned long flags, + int width, + int base) +{ + trio_uintmax_t number = 0; + int digit; + int count; + BOOLEAN_T isNegative = FALSE; + BOOLEAN_T gotNumber = FALSE; + int j; + + assert(VALID(self)); + assert(VALID(self->InStream)); + assert((base >= MIN_BASE && base <= MAX_BASE) || (base == NO_BASE)); + + if (internalDigitsUnconverted) + { + /* Lazy evaluation of digits array */ + memset(internalDigitArray, -1, sizeof(internalDigitArray)); + for (j = 0; j < (int)sizeof(internalDigitsLower) - 1; j++) + { + internalDigitArray[(int)internalDigitsLower[j]] = j; + internalDigitArray[(int)internalDigitsUpper[j]] = j; + } + internalDigitsUnconverted = FALSE; + } + + TrioSkipWhitespaces(self); + + if (!(flags & FLAGS_UNSIGNED)) + { + /* Leading sign */ + if (self->current == '+') + { + self->InStream(self, NULL); + } + else if (self->current == '-') + { + self->InStream(self, NULL); + isNegative = TRUE; + } + } + + count = self->processed; + + if (flags & FLAGS_ALTERNATIVE) + { + switch (base) + { + case NO_BASE: + case BASE_OCTAL: + case BASE_HEX: + case BASE_BINARY: + if (self->current == '0') + { + self->InStream(self, NULL); + if (self->current) + { + if ((base == BASE_HEX) && + (toupper(self->current) == 'X')) + { + self->InStream(self, NULL); + } + else if ((base == BASE_BINARY) && + (toupper(self->current) == 'B')) + { + self->InStream(self, NULL); + } + } + } + else + return FALSE; + break; + default: + break; + } + } + + while (((width == NO_WIDTH) || (self->processed - count < width)) && + (! ((self->current == EOF) || isspace(self->current)))) + { + if (isascii(self->current)) + { + digit = internalDigitArray[self->current]; + /* Abort if digit is not allowed in the specified base */ + if ((digit == -1) || (digit >= base)) + break; + } + else if (flags & FLAGS_QUOTE) + { + /* Compare with thousands separator */ + for (j = 0; internalThousandSeparator[j] && self->current; j++) + { + if (internalThousandSeparator[j] != self->current) + break; + + self->InStream(self, NULL); + } + if (internalThousandSeparator[j]) + break; /* Mismatch */ + else + continue; /* Match */ + } + else + break; + + number *= base; + number += digit; + gotNumber = TRUE; /* we need at least one digit */ + + self->InStream(self, NULL); + } + + /* Was anything read at all? */ + if (!gotNumber) + return FALSE; + + if (target) + *target = (isNegative) ? -((trio_intmax_t)number) : number; + return TRUE; +} + +/************************************************************************* + * TrioReadChar + */ +TRIO_PRIVATE int +TrioReadChar +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + char *target, + unsigned long flags, + int width) +{ + int i; + char ch; + trio_uintmax_t number; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + for (i = 0; + (self->current != EOF) && (i < width); + i++) + { + ch = (char)self->current; + self->InStream(self, NULL); + if ((flags & FLAGS_ALTERNATIVE) && (ch == CHAR_BACKSLASH)) + { + switch (self->current) + { + case '\\': ch = '\\'; break; + case 'a': ch = '\007'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + default: + if (isdigit(self->current)) + { + /* Read octal number */ + if (!TrioReadNumber(self, &number, 0, 3, BASE_OCTAL)) + return 0; + ch = (char)number; + } + else if (toupper(self->current) == 'X') + { + /* Read hexadecimal number */ + self->InStream(self, NULL); + if (!TrioReadNumber(self, &number, 0, 2, BASE_HEX)) + return 0; + ch = (char)number; + } + else + { + ch = (char)self->current; + } + break; + } + } + + if (target) + target[i] = ch; + } + return i + 1; +} + +/************************************************************************* + * TrioReadString + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadString +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + char *target, + unsigned long flags, + int width) +{ + int i; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + TrioSkipWhitespaces(self); + + /* + * Continue until end of string is reached, a whitespace is encountered, + * or width is exceeded + */ + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((self->current == EOF) || isspace(self->current))); + i++) + { + if (TrioReadChar(self, (target ? &target[i] : 0), flags, 1) == 0) + break; /* for */ + } + if (target) + target[i] = NIL; + return TRUE; +} + +/************************************************************************* + * TrioReadWideChar + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE int +TrioReadWideChar +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_wchar_t *target, + unsigned long flags, + int width) +{ + int i; + int j; + int size; + int amount = 0; + trio_wchar_t wch; + char buffer[MB_LEN_MAX + 1]; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + for (i = 0; + (self->current != EOF) && (i < width); + i++) + { + if (isascii(self->current)) + { + if (TrioReadChar(self, buffer, flags, 1) == 0) + return 0; + buffer[1] = NIL; + } + else + { + /* + * Collect a multibyte character, by enlarging buffer until + * it contains a fully legal multibyte character, or the + * buffer is full. + */ + j = 0; + do + { + buffer[j++] = (char)self->current; + buffer[j] = NIL; + self->InStream(self, NULL); + } + while ((j < (int)sizeof(buffer)) && (mblen(buffer, (size_t)j) != j)); + } + if (target) + { + size = mbtowc(&wch, buffer, sizeof(buffer)); + if (size > 0) + target[i] = wch; + } + amount += size; + self->InStream(self, NULL); + } + return amount; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioReadWideString + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE BOOLEAN_T +TrioReadWideString +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_wchar_t *target, + unsigned long flags, + int width) +{ + int i; + int size; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + TrioSkipWhitespaces(self); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + /* + * Continue until end of string is reached, a whitespace is encountered, + * or width is exceeded + */ + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((self->current == EOF) || isspace(self->current))); + ) + { + size = TrioReadWideChar(self, &target[i], flags, 1); + if (size == 0) + break; /* for */ + + i += size; + } + if (target) + target[i] = WCONST('\0'); + return TRUE; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioReadGroup + * + * FIXME: characterclass does not work with multibyte characters + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadGroup +TRIO_ARGS5((self, target, characterclass, flags, width), + trio_class_t *self, + char *target, + int *characterclass, + unsigned long flags, + int width) +{ + int ch; + int i; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + ch = self->current; + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((ch == EOF) || + (((flags & FLAGS_EXCLUDE) != 0) ^ (characterclass[ch] == 0)))); + i++) + { + if (target) + target[i] = (char)ch; + self->InStream(self, &ch); + } + + if (target) + target[i] = NIL; + return TRUE; +} + +/************************************************************************* + * TrioReadDouble + * + * FIXME: + * add long double + * handle base + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadDouble +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_pointer_t target, + unsigned long flags, + int width) +{ + int ch; + char doubleString[512]; + int index = 0; + int start; + int j; + BOOLEAN_T isHex = FALSE; + + doubleString[0] = 0; + + if ((width == NO_WIDTH) || (width > (int)sizeof(doubleString) - 1)) + width = sizeof(doubleString) - 1; + + TrioSkipWhitespaces(self); + + /* + * Read entire double number from stream. trio_to_double requires + * a string as input, but InStream can be anything, so we have to + * collect all characters. + */ + ch = self->current; + if ((ch == '+') || (ch == '-')) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + width--; + } + + start = index; + switch (ch) + { + case 'n': + case 'N': + /* Not-a-number */ + if (index != 0) + break; + /* FALLTHROUGH */ + case 'i': + case 'I': + /* Infinity */ + while (isalpha(ch) && (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + doubleString[index] = NIL; + + /* Case insensitive string comparison */ + if (trio_equal(&doubleString[start], INFINITE_UPPER) || + trio_equal(&doubleString[start], LONG_INFINITE_UPPER)) + { + if (flags & FLAGS_LONGDOUBLE) + { + if ((start == 1) && (doubleString[0] == '-')) + { + *((trio_long_double_t *)target) = trio_ninf(); + } + else + { + *((trio_long_double_t *)target) = trio_pinf(); + } + } + else + { + if ((start == 1) && (doubleString[0] == '-')) + { + *((double *)target) = trio_ninf(); + } + else + { + *((double *)target) = trio_pinf(); + } + } + return TRUE; + } + if (trio_equal(doubleString, NAN_UPPER)) + { + /* NaN must not have a preceeding + nor - */ + if (flags & FLAGS_LONGDOUBLE) + { + *((trio_long_double_t *)target) = trio_nan(); + } + else + { + *((double *)target) = trio_nan(); + } + return TRUE; + } + return FALSE; + + case '0': + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + if (toupper(ch) == 'X') + { + isHex = TRUE; + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + break; + + default: + break; + } + + while ((ch != EOF) && (index - start < width)) + { + /* Integer part */ + if (isHex ? isxdigit(ch) : isdigit(ch)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + else if (flags & FLAGS_QUOTE) + { + /* Compare with thousands separator */ + for (j = 0; internalThousandSeparator[j] && self->current; j++) + { + if (internalThousandSeparator[j] != self->current) + break; + + self->InStream(self, &ch); + } + if (internalThousandSeparator[j]) + break; /* Mismatch */ + else + continue; /* Match */ + } + else + break; /* while */ + } + if (ch == '.') + { + /* Decimal part */ + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + while ((isHex ? isxdigit(ch) : isdigit(ch)) && + (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + if (isHex ? (toupper(ch) == 'P') : (toupper(ch) == 'E')) + { + /* Exponent */ + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + if ((ch == '+') || (ch == '-')) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + while ((isHex ? isxdigit(ch) : isdigit(ch)) && + (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + } + } + + if ((index == start) || (*doubleString == NIL)) + return FALSE; + + doubleString[index] = 0; + + if (flags & FLAGS_LONGDOUBLE) + { + *((trio_long_double_t *)target) = trio_to_long_double(doubleString, NULL); + } + else + { + *((double *)target) = trio_to_double(doubleString, NULL); + } + return TRUE; +} + +/************************************************************************* + * TrioReadPointer + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadPointer +TRIO_ARGS3((self, target, flags), + trio_class_t *self, + trio_pointer_t *target, + unsigned long flags) +{ + trio_uintmax_t number; + char buffer[sizeof(internalNullString)]; + + flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE | FLAGS_NILPADDING); + + if (TrioReadNumber(self, + &number, + flags, + POINTER_WIDTH, + BASE_HEX)) + { + /* + * The strange assignment of number is a workaround for a compiler + * warning + */ + if (target) + *target = (char *)0 + number; + return TRUE; + } + else if (TrioReadString(self, + (flags & FLAGS_IGNORE) + ? NULL + : buffer, + 0, + sizeof(internalNullString) - 1)) + { + if (trio_equal_case(buffer, internalNullString)) + { + if (target) + *target = NULL; + return TRUE; + } + } + return FALSE; +} + +/************************************************************************* + * TrioScanProcess + */ +TRIO_PRIVATE int +TrioScanProcess +TRIO_ARGS3((data, format, parameters), + trio_class_t *data, + TRIO_CONST char *format, + trio_parameter_t *parameters) +{ +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; + int cnt; +#endif + int assignment; + int ch; + int index; /* Index of format string */ + int i; /* Index of current parameter */ + unsigned long flags; + int width; + int base; + trio_pointer_t pointer; + + assignment = 0; + i = 0; + index = 0; + data->InStream(data, &ch); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + charlen = mblen(&format[index], MB_LEN_MAX); + if (charlen != -1) + { + /* Compare multibyte characters in format string */ + for (cnt = 0; cnt < charlen - 1; cnt++) + { + if (ch != format[index + cnt]) + { + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + data->InStream(data, &ch); + } + continue; /* while characters left in formatting string */ + } + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + + if ((EOF == ch) && (parameters[i].type != FORMAT_COUNT)) + { + return (assignment > 0) ? assignment : EOF; + } + + if (CHAR_IDENTIFIER == format[index]) + { + if (CHAR_IDENTIFIER == format[index + 1]) + { + /* Two % in format matches one % in input stream */ + if (CHAR_IDENTIFIER == ch) + { + data->InStream(data, &ch); + index += 2; + continue; /* while format chars left */ + } + else + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + + /* Skip the parameter entries */ + while (parameters[i].type == FORMAT_PARAMETER) + i++; + + flags = parameters[i].flags; + /* Find width */ + width = parameters[i].width; + if (flags & FLAGS_WIDTH_PARAMETER) + { + /* Get width from parameter list */ + width = (int)parameters[width].data.number.as_signed; + } + /* Find base */ + base = parameters[i].base; + if (flags & FLAGS_BASE_PARAMETER) + { + /* Get base from parameter list */ + base = (int)parameters[base].data.number.as_signed; + } + + switch (parameters[i].type) + { + case FORMAT_INT: + { + trio_uintmax_t number; + + if (0 == base) + base = BASE_DECIMAL; + + if (!TrioReadNumber(data, + &number, + flags, + width, + base)) + return assignment; + + if (!(flags & FLAGS_IGNORE)) + { + assignment++; + + pointer = parameters[i].data.pointer; +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)number; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)number; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)number; + else +#endif + if (flags & FLAGS_QUAD) + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)number; + else if (flags & FLAGS_LONG) + *(long int *)pointer = (long int)number; + else if (flags & FLAGS_SHORT) + *(short int *)pointer = (short int)number; + else + *(int *)pointer = (int)number; + } + } + break; /* FORMAT_INT */ + + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + if (!TrioReadWideString(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.wstring, + flags, + width)) + return assignment; + } + else +#endif + { + if (!TrioReadString(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + flags, + width)) + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_STRING */ + + case FORMAT_DOUBLE: + { + trio_pointer_t pointer; + + if (flags & FLAGS_IGNORE) + { + pointer = NULL; + } + else + { + pointer = (flags & FLAGS_LONGDOUBLE) + ? (trio_pointer_t)parameters[i].data.longdoublePointer + : (trio_pointer_t)parameters[i].data.doublePointer; + } + if (!TrioReadDouble(data, pointer, flags, width)) + { + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + { + assignment++; + } + break; /* FORMAT_DOUBLE */ + } + case FORMAT_GROUP: + { + int characterclass[MAX_CHARACTER_CLASS + 1]; + int rc; + + /* Skip over modifiers */ + while (format[index] != SPECIFIER_GROUP) + { + index++; + } + /* Skip over group specifier */ + index++; + + memset(characterclass, 0, sizeof(characterclass)); + rc = TrioGetCharacterClass(format, + &index, + &flags, + characterclass); + if (rc < 0) + return rc; + + if (!TrioReadGroup(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + characterclass, + flags, + parameters[i].width)) + return assignment; + if (!(flags & FLAGS_IGNORE)) + assignment++; + } + break; /* FORMAT_GROUP */ + + case FORMAT_COUNT: + pointer = parameters[i].data.pointer; + if (NULL != pointer) + { + int count = data->committed; + if (ch != EOF) + count--; /* a character is read, but is not consumed yet */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)count; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)count; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)count; + else +#endif + if (flags & FLAGS_QUAD) + { + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)count; + } + else if (flags & FLAGS_LONG) + { + *(long int *)pointer = (long int)count; + } + else if (flags & FLAGS_SHORT) + { + *(short int *)pointer = (short int)count; + } + else + { + *(int *)pointer = (int)count; + } + } + break; /* FORMAT_COUNT */ + + case FORMAT_CHAR: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + if (TrioReadWideChar(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.wstring, + flags, + (width == NO_WIDTH) ? 1 : width) == 0) + return assignment; + } + else +#endif + { + if (TrioReadChar(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + flags, + (width == NO_WIDTH) ? 1 : width) == 0) + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_CHAR */ + + case FORMAT_POINTER: + if (!TrioReadPointer(data, + (flags & FLAGS_IGNORE) + ? NULL + : (trio_pointer_t *)parameters[i].data.pointer, + flags)) + return assignment; + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_POINTER */ + + case FORMAT_PARAMETER: + break; /* FORMAT_PARAMETER */ + + default: + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + ch = data->current; + index = parameters[i].indexAfterSpecifier; + i++; + } + else /* Not an % identifier */ + { + if (isspace((int)format[index])) + { + /* Whitespaces may match any amount of whitespaces */ + ch = TrioSkipWhitespaces(data); + } + else if (ch == format[index]) + { + data->InStream(data, &ch); + } + else + return assignment; + + index++; + } + } + return assignment; +} + +/************************************************************************* + * TrioScan + */ +TRIO_PRIVATE int +TrioScan +TRIO_ARGS6((source, sourceSize, InStream, format, arglist, argarray), + trio_pointer_t source, + size_t sourceSize, + void (*InStream) TRIO_PROTO((trio_class_t *, int *)), + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_parameter_t parameters[MAX_PARAMETERS]; + trio_class_t data; + + assert(VALID(InStream)); + assert(VALID(format)); + + memset(&data, 0, sizeof(data)); + data.InStream = InStream; + data.location = (trio_pointer_t)source; + data.max = sourceSize; + data.error = 0; + +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + + status = TrioParse(TYPE_SCAN, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioScanProcess(&data, format, parameters); + if (data.error != 0) + { + status = data.error; + } + return status; +} + +/************************************************************************* + * TrioInStreamFile + */ +TRIO_PRIVATE void +TrioInStreamFile +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + FILE *file = (FILE *)self->location; + + assert(VALID(self)); + assert(VALID(file)); + + self->current = fgetc(file); + if (self->current == EOF) + { + self->error = (ferror(file)) + ? TRIO_ERROR_RETURN(TRIO_ERRNO, 0) + : TRIO_ERROR_RETURN(TRIO_EOF, 0); + } + else + { + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamFileDescriptor + */ +TRIO_PRIVATE void +TrioInStreamFileDescriptor +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + int fd = *((int *)self->location); + int size; + unsigned char input; + + assert(VALID(self)); + + size = read(fd, &input, sizeof(char)); + if (size == -1) + { + self->error = TRIO_ERROR_RETURN(TRIO_ERRNO, 0); + self->current = EOF; + } + else + { + self->current = (size == 0) ? EOF : input; + } + if (self->current != EOF) + { + self->committed++; + self->processed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamCustom + */ +TRIO_PRIVATE void +TrioInStreamCustom +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + trio_custom_t *data; + + assert(VALID(self)); + assert(VALID(self->location)); + + data = (trio_custom_t *)self->location; + + self->current = (data->stream.in == NULL) + ? NIL + : (data->stream.in)(data->closure); + + if (self->current == NIL) + { + self->current = EOF; + } + else + { + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamString + */ +TRIO_PRIVATE void +TrioInStreamString +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + unsigned char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (unsigned char **)self->location; + self->current = (*buffer)[0]; + if (self->current == NIL) + { + self->current = EOF; + } + else + { + (*buffer)++; + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * + * Formatted scanning functions + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_scanf.h" +#endif +/** @addtogroup Scanf + @{ +*/ + +/************************************************************************* + * scanf + */ + +/** + Scan characters from standard input stream. + + @param format Formatting string. + @param ... Arguments. + @return Number of scanned characters. + */ +TRIO_PUBLIC int +trio_scanf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vscanf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_scanfv +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, NULL, args); +} + +/************************************************************************* + * fscanf + */ +TRIO_PUBLIC int +trio_fscanf +TRIO_VARGS3((file, format, va_alist), + FILE *file, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(file)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vfscanf +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_fscanfv +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, NULL, args); +} + +/************************************************************************* + * dscanf + */ +TRIO_PUBLIC int +trio_dscanf +TRIO_VARGS3((fd, format, va_alist), + int fd, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vdscanf +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_dscanfv +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, NULL, args); +} + +/************************************************************************* + * cscanf + */ +TRIO_PUBLIC int +trio_cscanf +TRIO_VARGS4((stream, closure, format, va_alist), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + data.stream.in = stream; + data.closure = closure; + status = TrioScan(&data, 0, TrioInStreamCustom, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vcscanf +TRIO_ARGS4((stream, closure, format, args), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + va_list args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.in = stream; + data.closure = closure; + return TrioScan(&data, 0, TrioInStreamCustom, format, &args, NULL); +} + +TRIO_PUBLIC int +trio_cscanfv +TRIO_ARGS4((stream, closure, format, args), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.in = stream; + data.closure = closure; + return TrioScan(&data, 0, TrioInStreamCustom, format, NULL, args); +} + +/************************************************************************* + * sscanf + */ +TRIO_PUBLIC int +trio_sscanf +TRIO_VARGS3((buffer, format, va_alist), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vsscanf +TRIO_ARGS3((buffer, format, args), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(buffer)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_sscanfv +TRIO_ARGS3((buffer, format, args), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(buffer)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, NULL, args); +} + +/** @} End of Scanf documentation module */ + +/************************************************************************* + * trio_strerror + */ +TRIO_PUBLIC TRIO_CONST char * +trio_strerror +TRIO_ARGS1((errorcode), + int errorcode) +{ + /* Textual versions of the error codes */ + switch (TRIO_ERROR_CODE(errorcode)) + { + case TRIO_EOF: + return "End of file"; + case TRIO_EINVAL: + return "Invalid argument"; + case TRIO_ETOOMANY: + return "Too many arguments"; + case TRIO_EDBLREF: + return "Double reference"; + case TRIO_EGAP: + return "Reference gap"; + case TRIO_ENOMEM: + return "Out of memory"; + case TRIO_ERANGE: + return "Invalid range"; + case TRIO_ECUSTOM: + return "Custom error"; + default: + return "Unknown"; + } +} diff --git a/glib/trio/trio.h b/glib/trio/trio.h new file mode 100644 index 0000000..770c6a6 --- /dev/null +++ b/glib/trio/trio.h @@ -0,0 +1,216 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************* + * + * http://ctrio.sourceforge.net/ + * + ************************************************************************/ + +#ifndef TRIO_TRIO_H +#define TRIO_TRIO_H + +#include +#include +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif + +#if !defined(WITHOUT_TRIO) + +/* + * Use autoconf defines if present. Packages using trio must define + * HAVE_CONFIG_H as a compiler option themselves. + */ +#if defined(HAVE_CONFIG_H) +# include +#endif + +#include "triodef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Error codes. + * + * Remember to add a textual description to trio_strerror. + */ +enum { + TRIO_EOF = 1, + TRIO_EINVAL = 2, + TRIO_ETOOMANY = 3, + TRIO_EDBLREF = 4, + TRIO_EGAP = 5, + TRIO_ENOMEM = 6, + TRIO_ERANGE = 7, + TRIO_ERRNO = 8, + TRIO_ECUSTOM = 9 +}; + +/* Error macros */ +#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF) +#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8) +#define TRIO_ERROR_NAME(x) trio_strerror(x) + +typedef int (*trio_outstream_t) TRIO_PROTO((trio_pointer_t, int)); +typedef int (*trio_instream_t) TRIO_PROTO((trio_pointer_t)); + +TRIO_CONST char *trio_strerror TRIO_PROTO((int)); + +/************************************************************************* + * Print Functions + */ + +int trio_printf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_printfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fprintfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dprintf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdprintf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dprintfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cprintfv TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, ...)); +int trio_vsprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sprintfv TRIO_PROTO((char *buffer, TRIO_CONST char *format, void **args)); + +int trio_snprintf TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintf TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); +int trio_snprintfv TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + void **args)); + +int trio_snprintfcat TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintfcat TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); + +char *trio_aprintf TRIO_PROTO((TRIO_CONST char *format, ...)); +char *trio_vaprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); + +int trio_asprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, ...)); +int trio_vasprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, va_list args)); + +/************************************************************************* + * Scan Functions + */ +int trio_scanf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vscanf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_scanfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fscanfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dscanf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdscanf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dscanfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cscanfv TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, ...)); +int trio_vsscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sscanfv TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, void **args)); + +/************************************************************************* + * Locale Functions + */ +void trio_locale_set_decimal_point TRIO_PROTO((char *decimalPoint)); +void trio_locale_set_thousand_separator TRIO_PROTO((char *thousandSeparator)); +void trio_locale_set_grouping TRIO_PROTO((char *grouping)); + +/************************************************************************* + * Renaming + */ +#ifdef TRIO_REPLACE_STDIO +/* Replace the functions */ +#ifndef HAVE_PRINTF +# define printf trio_printf +#endif +#ifndef HAVE_VPRINTF +# define vprintf trio_vprintf +#endif +#ifndef HAVE_FPRINTF +# define fprintf trio_fprintf +#endif +#ifndef HAVE_VFPRINTF +# define vfprintf trio_vfprintf +#endif +#ifndef HAVE_SPRINTF +# define sprintf trio_sprintf +#endif +#ifndef HAVE_VSPRINTF +# define vsprintf trio_vsprintf +#endif +#ifndef HAVE_SNPRINTF +# define snprintf trio_snprintf +#endif +#ifndef HAVE_VSNPRINTF +# define vsnprintf trio_vsnprintf +#endif +#ifndef HAVE_SCANF +# define scanf trio_scanf +#endif +#ifndef HAVE_VSCANF +# define vscanf trio_vscanf +#endif +#ifndef HAVE_FSCANF +# define fscanf trio_fscanf +#endif +#ifndef HAVE_VFSCANF +# define vfscanf trio_vfscanf +#endif +#ifndef HAVE_SSCANF +# define sscanf trio_sscanf +#endif +#ifndef HAVE_VSSCANF +# define vsscanf trio_vsscanf +#endif +/* These aren't stdio functions, but we make them look similar */ +#define dprintf trio_dprintf +#define vdprintf trio_vdprintf +#define aprintf trio_aprintf +#define vaprintf trio_vaprintf +#define asprintf trio_asprintf +#define vasprintf trio_vasprintf +#define dscanf trio_dscanf +#define vdscanf trio_vdscanf +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WITHOUT_TRIO */ + +#endif /* TRIO_TRIO_H */ diff --git a/glib/trio/triodef.h b/glib/trio/triodef.h new file mode 100644 index 0000000..f95b631 --- /dev/null +++ b/glib/trio/triodef.h @@ -0,0 +1,206 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_TRIODEF_H +#define TRIO_TRIODEF_H + +/************************************************************************* + * Platform and compiler support detection + */ +#if defined(__GNUC__) +# define TRIO_COMPILER_GCC +#elif defined(__SUNPRO_C) +# define TRIO_COMPILER_SUNPRO +#elif defined(__SUNPRO_CC) +# define TRIO_COMPILER_SUNPRO +# define __SUNPRO_C __SUNPRO_CC +#elif defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__) +# define TRIO_COMPILER_XLC +#elif defined(_AIX) && !defined(__GNUC__) +# define TRIO_COMPILER_XLC /* Workaround for old xlc */ +#elif defined(__DECC) || defined(__DECCXX) +# define TRIO_COMPILER_DECC +#elif defined(__osf__) && defined(__LANGUAGE_C__) +# define TRIO_COMPILER_DECC /* Workaround for old DEC C compilers */ +#elif defined(_MSC_VER) +# define TRIO_COMPILER_MSVC +#elif defined(__BORLANDC__) +# define TRIO_COMPILER_BCB +#endif + +#if defined(VMS) || defined(__VMS) +/* + * VMS is placed first to avoid identifying the platform as Unix + * based on the DECC compiler later on. + */ +# define TRIO_PLATFORM_VMS +#elif defined(unix) || defined(__unix) || defined(__unix__) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_XLC) || defined(_AIX) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_DECC) || defined(__osf___) +# define TRIO_PLATFORM_UNIX +#elif defined(__NetBSD__) +# define TRIO_PLATFORM_UNIX +#elif defined(__QNX__) +# define TRIO_PLATFORM_UNIX +# define TRIO_PLATFORM_QNX +#elif defined(__CYGWIN__) +# define TRIO_PLATFORM_UNIX +#elif defined(AMIGA) && defined(TRIO_COMPILER_GCC) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_MSVC) || defined(WIN32) || defined(_WIN32) +# define TRIO_PLATFORM_WIN32 +#elif defined(mpeix) || defined(__mpexl) +# define TRIO_PLATFORM_MPEIX +#endif + +#if defined(_AIX) +# define TRIO_PLATFORM_AIX +#elif defined(__hpux) +# define TRIO_PLATFORM_HPUX +#endif + +#if defined(__STDC__) || defined(TRIO_COMPILER_MSVC) +# define TRIO_COMPILER_SUPPORTS_C89 +# if defined(__STDC_VERSION__) +# define TRIO_COMPILER_SUPPORTS_C90 +# if (__STDC_VERSION__ >= 199409L) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# if (__STDC_VERSION__ >= 199901L) +# define TRIO_COMPILER_SUPPORTS_C99 +# endif +# elif defined(TRIO_COMPILER_SUNPRO) +# if (__SUNPRO_C >= 0x420) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# endif +#endif + +#if defined(_XOPEN_SOURCE) +# if defined(_XOPEN_SOURCE_EXTENDED) +# define TRIO_COMPILER_SUPPORTS_UNIX95 +# endif +# if (_XOPEN_VERSION >= 500) +# define TRIO_COMPILER_SUPPORTS_UNIX98 +# endif +# if (_XOPEN_VERSION >= 600) +# define TRIO_COMPILER_SUPPORTS_UNIX01 +# endif +#endif + +/************************************************************************* + * Generic defines + */ + +#if !defined(TRIO_PUBLIC) +# define TRIO_PUBLIC +#endif +#if !defined(TRIO_PRIVATE) +# define TRIO_PRIVATE static +#endif + +#if !(defined(TRIO_COMPILER_SUPPORTS_C89) || defined(__cplusplus)) +# define TRIO_COMPILER_ANCIENT +#endif + +#if defined(TRIO_COMPILER_ANCIENT) +# define TRIO_CONST +# define TRIO_VOLATILE +# define TRIO_SIGNED +typedef double trio_long_double_t; +typedef char * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x +# define TRIO_PROTO(x) () +# define TRIO_NOARGS +# define TRIO_ARGS1(list,a1) list a1; +# define TRIO_ARGS2(list,a1,a2) list a1; a2; +# define TRIO_ARGS3(list,a1,a2,a3) list a1; a2; a3; +# define TRIO_ARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4; +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5; +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) list a1; a2; a3; a4; a5; a6; +# define TRIO_VARGS2(list,a1,a2) list a1; a2 +# define TRIO_VARGS3(list,a1,a2,a3) list a1; a2; a3 +# define TRIO_VARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4 +# define TRIO_VARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5 +# define TRIO_VA_DECL va_dcl +# define TRIO_VA_START(x,y) va_start((x)) +# define TRIO_VA_END(x) va_end(x) +#else /* ANSI C */ +# define TRIO_CONST const +# define TRIO_VOLATILE volatile +# define TRIO_SIGNED signed +typedef long double trio_long_double_t; +typedef void * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x ## L +# define TRIO_PROTO(x) x +# define TRIO_NOARGS void +# define TRIO_ARGS1(list,a1) (a1) +# define TRIO_ARGS2(list,a1,a2) (a1,a2) +# define TRIO_ARGS3(list,a1,a2,a3) (a1,a2,a3) +# define TRIO_ARGS4(list,a1,a2,a3,a4) (a1,a2,a3,a4) +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) (a1,a2,a3,a4,a5) +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) (a1,a2,a3,a4,a5,a6) +# define TRIO_VARGS2 TRIO_ARGS2 +# define TRIO_VARGS3 TRIO_ARGS3 +# define TRIO_VARGS4 TRIO_ARGS4 +# define TRIO_VARGS5 TRIO_ARGS5 +# define TRIO_VA_DECL ... +# define TRIO_VA_START(x,y) va_start((x),(y)) +# define TRIO_VA_END(x) va_end(x) +#endif + +#if defined(TRIO_COMPILER_SUPPORTS_C99) || defined(__cplusplus) +# define TRIO_INLINE inline +#elif defined(TRIO_COMPILER_GCC) +# define TRIO_INLINE __inline__ +#elif defined(TRIO_COMPILER_MSVC) +# define TRIO_INLINE _inline +#elif defined(TRIO_COMPILER_BCB) +# define TRIO_INLINE __inline +#else +# define TRIO_INLINE +#endif + +/************************************************************************* + * Workarounds + */ + +#if defined(TRIO_PLATFORM_VMS) +/* + * Computations done with constants at compile time can trigger these + * even when compiling with IEEE enabled. + */ +# pragma message disable (UNDERFLOW, FLOATOVERFL) + +# if (__CRTL_VER > 80000000) +/* + * Although the compiler supports C99 language constructs, the C + * run-time library does not contain all C99 functions. + * + * This was the case for 70300022. Update the 80000000 value when + * it has been accurately determined what version of the library + * supports C99. + */ +# if defined(TRIO_COMPILER_SUPPORTS_C99) +# undef TRIO_COMPILER_SUPPORTS_C99 +# endif +# endif +#endif + +#endif /* TRIO_TRIODEF_H */ diff --git a/glib/trio/trionan.c b/glib/trio/trionan.c new file mode 100644 index 0000000..066de28 --- /dev/null +++ b/glib/trio/trionan.c @@ -0,0 +1,925 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************ + * + * Functions to handle special quantities in floating-point numbers + * (that is, NaNs and infinity). They provide the capability to detect + * and fabricate special quantities. + * + * Although written to be as portable as possible, it can never be + * guaranteed to work on all platforms, as not all hardware supports + * special quantities. + * + * The approach used here (approximately) is to: + * + * 1. Use C99 functionality when available. + * 2. Use IEEE 754 bit-patterns if possible. + * 3. Use platform-specific techniques. + * + ************************************************************************/ + +/* + * TODO: + * o Put all the magic into trio_fpclassify_and_signbit(), and use this from + * trio_isnan() etc. + */ + +/************************************************************************* + * Include files + */ +#include "triodef.h" +#include "trionan.h" + +#include +#include +#include +#include +#if defined(TRIO_PLATFORM_UNIX) +# include +#endif +#if defined(TRIO_COMPILER_DECC) +# include +#endif +#include + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_nan.h" +#endif +/** @addtogroup SpecialQuantities + @{ +*/ + +/************************************************************************* + * Definitions + */ + +#define TRIO_TRUE (1 == 1) +#define TRIO_FALSE (0 == 1) + +/* We must enable IEEE floating-point on Alpha */ +#if defined(__alpha) && !defined(_IEEE_FP) +# if defined(TRIO_COMPILER_DECC) +# if defined(TRIO_PLATFORM_VMS) +# error "Must be compiled with option /IEEE_MODE=UNDERFLOW_TO_ZERO/FLOAT=IEEE" +# else +# if !defined(_CFE) +# error "Must be compiled with option -ieee" +# endif +# endif +# elif defined(TRIO_COMPILER_GCC) && (defined(__osf__) || defined(__linux__)) +# error "Must be compiled with option -mieee" +# endif +#endif /* __alpha && ! _IEEE_FP */ + +/* + * In ANSI/IEEE 754-1985 64-bits double format numbers have the + * following properties (amoungst others) + * + * o FLT_RADIX == 2: binary encoding + * o DBL_MAX_EXP == 1024: 11 bits exponent, where one bit is used + * to indicate special numbers (e.g. NaN and Infinity), so the + * maximum exponent is 10 bits wide (2^10 == 1024). + * o DBL_MANT_DIG == 53: The mantissa is 52 bits wide, but because + * numbers are normalized the initial binary 1 is represented + * implicitly (the so-called "hidden bit"), which leaves us with + * the ability to represent 53 bits wide mantissa. + */ +#if (FLT_RADIX == 2) && (DBL_MAX_EXP == 1024) && (DBL_MANT_DIG == 53) +# define USE_IEEE_754 +#endif + + +/************************************************************************* + * Constants + */ + +static TRIO_CONST char rcsid[] = "@(#)$Id$"; + +#if defined(USE_IEEE_754) + +/* + * Endian-agnostic indexing macro. + * + * The value of internalEndianMagic, when converted into a 64-bit + * integer, becomes 0x0706050403020100 (we could have used a 64-bit + * integer value instead of a double, but not all platforms supports + * that type). The value is automatically encoded with the correct + * endianess by the compiler, which means that we can support any + * kind of endianess. The individual bytes are then used as an index + * for the IEEE 754 bit-patterns and masks. + */ +#define TRIO_DOUBLE_INDEX(x) (((unsigned char *)&internalEndianMagic)[7-(x)]) + +static TRIO_CONST double internalEndianMagic = 7.949928895127363e-275; + +/* Mask for the exponent */ +static TRIO_CONST unsigned char ieee_754_exponent_mask[] = { + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Mask for the mantissa */ +static TRIO_CONST unsigned char ieee_754_mantissa_mask[] = { + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* Mask for the sign bit */ +static TRIO_CONST unsigned char ieee_754_sign_mask[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for negative zero */ +static TRIO_CONST unsigned char ieee_754_negzero_array[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for infinity */ +static TRIO_CONST unsigned char ieee_754_infinity_array[] = { + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for quiet NaN */ +static TRIO_CONST unsigned char ieee_754_qnan_array[] = { + 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/************************************************************************* + * Functions + */ + +/* + * trio_make_double + */ +TRIO_PRIVATE double +trio_make_double +TRIO_ARGS1((values), + TRIO_CONST unsigned char *values) +{ + TRIO_VOLATILE double result; + int i; + + for (i = 0; i < (int)sizeof(double); i++) { + ((TRIO_VOLATILE unsigned char *)&result)[TRIO_DOUBLE_INDEX(i)] = values[i]; + } + return result; +} + +/* + * trio_is_special_quantity + */ +TRIO_PRIVATE int +trio_is_special_quantity +TRIO_ARGS2((number, has_mantissa), + double number, + int *has_mantissa) +{ + unsigned int i; + unsigned char current; + int is_special_quantity = TRIO_TRUE; + + *has_mantissa = 0; + + for (i = 0; i < (unsigned int)sizeof(double); i++) { + current = ((unsigned char *)&number)[TRIO_DOUBLE_INDEX(i)]; + is_special_quantity + &= ((current & ieee_754_exponent_mask[i]) == ieee_754_exponent_mask[i]); + *has_mantissa |= (current & ieee_754_mantissa_mask[i]); + } + return is_special_quantity; +} + +/* + * trio_is_negative + */ +TRIO_PRIVATE int +trio_is_negative +TRIO_ARGS1((number), + double number) +{ + unsigned int i; + int is_negative = TRIO_FALSE; + + for (i = 0; i < (unsigned int)sizeof(double); i++) { + is_negative |= (((unsigned char *)&number)[TRIO_DOUBLE_INDEX(i)] + & ieee_754_sign_mask[i]); + } + return is_negative; +} + +#endif /* USE_IEEE_754 */ + + +/** + Generate negative zero. + + @return Floating-point representation of negative zero. +*/ +TRIO_PUBLIC double +trio_nzero(TRIO_NOARGS) +{ +#if defined(USE_IEEE_754) + return trio_make_double(ieee_754_negzero_array); +#else + TRIO_VOLATILE double zero = 0.0; + + return -zero; +#endif +} + +/** + Generate positive infinity. + + @return Floating-point representation of positive infinity. +*/ +TRIO_PUBLIC double +trio_pinf(TRIO_NOARGS) +{ + /* Cache the result */ + static double result = 0.0; + + if (result == 0.0) { + +#if defined(INFINITY) && defined(__STDC_IEC_559__) + result = (double)INFINITY; + +#elif defined(USE_IEEE_754) + result = trio_make_double(ieee_754_infinity_array); + +#else + /* + * If HUGE_VAL is different from DBL_MAX, then HUGE_VAL is used + * as infinity. Otherwise we have to resort to an overflow + * operation to generate infinity. + */ +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + result = HUGE_VAL; + if (HUGE_VAL == DBL_MAX) { + /* Force overflow */ + result += HUGE_VAL; + } + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + +#endif + } + return result; +} + +/** + Generate negative infinity. + + @return Floating-point value of negative infinity. +*/ +TRIO_PUBLIC double +trio_ninf(TRIO_NOARGS) +{ + static double result = 0.0; + + if (result == 0.0) { + /* + * Negative infinity is calculated by negating positive infinity, + * which can be done because it is legal to do calculations on + * infinity (for example, 1 / infinity == 0). + */ + result = -trio_pinf(); + } + return result; +} + +/** + Generate NaN. + + @return Floating-point representation of NaN. +*/ +TRIO_PUBLIC double +trio_nan(TRIO_NOARGS) +{ + /* Cache the result */ + static double result = 0.0; + + if (result == 0.0) { + +#if defined(TRIO_COMPILER_SUPPORTS_C99) + result = nan(""); + +#elif defined(NAN) && defined(__STDC_IEC_559__) + result = (double)NAN; + +#elif defined(USE_IEEE_754) + result = trio_make_double(ieee_754_qnan_array); + +#else + /* + * There are several ways to generate NaN. The one used here is + * to divide infinity by infinity. I would have preferred to add + * negative infinity to positive infinity, but that yields wrong + * result (infinity) on FreeBSD. + * + * This may fail if the hardware does not support NaN, or if + * the Invalid Operation floating-point exception is unmasked. + */ +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + result = trio_pinf() / trio_pinf(); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + +#endif + } + return result; +} + +/** + Check for NaN. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number is a NaN. +*/ +TRIO_PUBLIC int +trio_isnan +TRIO_ARGS1((number), + double number) +{ +#if (defined(TRIO_COMPILER_SUPPORTS_C99) && defined(isnan)) \ + || defined(TRIO_COMPILER_SUPPORTS_UNIX95) + /* + * C99 defines isnan() as a macro. UNIX95 defines isnan() as a + * function. This function was already present in XPG4, but this + * is a bit tricky to detect with compiler defines, so we choose + * the conservative approach and only use it for UNIX95. + */ + return isnan(number); + +#elif defined(TRIO_COMPILER_MSVC) + /* + * MSVC has an _isnan() function + */ + return _isnan(number); + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. A NaN must have a special exponent + * pattern, and a non-empty mantissa. + */ + int has_mantissa; + int is_special_quantity; + + is_special_quantity = trio_is_special_quantity(number, &has_mantissa); + + return (is_special_quantity && has_mantissa); + +#else + /* + * Fallback solution + */ + int status; + double integral, fraction; + +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + status = (/* + * NaN is the only number which does not compare to itself + */ + ((TRIO_VOLATILE double)number != (TRIO_VOLATILE double)number) || + /* + * Fallback solution if NaN compares to NaN + */ + ((number != 0.0) && + (fraction = modf(number, &integral), + integral == fraction))); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + return status; + +#endif +} + +/** + Check for infinity. + + @param number An arbitrary floating-point number. + @return 1 if positive infinity, -1 if negative infinity, 0 otherwise. +*/ +TRIO_PUBLIC int +trio_isinf +TRIO_ARGS1((number), + double number) +{ +#if defined(TRIO_COMPILER_DECC) + /* + * DECC has an isinf() macro, but it works differently than that + * of C99, so we use the fp_class() function instead. + */ + return ((fp_class(number) == FP_POS_INF) + ? 1 + : ((fp_class(number) == FP_NEG_INF) ? -1 : 0)); + +#elif defined(isinf) + /* + * C99 defines isinf() as a macro. + */ + return isinf(number) + ? ((number > 0.0) ? 1 : -1) + : 0; + +#elif defined(TRIO_COMPILER_MSVC) + /* + * MSVC has an _fpclass() function that can be used to detect infinity. + */ + return ((_fpclass(number) == _FPCLASS_PINF) + ? 1 + : ((_fpclass(number) == _FPCLASS_NINF) ? -1 : 0)); + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. Infinity must have a special exponent + * pattern, and an empty mantissa. + */ + int has_mantissa; + int is_special_quantity; + + is_special_quantity = trio_is_special_quantity(number, &has_mantissa); + + return (is_special_quantity && !has_mantissa) + ? ((number < 0.0) ? -1 : 1) + : 0; + +#else + /* + * Fallback solution. + */ + int status; + +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + double infinity = trio_pinf(); + + status = ((number == infinity) + ? 1 + : ((number == -infinity) ? -1 : 0)); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + return status; + +#endif +} + + +/** + Check for finity. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number is a finite. +*/ +TRIO_PUBLIC int +trio_isfinite +TRIO_ARGS1((number), + double number) +{ +#if defined(TRIO_COMPILER_SUPPORTS_C99) && defined(isfinite) + /* + * C99 defines isfinite() as a macro. + */ + return isfinite(number); + +#elif defined(TRIO_COMPILER_MSVC) + /* + * MSVC uses _finite(). + */ + return _finite(number); + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. For finity we do not care about the + * mantissa. + */ + int dummy; + + return (! trio_is_special_quantity(number, &dummy)); + +#else + /* + * Fallback solution. + */ + return ((trio_isinf(number) == 0) && (trio_isnan(number) == 0)); + +#endif +} + +/* + * The sign of NaN is always false + */ +TRIO_PUBLIC int +trio_fpclassify_and_signbit +TRIO_ARGS2((number, is_negative), + double number, + int *is_negative) +{ +#if defined(fpclassify) && defined(signbit) + /* + * C99 defines fpclassify() and signbit() as a macros + */ + *is_negative = signbit(number); + switch (fpclassify(number)) { + case FP_NAN: + return TRIO_FP_NAN; + case FP_INFINITE: + return TRIO_FP_INFINITE; + case FP_SUBNORMAL: + return TRIO_FP_SUBNORMAL; + case FP_ZERO: + return TRIO_FP_ZERO; + default: + return TRIO_FP_NORMAL; + } + +#elif defined(TRIO_COMPILER_DECC) + /* + * DECC has an fp_class() function. + */ + switch (fp_class(number)) { + case FP_QNAN: + case FP_SNAN: + *is_negative = TRIO_FALSE; /* NaN has no sign */ + return TRIO_FP_NAN; + case FP_POS_INF: + *is_negative = TRIO_FALSE; + return TRIO_FP_INFINITE; + case FP_NEG_INF: + *is_negative = TRIO_TRUE; + return TRIO_FP_INFINITE; + case FP_POS_DENORM: + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + case FP_NEG_DENORM: + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + case FP_POS_ZERO: + *is_negative = TRIO_FALSE; + return TRIO_FP_ZERO; + case FP_NEG_ZERO: + *is_negative = TRIO_TRUE; + return TRIO_FP_ZERO; + case FP_POS_NORM: + *is_negative = TRIO_FALSE; + return TRIO_FP_NORMAL; + case FP_NEG_NORM: + *is_negative = TRIO_TRUE; + return TRIO_FP_NORMAL; + default: + /* Just in case... */ + *is_negative = (number < 0.0); + return TRIO_FP_NORMAL; + } + +#elif defined(TRIO_COMPILER_MSVC) + /* + * MSVC has an _fpclass() function. + */ + switch (_fpclass(number)) { + case _FPCLASS_QNAN: + case _FPCLASS_SNAN: + *is_negative = TRIO_FALSE; + return TRIO_FP_NAN; + case _FPCLASS_PINF: + *is_negative = TRIO_FALSE; + return TRIO_FP_INFINITE; + case _FPCLASS_NINF: + *is_negative = TRIO_TRUE; + return TRIO_FP_INFINITE; + case _FPCLASS_PD: + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + case _FPCLASS_ND: + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + case _FPCLASS_PZ: + *is_negative = TRIO_FALSE; + return TRIO_FP_ZERO; + case _FPCLASS_NZ: + *is_negative = TRIO_TRUE; + return TRIO_FP_ZERO; + case _FPCLASS_PN: + *is_negative = TRIO_FALSE; + return TRIO_FP_NORMAL; + case _FPCLASS_NN: + *is_negative = TRIO_TRUE; + return TRIO_FP_NORMAL; + default: + /* Just in case... */ + *is_negative = (number < 0.0); + return TRIO_FP_NORMAL; + } + +#elif defined(FP_PLUS_NORM) + + /* + * HP-UX 9.x and 10.x have an fpclassify() function, that is different + * from the C99 fpclassify() macro supported on HP-UX 11.x. + * + * AIX has class() for C, and _class() for C++, which returns the + * same values as the HP-UX fpclassify() function. + */ + +# if defined(TRIO_PLATFORM_AIX) +# if defined(__cplusplus) +# define fpclassify(x) _class(x) +# else +# define fpclassify(x) class(x) +# endif +# endif + + switch (fpclassify(number)) { + case FP_QNAN: + case FP_SNAN: + *is_negative = TRIO_FALSE; + return TRIO_FP_NAN; + case FP_PLUS_INF: + *is_negative = TRIO_FALSE; + return TRIO_FP_INFINITE; + case FP_MINUS_INF: + *is_negative = TRIO_TRUE; + return TRIO_FP_INFINITE; + case FP_PLUS_DENORM: + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + case FP_MINUS_DENORM: + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + case FP_PLUS_ZERO: + *is_negative = TRIO_FALSE; + return TRIO_FP_ZERO; + case FP_MINUS_ZERO: + *is_negative = TRIO_TRUE; + return TRIO_FP_ZERO; + case FP_PLUS_NORM: + *is_negative = TRIO_FALSE; + return TRIO_FP_NORMAL; + case FP_MINUS_NORM: + *is_negative = TRIO_TRUE; + return TRIO_FP_NORMAL; + default: + assert(0); + } + +#else + /* + * Fallback solution. + */ + int rc; + + if (number == 0.0) { + /* + * In IEEE 754 the sign of zero is ignored in comparisons, so we + * have to handle this as a special case by examining the sign bit + * directly. + */ +#if defined(USE_IEEE_754) + *is_negative = trio_is_negative(number); +#else + *is_negative = TRIO_FALSE; /* FIXME */ +#endif + return TRIO_FP_ZERO; + } + if (trio_isnan(number)) { + *is_negative = TRIO_FALSE; + return TRIO_FP_NAN; + } + if ((rc = trio_isinf(number))) { + *is_negative = (rc == -1); + return TRIO_FP_INFINITE; + } + if ((number > 0.0) && (number < DBL_MIN)) { + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + } + if ((number < 0.0) && (number > -DBL_MIN)) { + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + } + *is_negative = (number < 0.0); + return TRIO_FP_NORMAL; + +#endif +} + +/** + Examine the sign of a number. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number has the + sign bit set (i.e. is negative). +*/ +TRIO_PUBLIC int +trio_signbit +TRIO_ARGS1((number), + double number) +{ + int is_negative; + + (void)trio_fpclassify_and_signbit(number, &is_negative); + return is_negative; +} + +/** + Examine the class of a number. + + @param number An arbitrary floating-point number. + @return Enumerable value indicating the class of @p number +*/ +TRIO_PUBLIC int +trio_fpclassify +TRIO_ARGS1((number), + double number) +{ + int dummy; + + return trio_fpclassify_and_signbit(number, &dummy); +} + + +/** @} SpecialQuantities */ + +/************************************************************************* + * For test purposes. + * + * Add the following compiler option to include this test code. + * + * Unix : -DSTANDALONE + * VMS : /DEFINE=(STANDALONE) + */ +#if defined(STANDALONE) +# include + +static TRIO_CONST char * +getClassification +TRIO_ARGS1((type), + int type) +{ + switch (type) { + case TRIO_FP_INFINITE: + return "FP_INFINITE"; + case TRIO_FP_NAN: + return "FP_NAN"; + case TRIO_FP_NORMAL: + return "FP_NORMAL"; + case TRIO_FP_SUBNORMAL: + return "FP_SUBNORMAL"; + case TRIO_FP_ZERO: + return "FP_ZERO"; + default: + return "FP_UNKNOWN"; + } +} + +static void +print_class +TRIO_ARGS2((prefix, number), + TRIO_CONST char *prefix, + double number) +{ + printf("%-6s: %s %-15s %g\n", + prefix, + trio_signbit(number) ? "-" : "+", + getClassification(trio_fpclassify(number)), + number); +} + +int main(TRIO_NOARGS) +{ + double my_nan; + double my_pinf; + double my_ninf; +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler) TRIO_PROTO((int)); +# endif + + my_nan = trio_nan(); + my_pinf = trio_pinf(); + my_ninf = trio_ninf(); + + print_class("Nan", my_nan); + print_class("PInf", my_pinf); + print_class("NInf", my_ninf); + print_class("PZero", 0.0); + print_class("NZero", -0.0); + print_class("PNorm", 1.0); + print_class("NNorm", -1.0); + print_class("PSub", 1.01e-307 - 1.00e-307); + print_class("NSub", 1.00e-307 - 1.01e-307); + + printf("NaN : %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_nan, + ((unsigned char *)&my_nan)[0], + ((unsigned char *)&my_nan)[1], + ((unsigned char *)&my_nan)[2], + ((unsigned char *)&my_nan)[3], + ((unsigned char *)&my_nan)[4], + ((unsigned char *)&my_nan)[5], + ((unsigned char *)&my_nan)[6], + ((unsigned char *)&my_nan)[7], + trio_isnan(my_nan), trio_isinf(my_nan)); + printf("PInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_pinf, + ((unsigned char *)&my_pinf)[0], + ((unsigned char *)&my_pinf)[1], + ((unsigned char *)&my_pinf)[2], + ((unsigned char *)&my_pinf)[3], + ((unsigned char *)&my_pinf)[4], + ((unsigned char *)&my_pinf)[5], + ((unsigned char *)&my_pinf)[6], + ((unsigned char *)&my_pinf)[7], + trio_isnan(my_pinf), trio_isinf(my_pinf)); + printf("NInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_ninf, + ((unsigned char *)&my_ninf)[0], + ((unsigned char *)&my_ninf)[1], + ((unsigned char *)&my_ninf)[2], + ((unsigned char *)&my_ninf)[3], + ((unsigned char *)&my_ninf)[4], + ((unsigned char *)&my_ninf)[5], + ((unsigned char *)&my_ninf)[6], + ((unsigned char *)&my_ninf)[7], + trio_isnan(my_ninf), trio_isinf(my_ninf)); + +# if defined(TRIO_PLATFORM_UNIX) + signal_handler = signal(SIGFPE, SIG_IGN); +# endif + + my_pinf = DBL_MAX + DBL_MAX; + my_ninf = -my_pinf; + my_nan = my_pinf / my_pinf; + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + printf("NaN : %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_nan, + ((unsigned char *)&my_nan)[0], + ((unsigned char *)&my_nan)[1], + ((unsigned char *)&my_nan)[2], + ((unsigned char *)&my_nan)[3], + ((unsigned char *)&my_nan)[4], + ((unsigned char *)&my_nan)[5], + ((unsigned char *)&my_nan)[6], + ((unsigned char *)&my_nan)[7], + trio_isnan(my_nan), trio_isinf(my_nan)); + printf("PInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_pinf, + ((unsigned char *)&my_pinf)[0], + ((unsigned char *)&my_pinf)[1], + ((unsigned char *)&my_pinf)[2], + ((unsigned char *)&my_pinf)[3], + ((unsigned char *)&my_pinf)[4], + ((unsigned char *)&my_pinf)[5], + ((unsigned char *)&my_pinf)[6], + ((unsigned char *)&my_pinf)[7], + trio_isnan(my_pinf), trio_isinf(my_pinf)); + printf("NInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_ninf, + ((unsigned char *)&my_ninf)[0], + ((unsigned char *)&my_ninf)[1], + ((unsigned char *)&my_ninf)[2], + ((unsigned char *)&my_ninf)[3], + ((unsigned char *)&my_ninf)[4], + ((unsigned char *)&my_ninf)[5], + ((unsigned char *)&my_ninf)[6], + ((unsigned char *)&my_ninf)[7], + trio_isnan(my_ninf), trio_isinf(my_ninf)); + + return 0; +} +#endif diff --git a/glib/trio/trionan.h b/glib/trio/trionan.h new file mode 100644 index 0000000..bed0c12 --- /dev/null +++ b/glib/trio/trionan.h @@ -0,0 +1,81 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_NAN_H +#define TRIO_NAN_H + +#include "triodef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + TRIO_FP_INFINITE, + TRIO_FP_NAN, + TRIO_FP_NORMAL, + TRIO_FP_SUBNORMAL, + TRIO_FP_ZERO +}; + +/* + * Return NaN (Not-a-Number). + */ +TRIO_PUBLIC double trio_nan TRIO_PROTO((void)); + +/* + * Return positive infinity. + */ +TRIO_PUBLIC double trio_pinf TRIO_PROTO((void)); + +/* + * Return negative infinity. + */ +TRIO_PUBLIC double trio_ninf TRIO_PROTO((void)); + +/* + * Return negative zero. + */ +TRIO_PUBLIC double trio_nzero TRIO_PROTO((TRIO_NOARGS)); + +/* + * If number is a NaN return non-zero, otherwise return zero. + */ +TRIO_PUBLIC int trio_isnan TRIO_PROTO((double number)); + +/* + * If number is positive infinity return 1, if number is negative + * infinity return -1, otherwise return 0. + */ +TRIO_PUBLIC int trio_isinf TRIO_PROTO((double number)); + +/* + * If number is finite return non-zero, otherwise return zero. + */ +TRIO_PUBLIC int trio_isfinite TRIO_PROTO((double number)); + +TRIO_PUBLIC int trio_signbit TRIO_PROTO((double number)); + +TRIO_PUBLIC int trio_fpclassify TRIO_PROTO((double number)); + +TRIO_PUBLIC int trio_fpclassify_and_signbit TRIO_PROTO((double number, int *is_negative)); + +#ifdef __cplusplus +} +#endif + +#endif /* TRIO_NAN_H */ diff --git a/glib/trio/triop.h b/glib/trio/triop.h new file mode 100644 index 0000000..8462c56 --- /dev/null +++ b/glib/trio/triop.h @@ -0,0 +1,150 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2000 Bjorn Reese and Daniel Stenberg. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************ + * + * Private functions, types, etc. used for callback functions. + * + * The ref pointer is an opaque type and should remain as such. + * Private data must only be accessible through the getter and + * setter functions. + * + ************************************************************************/ + +#ifndef TRIO_TRIOP_H +#define TRIO_TRIOP_H + +#include "triodef.h" + +#include +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef TRIO_C99 +# define TRIO_C99 1 +#endif +#ifndef TRIO_BSD +# define TRIO_BSD 1 +#endif +#ifndef TRIO_GNU +# define TRIO_GNU 1 +#endif +#ifndef TRIO_MISC +# define TRIO_MISC 1 +#endif +#ifndef TRIO_UNIX98 +# define TRIO_UNIX98 1 +#endif +#ifndef TRIO_MICROSOFT +# define TRIO_MICROSOFT 1 +#endif +#ifndef TRIO_EXTENSION +# define TRIO_EXTENSION 1 +#endif +#ifndef TRIO_WIDECHAR /* Does not work yet. Do not enable */ +# define TRIO_WIDECHAR 0 +#endif +#ifndef TRIO_ERRORS +# define TRIO_ERRORS 1 +#endif + +#ifndef TRIO_MALLOC +# define TRIO_MALLOC(n) malloc(n) +#endif +#ifndef TRIO_REALLOC +# define TRIO_REALLOC(x,n) realloc((x),(n)) +#endif +#ifndef TRIO_FREE +# define TRIO_FREE(x) free(x) +#endif + + +/************************************************************************* + * User-defined specifiers + */ + +typedef int (*trio_callback_t) TRIO_PROTO((trio_pointer_t)); + +trio_pointer_t trio_register TRIO_PROTO((trio_callback_t callback, const char *name)); +void trio_unregister TRIO_PROTO((trio_pointer_t handle)); + +TRIO_CONST char *trio_get_format TRIO_PROTO((trio_pointer_t ref)); +trio_pointer_t trio_get_argument TRIO_PROTO((trio_pointer_t ref)); + +/* Modifiers */ +int trio_get_width TRIO_PROTO((trio_pointer_t ref)); +void trio_set_width TRIO_PROTO((trio_pointer_t ref, int width)); +int trio_get_precision TRIO_PROTO((trio_pointer_t ref)); +void trio_set_precision TRIO_PROTO((trio_pointer_t ref, int precision)); +int trio_get_base TRIO_PROTO((trio_pointer_t ref)); +void trio_set_base TRIO_PROTO((trio_pointer_t ref, int base)); +int trio_get_padding TRIO_PROTO((trio_pointer_t ref)); +void trio_set_padding TRIO_PROTO((trio_pointer_t ref, int is_padding)); +int trio_get_short TRIO_PROTO((trio_pointer_t ref)); /* h */ +void trio_set_shortshort TRIO_PROTO((trio_pointer_t ref, int is_shortshort)); +int trio_get_shortshort TRIO_PROTO((trio_pointer_t ref)); /* hh */ +void trio_set_short TRIO_PROTO((trio_pointer_t ref, int is_short)); +int trio_get_long TRIO_PROTO((trio_pointer_t ref)); /* l */ +void trio_set_long TRIO_PROTO((trio_pointer_t ref, int is_long)); +int trio_get_longlong TRIO_PROTO((trio_pointer_t ref)); /* ll */ +void trio_set_longlong TRIO_PROTO((trio_pointer_t ref, int is_longlong)); +int trio_get_longdouble TRIO_PROTO((trio_pointer_t ref)); /* L */ +void trio_set_longdouble TRIO_PROTO((trio_pointer_t ref, int is_longdouble)); +int trio_get_alternative TRIO_PROTO((trio_pointer_t ref)); /* # */ +void trio_set_alternative TRIO_PROTO((trio_pointer_t ref, int is_alternative)); +int trio_get_alignment TRIO_PROTO((trio_pointer_t ref)); /* - */ +void trio_set_alignment TRIO_PROTO((trio_pointer_t ref, int is_leftaligned)); +int trio_get_spacing TRIO_PROTO((trio_pointer_t ref)); /* TRIO_PROTO((space) */ +void trio_set_spacing TRIO_PROTO((trio_pointer_t ref, int is_space)); +int trio_get_sign TRIO_PROTO((trio_pointer_t ref)); /* + */ +void trio_set_sign TRIO_PROTO((trio_pointer_t ref, int is_showsign)); +int trio_get_quote TRIO_PROTO((trio_pointer_t ref)); /* ' */ +void trio_set_quote TRIO_PROTO((trio_pointer_t ref, int is_quote)); +int trio_get_upper TRIO_PROTO((trio_pointer_t ref)); +void trio_set_upper TRIO_PROTO((trio_pointer_t ref, int is_upper)); +#if TRIO_C99 +int trio_get_largest TRIO_PROTO((trio_pointer_t ref)); /* j */ +void trio_set_largest TRIO_PROTO((trio_pointer_t ref, int is_largest)); +int trio_get_ptrdiff TRIO_PROTO((trio_pointer_t ref)); /* t */ +void trio_set_ptrdiff TRIO_PROTO((trio_pointer_t ref, int is_ptrdiff)); +int trio_get_size TRIO_PROTO((trio_pointer_t ref)); /* z / Z */ +void trio_set_size TRIO_PROTO((trio_pointer_t ref, int is_size)); +#endif + +/* Printing */ +int trio_print_ref TRIO_PROTO((trio_pointer_t ref, const char *format, ...)); +int trio_vprint_ref TRIO_PROTO((trio_pointer_t ref, const char *format, va_list args)); +int trio_printv_ref TRIO_PROTO((trio_pointer_t ref, const char *format, trio_pointer_t *args)); + +void trio_print_int TRIO_PROTO((trio_pointer_t ref, int number)); +void trio_print_uint TRIO_PROTO((trio_pointer_t ref, unsigned int number)); +/* void trio_print_long TRIO_PROTO((trio_pointer_t ref, long number)); */ +/* void trio_print_ulong TRIO_PROTO((trio_pointer_t ref, unsigned long number)); */ +void trio_print_double TRIO_PROTO((trio_pointer_t ref, double number)); +void trio_print_string TRIO_PROTO((trio_pointer_t ref, char *string)); +void trio_print_pointer TRIO_PROTO((trio_pointer_t ref, trio_pointer_t pointer)); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TRIO_TRIOP_H */ diff --git a/glib/trio/triostr.c b/glib/trio/triostr.c new file mode 100644 index 0000000..43a30da --- /dev/null +++ b/glib/trio/triostr.c @@ -0,0 +1,2024 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese and Daniel Stenberg. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +/************************************************************************* + * Include files + */ + +#include +#include +#include +#include +#include +#include "triodef.h" +#include "triostr.h" + +/************************************************************************* + * Definitions + */ + +#if !defined(TRIO_STRING_PUBLIC) +# define TRIO_STRING_PUBLIC TRIO_PUBLIC +#endif +#if !defined(TRIO_STRING_PRIVATE) +# define TRIO_STRING_PRIVATE TRIO_PRIVATE +#endif + +#if !defined(NULL) +# define NULL 0 +#endif +#if !defined(NIL) +# define NIL ((char)0) +#endif +#if !defined(FALSE) +# define FALSE (1 == 0) +# define TRUE (! FALSE) +#endif +#if !defined(BOOLEAN_T) +# define BOOLEAN_T int +#endif + +#if defined(TRIO_COMPILER_SUPPORTS_C99) +# define USE_STRTOD +# define USE_STRTOF +#elif defined(TRIO_COMPILER_MSVC) +# define USE_STRTOD +#endif + +#if defined(TRIO_PLATFORM_UNIX) +# define USE_STRCASECMP +# define USE_STRNCASECMP +# define USE_STRERROR +# if defined(TRIO_PLATFORM_QNX) +# define strcasecmp(x,y) stricmp(x,y) +# define strncasecmp(x,y,n) strnicmp(x,y,n) +# endif +#elif defined(TRIO_PLATFORM_WIN32) +# define USE_STRCASECMP +# define strcasecmp(x,y) strcmpi(x,y) +#endif + +/************************************************************************* + * Structures + */ + +struct _trio_string_t +{ + char *content; + size_t length; + size_t allocated; +}; + +/************************************************************************* + * Constants + */ + +#if !defined(TRIO_MINIMAL) +static TRIO_CONST char rcsid[] = "@(#)$Id$"; +#endif + +/************************************************************************* + * Static String Functions + */ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_static.h" +#endif +/** @addtogroup StaticStrings + @{ +*/ + +/** + Create new string. + + @param size Size of new string. + @return Pointer to string, or NULL if allocation failed. +*/ +TRIO_STRING_PUBLIC char * +trio_create +TRIO_ARGS1((size), + size_t size) +{ + return (char *)TRIO_MALLOC(size); +} + + +/** + Destroy string. + + @param string String to be freed. +*/ +TRIO_STRING_PUBLIC void +trio_destroy +TRIO_ARGS1((string), + char *string) +{ + if (string) + { + TRIO_FREE(string); + } +} + + +/** + Count the number of characters in a string. + + @param string String to measure. + @return Number of characters in @string. +*/ +TRIO_STRING_PUBLIC size_t +trio_length +TRIO_ARGS1((string), + TRIO_CONST char *string) +{ + return strlen(string); +} + + +#if !defined(TRIO_MINIMAL) +/** + Append @p source at the end of @p target. + + @param target Target string. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p target string and @p source string. + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_append +TRIO_ARGS2((target, source), + char *target, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + + return (strcat(target, source) != NULL); +} +#endif /* !defined(TRIO_MINIMAL) */ + +#if !defined(TRIO_MINIMAL) +/** + Append at most @p max characters from @p source to @p target. + + @param target Target string. + @param max Maximum number of characters to append. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chuck with sufficient room to + contain the @p target string and the @p source string (at most @p max + characters). + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_append_max +TRIO_ARGS3((target, max, source), + char *target, + size_t max, + TRIO_CONST char *source) +{ + size_t length; + + assert(target); + assert(source); + + length = trio_length(target); + + if (max > length) + { + strncat(target, source, max - length - 1); + } + return TRUE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Determine if a string contains a substring. + + @param string String to be searched. + @param substring String to be found. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_contains +TRIO_ARGS2((string, substring), + TRIO_CONST char *string, + TRIO_CONST char *substring) +{ + assert(string); + assert(substring); + + return (0 != strstr(string, substring)); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Copy @p source to @p target. + + @param target Target string. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p source string. + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_copy +TRIO_ARGS2((target, source), + char *target, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + + (void)strcpy(target, source); + return TRUE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Copy at most @p max characters from @p source to @p target. + + @param target Target string. + @param max Maximum number of characters to append. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p source string (at most @p max characters). + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_copy_max +TRIO_ARGS3((target, max, source), + char *target, + size_t max, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + assert(max > 0); /* Includes != 0 */ + + (void)strncpy(target, source, max - 1); + target[max - 1] = (char)0; + return TRUE; +} + + +/* + * TrioDuplicateMax + */ +TRIO_STRING_PRIVATE char * +TrioDuplicateMax +TRIO_ARGS2((source, size), + TRIO_CONST char *source, + size_t size) +{ + char *target; + + assert(source); + + /* Make room for string plus a terminating zero */ + size++; + target = trio_create(size); + if (target) + { + trio_copy_max(target, size, source); + } + return target; +} + + +/** + Duplicate @p source. + + @param source Source string. + @return A copy of the @p source string. + + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC char * +trio_duplicate +TRIO_ARGS1((source), + TRIO_CONST char *source) +{ + return TrioDuplicateMax(source, trio_length(source)); +} + + +#if !defined(TRIO_MINIMAL) +/** + Duplicate at most @p max characters of @p source. + + @param source Source string. + @param max Maximum number of characters to duplicate. + @return A copy of the @p source string. + + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC char * +trio_duplicate_max TRIO_ARGS2((source, max), + TRIO_CONST char *source, + size_t max) +{ + size_t length; + + assert(source); + assert(max > 0); + + length = trio_length(source); + if (length > max) + { + length = max; + } + return TrioDuplicateMax(source, length); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-insensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { +#if defined(USE_STRCASECMP) + return (0 == strcasecmp(first, second)); +#else + while ((*first != NIL) && (*second != NIL)) + { + if (toupper(*first) != toupper(*second)) + { + break; + } + first++; + second++; + } + return ((*first == NIL) && (*second == NIL)); +#endif + } + return FALSE; +} + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-sensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_case +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { + return (0 == strcmp(first, second)); + } + return FALSE; +} + + +#if !defined(TRIO_MINIMAL) +/** + Compare if two strings up until the first @p max characters are equal. + + @param first First string. + @param max Maximum number of characters to compare. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-sensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_case_max +TRIO_ARGS3((first, max, second), + TRIO_CONST char *first, + size_t max, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { + return (0 == strncmp(first, second, max)); + } + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Collating characters are considered equal. +*/ +TRIO_STRING_PUBLIC int +trio_equal_locale +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + +#if defined(LC_COLLATE) + return (strcoll(first, second) == 0); +#else + return trio_equal(first, second); +#endif +} + + +/** + Compare if two strings up until the first @p max characters are equal. + + @param first First string. + @param max Maximum number of characters to compare. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-insensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_max +TRIO_ARGS3((first, max, second), + TRIO_CONST char *first, + size_t max, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { +#if defined(USE_STRNCASECMP) + return (0 == strncasecmp(first, second, max)); +#else + /* Not adequately tested yet */ + size_t cnt = 0; + while ((*first != NIL) && (*second != NIL) && (cnt <= max)) + { + if (toupper(*first) != toupper(*second)) + { + break; + } + first++; + second++; + cnt++; + } + return ((cnt == max) || ((*first == NIL) && (*second == NIL))); +#endif + } + return FALSE; +} + + +/** + Provide a textual description of an error code (errno). + + @param error_number Error number. + @return Textual description of @p error_number. +*/ +TRIO_STRING_PUBLIC TRIO_CONST char * +trio_error +TRIO_ARGS1((error_number), + int error_number) +{ +#if defined(USE_STRERROR) + return strerror(error_number); +#else + return "unknown"; +#endif +} + + +#if !defined(TRIO_MINIMAL) +/** + Format the date/time according to @p format. + + @param target Target string. + @param max Maximum number of characters to format. + @param format Formatting string. + @param datetime Date/time structure. + @return Number of formatted characters. + + The formatting string accepts the same specifiers as the standard C + function strftime. +*/ +TRIO_STRING_PUBLIC size_t +trio_format_date_max +TRIO_ARGS4((target, max, format, datetime), + char *target, + size_t max, + TRIO_CONST char *format, + TRIO_CONST struct tm *datetime) +{ + assert(target); + assert(format); + assert(datetime); + assert(max > 0); + + return strftime(target, max, format, datetime); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Calculate a hash value for a string. + + @param string String to be calculated on. + @param type Hash function. + @return Calculated hash value. + + @p type can be one of the following + @li @c TRIO_HASH_PLAIN Plain hash function. +*/ +TRIO_STRING_PUBLIC unsigned long +trio_hash +TRIO_ARGS2((string, type), + TRIO_CONST char *string, + int type) +{ + unsigned long value = 0L; + char ch; + + assert(string); + + switch (type) + { + case TRIO_HASH_PLAIN: + while ( (ch = *string++) != NIL ) + { + value *= 31; + value += (unsigned long)ch; + } + break; + default: + assert(FALSE); + break; + } + return value; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Find first occurrence of a character in a string. + + @param string String to be searched. + @param character Character to be found. + @param A pointer to the found character, or NULL if character was not found. + */ +TRIO_STRING_PUBLIC char * +trio_index +TRIO_ARGS2((string, character), + TRIO_CONST char *string, + int character) +{ + assert(string); + + return strchr(string, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Find last occurrence of a character in a string. + + @param string String to be searched. + @param character Character to be found. + @param A pointer to the found character, or NULL if character was not found. + */ +TRIO_STRING_PUBLIC char * +trio_index_last +TRIO_ARGS2((string, character), + TRIO_CONST char *string, + int character) +{ + assert(string); + + return strchr(string, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Convert the alphabetic letters in the string to lower-case. + + @param target String to be converted. + @return Number of processed characters (converted or not). +*/ +TRIO_STRING_PUBLIC int +trio_lower +TRIO_ARGS1((target), + char *target) +{ + assert(target); + + return trio_span_function(target, target, tolower); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Compare two strings using wildcards. + + @param string String to be searched. + @param pattern Pattern, including wildcards, to search for. + @return Boolean value indicating success or failure. + + Case-insensitive comparison. + + The following wildcards can be used + @li @c * Match any number of characters. + @li @c ? Match a single character. +*/ +TRIO_STRING_PUBLIC int +trio_match +TRIO_ARGS2((string, pattern), + TRIO_CONST char *string, + TRIO_CONST char *pattern) +{ + assert(string); + assert(pattern); + + for (; ('*' != *pattern); ++pattern, ++string) + { + if (NIL == *string) + { + return (NIL == *pattern); + } + if ((toupper((int)*string) != toupper((int)*pattern)) + && ('?' != *pattern)) + { + return FALSE; + } + } + /* two-line patch to prevent *too* much recursiveness: */ + while ('*' == pattern[1]) + pattern++; + + do + { + if ( trio_match(string, &pattern[1]) ) + { + return TRUE; + } + } + while (*string++); + + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Compare two strings using wildcards. + + @param string String to be searched. + @param pattern Pattern, including wildcards, to search for. + @return Boolean value indicating success or failure. + + Case-sensitive comparison. + + The following wildcards can be used + @li @c * Match any number of characters. + @li @c ? Match a single character. +*/ +TRIO_STRING_PUBLIC int +trio_match_case +TRIO_ARGS2((string, pattern), + TRIO_CONST char *string, + TRIO_CONST char *pattern) +{ + assert(string); + assert(pattern); + + for (; ('*' != *pattern); ++pattern, ++string) + { + if (NIL == *string) + { + return (NIL == *pattern); + } + if ((*string != *pattern) + && ('?' != *pattern)) + { + return FALSE; + } + } + /* two-line patch to prevent *too* much recursiveness: */ + while ('*' == pattern[1]) + pattern++; + + do + { + if ( trio_match_case(string, &pattern[1]) ) + { + return TRUE; + } + } + while (*string++); + + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Execute a function on each character in string. + + @param target Target string. + @param source Source string. + @param Function Function to be executed. + @return Number of processed characters. +*/ +TRIO_STRING_PUBLIC size_t +trio_span_function +TRIO_ARGS3((target, source, Function), + char *target, + TRIO_CONST char *source, + int (*Function) TRIO_PROTO((int))) +{ + size_t count = 0; + + assert(target); + assert(source); + assert(Function); + + while (*source != NIL) + { + *target++ = Function(*source++); + count++; + } + return count; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Search for a substring in a string. + + @param string String to be searched. + @param substring String to be found. + @return Pointer to first occurrence of @p substring in @p string, or NULL + if no match was found. +*/ +TRIO_STRING_PUBLIC char * +trio_substring +TRIO_ARGS2((string, substring), + TRIO_CONST char *string, + TRIO_CONST char *substring) +{ + assert(string); + assert(substring); + + return strstr(string, substring); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Search for a substring in the first @p max characters of a string. + + @param string String to be searched. + @param max Maximum characters to be searched. + @param substring String to be found. + @return Pointer to first occurrence of @p substring in @p string, or NULL + if no match was found. +*/ +TRIO_STRING_PUBLIC char * +trio_substring_max +TRIO_ARGS3((string, max, substring), + TRIO_CONST char *string, + size_t max, + TRIO_CONST char *substring) +{ + size_t count; + size_t size; + char *result = NULL; + + assert(string); + assert(substring); + + size = trio_length(substring); + if (size <= max) + { + for (count = 0; count <= max - size; count++) + { + if (trio_equal_max(substring, size, &string[count])) + { + result = (char *)&string[count]; + break; + } + } + } + return result; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Tokenize string. + + @param string String to be tokenized. + @param tokens String containing list of delimiting characters. + @return Start of new token. + + @warning @p string will be destroyed. +*/ +TRIO_STRING_PUBLIC char * +trio_tokenize +TRIO_ARGS2((string, delimiters), + char *string, + TRIO_CONST char *delimiters) +{ + assert(delimiters); + + return strtok(string, delimiters); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + The following Extended Backus-Naur form is used + @verbatim + double ::= [ ] + ( | + | + ) + [ [ ] ] + number ::= 1*( ) + digit ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) + exponential ::= ( 'e' | 'E' ) + sign ::= ( '-' | '+' ) + decimal_point ::= '.' + @endverbatim +*/ +/* FIXME: Add EBNF for hex-floats */ +TRIO_STRING_PUBLIC trio_long_double_t +trio_to_long_double +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOLD) + return strtold(source, endp); +#else + int isNegative = FALSE; + int isExponentNegative = FALSE; + trio_long_double_t integer = 0.0; + trio_long_double_t fraction = 0.0; + unsigned long exponent = 0; + trio_long_double_t base; + trio_long_double_t fracdiv = 1.0; + trio_long_double_t value = 0.0; + + /* First try hex-floats */ + if ((source[0] == '0') && ((source[1] == 'x') || (source[1] == 'X'))) + { + base = 16.0; + source += 2; + while (isxdigit((int)*source)) + { + integer *= base; + integer += (isdigit((int)*source) + ? (*source - '0') + : 10 + (toupper((int)*source) - 'A')); + source++; + } + if (*source == '.') + { + source++; + while (isxdigit((int)*source)) + { + fracdiv /= base; + fraction += fracdiv * (isdigit((int)*source) + ? (*source - '0') + : 10 + (toupper((int)*source) - 'A')); + source++; + } + if ((*source == 'p') || (*source == 'P')) + { + source++; + if ((*source == '+') || (*source == '-')) + { + isExponentNegative = (*source == '-'); + source++; + } + while (isdigit((int)*source)) + { + exponent *= (int)base; + exponent += (*source - '0'); + source++; + } + } + } + } + else /* Then try normal decimal floats */ + { + base = 10.0; + isNegative = (*source == '-'); + /* Skip sign */ + if ((*source == '+') || (*source == '-')) + source++; + + /* Integer part */ + while (isdigit((int)*source)) + { + integer *= base; + integer += (*source - '0'); + source++; + } + + if (*source == '.') + { + source++; /* skip decimal point */ + while (isdigit((int)*source)) + { + fracdiv /= base; + fraction += (*source - '0') * fracdiv; + source++; + } + } + if ((*source == 'e') + || (*source == 'E') +#if TRIO_MICROSOFT + || (*source == 'd') + || (*source == 'D') +#endif + ) + { + source++; /* Skip exponential indicator */ + isExponentNegative = (*source == '-'); + if ((*source == '+') || (*source == '-')) + source++; + while (isdigit((int)*source)) + { + exponent *= (int)base; + exponent += (*source - '0'); + source++; + } + } + } + + value = integer + fraction; + if (exponent != 0) + { + if (isExponentNegative) + value /= pow(base, (double)exponent); + else + value *= pow(base, (double)exponent); + } + if (isNegative) + value = -value; + + if (endp) + *endp = (char *)source; + return value; +#endif +} + + +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + See @ref trio_to_long_double. +*/ +TRIO_STRING_PUBLIC double +trio_to_double +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOD) + return strtod(source, endp); +#else + return (double)trio_to_long_double(source, endp); +#endif +} + +#if !defined(TRIO_MINIMAL) +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + See @ref trio_to_long_double. +*/ +TRIO_STRING_PUBLIC float +trio_to_float +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOF) + return strtof(source, endp); +#else + return (float)trio_to_long_double(source, endp); +#endif +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Convert string to signed integer. + + @param string String to be converted. + @param endp Pointer to end of converted string. + @param base Radix number of number. +*/ +TRIO_STRING_PUBLIC long +trio_to_long +TRIO_ARGS3((string, endp, base), + TRIO_CONST char *string, + char **endp, + int base) +{ + assert(string); + assert((base >= 2) && (base <= 36)); + + return strtol(string, endp, base); +} + + +#if !defined(TRIO_MINIMAL) +/** + Convert string to unsigned integer. + + @param string String to be converted. + @param endp Pointer to end of converted string. + @param base Radix number of number. +*/ +TRIO_STRING_PUBLIC unsigned long +trio_to_unsigned_long +TRIO_ARGS3((string, endp, base), + TRIO_CONST char *string, + char **endp, + int base) +{ + assert(string); + assert((base >= 2) && (base <= 36)); + + return strtoul(string, endp, base); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Convert the alphabetic letters in the string to upper-case. + + @param target The string to be converted. + @return The number of processed characters (converted or not). +*/ +TRIO_STRING_PUBLIC int +trio_upper +TRIO_ARGS1((target), + char *target) +{ + assert(target); + + return trio_span_function(target, target, toupper); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** @} End of StaticStrings */ + + +/************************************************************************* + * Dynamic String Functions + */ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_dynamic.h" +#endif +/** @addtogroup DynamicStrings + @{ +*/ + +/* + * TrioStringAlloc + */ +TRIO_STRING_PRIVATE trio_string_t * +TrioStringAlloc(TRIO_NOARGS) +{ + trio_string_t *self; + + self = (trio_string_t *)TRIO_MALLOC(sizeof(trio_string_t)); + if (self) + { + self->content = NULL; + self->length = 0; + self->allocated = 0; + } + return self; +} + + +/* + * TrioStringGrow + * + * The size of the string will be increased by 'delta' characters. If + * 'delta' is zero, the size will be doubled. + */ +TRIO_STRING_PRIVATE BOOLEAN_T +TrioStringGrow +TRIO_ARGS2((self, delta), + trio_string_t *self, + size_t delta) +{ + BOOLEAN_T status = FALSE; + char *new_content; + size_t new_size; + + new_size = (delta == 0) + ? ( (self->allocated == 0) ? 1 : self->allocated * 2 ) + : self->allocated + delta; + + new_content = (char *)TRIO_REALLOC(self->content, new_size); + if (new_content) + { + self->content = new_content; + self->allocated = new_size; + status = TRUE; + } + return status; +} + + +/* + * TrioStringGrowTo + * + * The size of the string will be increased to 'length' plus one characters. + * If 'length' is less than the original size, the original size will be + * used (that is, the size of the string is never decreased). + */ +TRIO_STRING_PRIVATE BOOLEAN_T +TrioStringGrowTo +TRIO_ARGS2((self, length), + trio_string_t *self, + size_t length) +{ + length++; /* Room for terminating zero */ + return (self->allocated < length) + ? TrioStringGrow(self, length - self->allocated) + : TRUE; +} + + +#if !defined(TRIO_MINIMAL) +/** + Create a new dynamic string. + + @param initial_size Initial size of the buffer. + @return Newly allocated dynamic string, or NULL if memory allocation failed. +*/ +TRIO_STRING_PUBLIC trio_string_t * +trio_string_create +TRIO_ARGS1((initial_size), + int initial_size) +{ + trio_string_t *self; + + self = TrioStringAlloc(); + if (self) + { + if (TrioStringGrow(self, + (size_t)((initial_size > 0) ? initial_size : 1))) + { + self->content[0] = (char)0; + self->allocated = initial_size; + } + else + { + trio_string_destroy(self); + self = NULL; + } + } + return self; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Deallocate the dynamic string and its contents. + + @param self Dynamic string +*/ +TRIO_STRING_PUBLIC void +trio_string_destroy +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + if (self) + { + trio_destroy(self->content); + TRIO_FREE(self); + } +} + + +#if !defined(TRIO_MINIMAL) +/** + Get a pointer to the content. + + @param self Dynamic string. + @param offset Offset into content. + @return Pointer to the content. + + @p Offset can be zero, positive, or negative. If @p offset is zero, + then the start of the content will be returned. If @p offset is positive, + then a pointer to @p offset number of characters from the beginning of the + content is returned. If @p offset is negative, then a pointer to @p offset + number of characters from the ending of the string, starting at the + terminating zero, is returned. +*/ +TRIO_STRING_PUBLIC char * +trio_string_get +TRIO_ARGS2((self, offset), + trio_string_t *self, + int offset) +{ + char *result = NULL; + + assert(self); + + if (self->content != NULL) + { + if (self->length == 0) + { + (void)trio_string_length(self); + } + if (offset >= 0) + { + if (offset > (int)self->length) + { + offset = self->length; + } + } + else + { + offset += self->length + 1; + if (offset < 0) + { + offset = 0; + } + } + result = &(self->content[offset]); + } + return result; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Extract the content. + + @param self Dynamic String + @return Content of dynamic string. + + The content is removed from the dynamic string. This enables destruction + of the dynamic string without deallocation of the content. +*/ +TRIO_STRING_PUBLIC char * +trio_string_extract +TRIO_ARGS1((self), + trio_string_t *self) +{ + char *result; + + assert(self); + + result = self->content; + /* FIXME: Allocate new empty buffer? */ + self->content = NULL; + self->length = self->allocated = 0; + return result; +} + + +#if !defined(TRIO_MINIMAL) +/** + Set the content of the dynamic string. + + @param self Dynamic String + @param buffer The new content. + + Sets the content of the dynamic string to a copy @p buffer. + An existing content will be deallocated first, if necessary. + + @remark + This function will make a copy of @p buffer. + You are responsible for deallocating @p buffer yourself. +*/ +TRIO_STRING_PUBLIC void +trio_xstring_set +TRIO_ARGS2((self, buffer), + trio_string_t *self, + char *buffer) +{ + assert(self); + + trio_destroy(self->content); + self->content = trio_duplicate(buffer); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_string_size + */ +TRIO_STRING_PUBLIC int +trio_string_size +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return self->allocated; +} + + +/* + * trio_string_terminate + */ +TRIO_STRING_PUBLIC void +trio_string_terminate +TRIO_ARGS1((self), + trio_string_t *self) +{ + trio_xstring_append_char(self, 0); +} + + +#if !defined(TRIO_MINIMAL) +/** + Append the second string to the first. + + @param self Dynamic string to be modified. + @param other Dynamic string to copy from. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_string_append +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + size_t length; + + assert(self); + assert(other); + + length = self->length + other->length; + if (!TrioStringGrowTo(self, length)) + goto error; + trio_copy(&self->content[self->length], other->content); + self->length = length; + return TRUE; + + error: + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_append + */ +TRIO_STRING_PUBLIC int +trio_xstring_append +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + size_t length; + + assert(self); + assert(other); + + length = self->length + trio_length(other); + if (!TrioStringGrowTo(self, length)) + goto error; + trio_copy(&self->content[self->length], other); + self->length = length; + return TRUE; + + error: + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_xstring_append_char + */ +TRIO_STRING_PUBLIC int +trio_xstring_append_char +TRIO_ARGS2((self, character), + trio_string_t *self, + char character) +{ + assert(self); + + if ((int)self->length >= trio_string_size(self)) + { + if (!TrioStringGrow(self, 0)) + goto error; + } + self->content[self->length] = character; + self->length++; + return TRUE; + + error: + return FALSE; +} + + +#if !defined(TRIO_MINIMAL) +/** + Search for the first occurrence of second parameter in the first. + + @param self Dynamic string to be modified. + @param other Dynamic string to copy from. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_string_contains +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_contains(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_contains + */ +TRIO_STRING_PUBLIC int +trio_xstring_contains +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_contains(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_copy + */ +TRIO_STRING_PUBLIC int +trio_string_copy +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + self->length = 0; + return trio_string_append(self, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_copy + */ +TRIO_STRING_PUBLIC int +trio_xstring_copy +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + self->length = 0; + return trio_xstring_append(self, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_duplicate + */ +TRIO_STRING_PUBLIC trio_string_t * +trio_string_duplicate +TRIO_ARGS1((other), + trio_string_t *other) +{ + trio_string_t *self; + + assert(other); + + self = TrioStringAlloc(); + if (self) + { + self->content = TrioDuplicateMax(other->content, other->length); + if (self->content) + { + self->length = other->length; + self->allocated = self->length + 1; + } + else + { + self->length = self->allocated = 0; + } + } + return self; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_xstring_duplicate + */ +TRIO_STRING_PUBLIC trio_string_t * +trio_xstring_duplicate +TRIO_ARGS1((other), + TRIO_CONST char *other) +{ + trio_string_t *self; + + assert(other); + + self = TrioStringAlloc(); + if (self) + { + self->content = TrioDuplicateMax(other, trio_length(other)); + if (self->content) + { + self->length = trio_length(self->content); + self->allocated = self->length + 1; + } + else + { + self->length = self->allocated = 0; + } + } + return self; +} + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal + */ +TRIO_STRING_PUBLIC int +trio_string_equal +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_max + */ +TRIO_STRING_PUBLIC int +trio_string_equal_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_max(self->content, max, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_max + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_max(self->content, max, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_case + */ +TRIO_STRING_PUBLIC int +trio_string_equal_case +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_case(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_case + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_case +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_case(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_case_max + */ +TRIO_STRING_PUBLIC int +trio_string_equal_case_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_case_max(self->content, max, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_case_max + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_case_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_case_max(self->content, max, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_format_data_max + */ +TRIO_STRING_PUBLIC size_t +trio_string_format_date_max +TRIO_ARGS4((self, max, format, datetime), + trio_string_t *self, + size_t max, + TRIO_CONST char *format, + TRIO_CONST struct tm *datetime) +{ + assert(self); + + return trio_format_date_max(self->content, max, format, datetime); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_index + */ +TRIO_STRING_PUBLIC char * +trio_string_index +TRIO_ARGS2((self, character), + trio_string_t *self, + int character) +{ + assert(self); + + return trio_index(self->content, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_index_last + */ +TRIO_STRING_PUBLIC char * +trio_string_index_last +TRIO_ARGS2((self, character), + trio_string_t *self, + int character) +{ + assert(self); + + return trio_index_last(self->content, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_length + */ +TRIO_STRING_PUBLIC int +trio_string_length +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + if (self->length == 0) + { + self->length = trio_length(self->content); + } + return self->length; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_lower + */ +TRIO_STRING_PUBLIC int +trio_string_lower +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return trio_lower(self->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_match + */ +TRIO_STRING_PUBLIC int +trio_string_match +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_match(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_match + */ +TRIO_STRING_PUBLIC int +trio_xstring_match +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_match(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_match_case + */ +TRIO_STRING_PUBLIC int +trio_string_match_case +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_match_case(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_match_case + */ +TRIO_STRING_PUBLIC int +trio_xstring_match_case +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_match_case(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_substring + */ +TRIO_STRING_PUBLIC char * +trio_string_substring +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_substring(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_substring + */ +TRIO_STRING_PUBLIC char * +trio_xstring_substring +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_substring(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_upper + */ +TRIO_STRING_PUBLIC int +trio_string_upper +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return trio_upper(self->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + +/** @} End of DynamicStrings */ diff --git a/glib/trio/triostr.h b/glib/trio/triostr.h new file mode 100644 index 0000000..083e2a5 --- /dev/null +++ b/glib/trio/triostr.h @@ -0,0 +1,127 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese and Daniel Stenberg. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_TRIOSTR_H +#define TRIO_TRIOSTR_H + +#include +#include +#include +#include +#include "triodef.h" +#include "triop.h" + +enum { + TRIO_HASH_NONE = 0, + TRIO_HASH_PLAIN, + TRIO_HASH_TWOSIGNED +}; + +#if !defined(TRIO_STRING_PUBLIC) +# if !defined(TRIO_PUBLIC) +# define TRIO_PUBLIC +# endif +# define TRIO_STRING_PUBLIC TRIO_PUBLIC +#endif + +/************************************************************************* + * String functions + */ + +TRIO_STRING_PUBLIC int trio_append TRIO_PROTO((char *target, const char *source)); +TRIO_STRING_PUBLIC int trio_append_max TRIO_PROTO((char *target, size_t max, const char *source)); +TRIO_STRING_PUBLIC int trio_contains TRIO_PROTO((const char *string, const char *substring)); +TRIO_STRING_PUBLIC int trio_copy TRIO_PROTO((char *target, const char *source)); +TRIO_STRING_PUBLIC int trio_copy_max TRIO_PROTO((char *target, size_t max, const char *source)); +TRIO_STRING_PUBLIC char *trio_create TRIO_PROTO((size_t size)); +TRIO_STRING_PUBLIC void trio_destroy TRIO_PROTO((char *string)); +TRIO_STRING_PUBLIC char *trio_duplicate TRIO_PROTO((const char *source)); +TRIO_STRING_PUBLIC char *trio_duplicate_max TRIO_PROTO((const char *source, size_t max)); +TRIO_STRING_PUBLIC int trio_equal TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_case TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_case_max TRIO_PROTO((const char *first, size_t max, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_locale TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_max TRIO_PROTO((const char *first, size_t max, const char *second)); +TRIO_STRING_PUBLIC TRIO_CONST char *trio_error TRIO_PROTO((int)); +TRIO_STRING_PUBLIC size_t trio_format_date_max TRIO_PROTO((char *target, size_t max, const char *format, const struct tm *datetime)); +TRIO_STRING_PUBLIC unsigned long trio_hash TRIO_PROTO((const char *string, int type)); +TRIO_STRING_PUBLIC char *trio_index TRIO_PROTO((const char *string, int character)); +TRIO_STRING_PUBLIC char *trio_index_last TRIO_PROTO((const char *string, int character)); +TRIO_STRING_PUBLIC size_t trio_length TRIO_PROTO((const char *string)); +TRIO_STRING_PUBLIC int trio_lower TRIO_PROTO((char *target)); +TRIO_STRING_PUBLIC int trio_match TRIO_PROTO((const char *string, const char *pattern)); +TRIO_STRING_PUBLIC int trio_match_case TRIO_PROTO((const char *string, const char *pattern)); +TRIO_STRING_PUBLIC size_t trio_span_function TRIO_PROTO((char *target, const char *source, int (*Function) TRIO_PROTO((int)))); +TRIO_STRING_PUBLIC char *trio_substring TRIO_PROTO((const char *string, const char *substring)); +TRIO_STRING_PUBLIC char *trio_substring_max TRIO_PROTO((const char *string, size_t max, const char *substring)); +TRIO_STRING_PUBLIC double trio_to_double TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC float trio_to_float TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC trio_long_double_t trio_to_long_double TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC long trio_to_long TRIO_PROTO((const char *source, char **endp, int base)); +TRIO_STRING_PUBLIC unsigned long trio_to_unsigned_long TRIO_PROTO((const char *source, char **endp, int base)); +TRIO_STRING_PUBLIC char *trio_tokenize TRIO_PROTO((char *string, const char *delimiters)); +TRIO_STRING_PUBLIC int trio_upper TRIO_PROTO((char *target)); + +/************************************************************************* + * Dynamic string functions + */ + +/* + * Opaque type for dynamic strings + */ +typedef struct _trio_string_t trio_string_t; + +TRIO_STRING_PUBLIC trio_string_t *trio_string_create TRIO_PROTO((int initial_size)); +TRIO_STRING_PUBLIC void trio_string_destroy TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC char *trio_string_get TRIO_PROTO((trio_string_t *self, int offset)); +TRIO_STRING_PUBLIC void trio_xstring_set TRIO_PROTO((trio_string_t *self, char *buffer)); +TRIO_STRING_PUBLIC char *trio_string_extract TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_size TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC void trio_string_terminate TRIO_PROTO((trio_string_t *self)); + +TRIO_STRING_PUBLIC int trio_string_append TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_contains TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_copy TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC trio_string_t *trio_string_duplicate TRIO_PROTO((trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal_max TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *second)); +TRIO_STRING_PUBLIC int trio_string_equal_case TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal_case_max TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *other)); +TRIO_STRING_PUBLIC size_t trio_string_format_date_max TRIO_PROTO((trio_string_t *self, size_t max, const char *format, const struct tm *datetime)); +TRIO_STRING_PUBLIC char *trio_string_index TRIO_PROTO((trio_string_t *self, int character)); +TRIO_STRING_PUBLIC char *trio_string_index_last TRIO_PROTO((trio_string_t *self, int character)); +TRIO_STRING_PUBLIC int trio_string_length TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_lower TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_match TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_match_case TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC char *trio_string_substring TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_upper TRIO_PROTO((trio_string_t *self)); + +TRIO_STRING_PUBLIC int trio_xstring_append_char TRIO_PROTO((trio_string_t *self, char character)); +TRIO_STRING_PUBLIC int trio_xstring_append TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_contains TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_copy TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC trio_string_t *trio_xstring_duplicate TRIO_PROTO((const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_max TRIO_PROTO((trio_string_t *self, size_t max, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_case TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_case_max TRIO_PROTO((trio_string_t *self, size_t max, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_match TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_match_case TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC char *trio_xstring_substring TRIO_PROTO((trio_string_t *self, const char *other)); + +#endif /* TRIO_TRIOSTR_H */ -- 2.7.4