r15795 - gnucash/trunk/lib/libqof/qof - Improve printing of dates and times with non-UTF-8 locales and on Windows.

Andreas Köhler andi5 at cvs.gnucash.org
Wed Apr 4 14:48:44 EDT 2007


Author: andi5
Date: 2007-04-04 14:48:42 -0400 (Wed, 04 Apr 2007)
New Revision: 15795
Trac: http://svn.gnucash.org/trac/changeset/15795

Added:
   gnucash/trunk/lib/libqof/qof/gnc-date-p.h
   gnucash/trunk/lib/libqof/qof/qof-win32.c
Modified:
   gnucash/trunk/lib/libqof/qof/Makefile.am
   gnucash/trunk/lib/libqof/qof/gnc-date.c
   gnucash/trunk/lib/libqof/qof/gnc-date.h
Log:
Improve printing of dates and times with non-UTF-8 locales and on Windows.

* qof_time_format_from_utf8 converts an strftime format specification
  from UTF-8 to the locale encoding
* qof_format_time calls strftime repeatedly with growing allocated
  buffers until the result fits into one, or return NULL
* qof_formatted_time_to_utf8 converts the result back to UTF-8
* qof_strftime takes similar arguments to strftime, but in UTF-8
* the conversion functions on Windows are implemented in qof-win32.c to
  allow them to use windows.h, as they need wcstombs and mbstowcs


Modified: gnucash/trunk/lib/libqof/qof/Makefile.am
===================================================================
--- gnucash/trunk/lib/libqof/qof/Makefile.am	2007-04-04 17:58:23 UTC (rev 15794)
+++ gnucash/trunk/lib/libqof/qof/Makefile.am	2007-04-04 18:48:42 UTC (rev 15795)
@@ -39,6 +39,7 @@
 qofinclude_HEADERS = \
    deprecated.h      \
    gnc-date.h        \
+   gnc-date-p.h      \
    gnc-numeric.h     \
    guid.h            \
    kvp_frame.h       \
@@ -86,6 +87,12 @@
   qofla-dir.h.in \
   qofmath128.c
 
+if OS_WIN32
+libgnc_qof_la_SOURCES += qof-win32.c
+else
+EXTRA_DIST += qof-win32.c
+endif
+
 qofla-dir.h: $(srcdir)/qofla-dir.h.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \

Added: gnucash/trunk/lib/libqof/qof/gnc-date-p.h
===================================================================
--- gnucash/trunk/lib/libqof/qof/gnc-date-p.h	2007-04-04 17:58:23 UTC (rev 15794)
+++ gnucash/trunk/lib/libqof/qof/gnc-date-p.h	2007-04-04 18:48:42 UTC (rev 15795)
@@ -0,0 +1,45 @@
+/* 
+ * gnc-date-p.h
+ *
+ * Copyright (C) 2007 Andreas Koehler <andi5.py at gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef __GNC_DATE_P_H__
+#define __GNC_DATE_P_H__
+
+#include "gnc-date.h"
+
+/** Convert a given date/time format from UTF-8 to an encoding suitable for the
+ *  strftime system call.
+ *
+ *  @param utf8_format Date/time format specification in UTF-8.
+ *
+ *  @return A newly allocated string on success, or NULL otherwise.
+ */
+gchar *qof_time_format_from_utf8(const gchar *utf8_format);
+
+/** Convert a result of a call to strftime back to UTF-8.
+ *
+ *  @param locale_string The result of a call to strftime.
+ *
+ *  @return A newly allocated string on success, or NULL otherwise.
+ */
+gchar *qof_formatted_time_to_utf8(const gchar *locale_string);
+
+#endif /* __GNC_DATE_P_H__ */

Modified: gnucash/trunk/lib/libqof/qof/gnc-date.c
===================================================================
--- gnucash/trunk/lib/libqof/qof/gnc-date.c	2007-04-04 17:58:23 UTC (rev 15794)
+++ gnucash/trunk/lib/libqof/qof/gnc-date.c	2007-04-04 18:48:42 UTC (rev 15795)
@@ -46,7 +46,7 @@
 
 #include <glib.h>
 
-#include "gnc-date.h"
+#include "gnc-date-p.h"
 #include "qof.h"
 
 #ifndef HAVE_STRPTIME
@@ -439,7 +439,7 @@
         gnc_tm_set_day_start (&tm_str);
 	t = mktime (&tm_str);
 	localtime_r (&t, &tm_str);
-        flen = strftime (buff, len, GNC_D_FMT, &tm_str);
+        flen = qof_strftime (buff, len, GNC_D_FMT, &tm_str);
        if (flen != 0)
          break;
       }
@@ -618,12 +618,12 @@
 	case QOF_DATE_FORMAT_UTC:
 	{
 		gtm = *gmtime (&secs);
-		flen = strftime (buff, len, QOF_UTC_DATE_FORMAT, &gtm);
+		flen = qof_strftime (buff, len, QOF_UTC_DATE_FORMAT, &gtm);
 		break;
 	}
     case QOF_DATE_FORMAT_LOCALE:
       {
-        flen = strftime (buff, len, GNC_D_T_FMT, &ltm);
+        flen = qof_strftime (buff, len, GNC_D_T_FMT, &ltm);
       }
       break;
 
@@ -645,11 +645,11 @@
 	if(dateFormat == QOF_DATE_FORMAT_UTC)
 	{
 		gtm = *gmtime (&secs);
-		flen = strftime(buff, len, QOF_UTC_DATE_FORMAT, &gtm);
+		flen = qof_strftime(buff, len, QOF_UTC_DATE_FORMAT, &gtm);
 		return flen;
 	}
 	ltm = *localtime (&secs);
-	flen = strftime (buff, len, GNC_T_FMT, &ltm);
+	flen = qof_strftime (buff, len, GNC_T_FMT, &ltm);
 	
 	return flen;
 }
@@ -909,7 +909,7 @@
 
         secs = time(NULL);
         localtime_r(&secs, &tm);
-        strftime(string, sizeof(string), GNC_D_FMT, &tm);
+        qof_strftime(string, sizeof(string), GNC_D_FMT, &tm);
 
         for (s = string; s != '\0'; s++)
           if (!isdigit(*s))
@@ -920,6 +920,127 @@
   return '\0';
 }
 
+
+#ifndef G_OS_WIN32
+gchar *
+qof_time_format_from_utf8(const gchar *utf8_format)
+{
+    gchar *retval;
+    GError *error = NULL;
+
+    retval = g_locale_from_utf8(utf8_format, -1, NULL, NULL, &error);
+
+    if (!retval) {
+        g_warning("Could not convert format '%s' from UTF-8: %s", utf8_format,
+                  error->message);
+        g_error_free(error);
+    }
+    return retval;
+}
+
+gchar *
+qof_formatted_time_to_utf8(const gchar *locale_string)
+{
+    gchar *retval;
+    GError *error = NULL;
+
+    retval = g_locale_to_utf8(locale_string, -1, NULL, NULL, &error);
+
+    if (!retval) {
+        g_warning("Could not convert '%s' to UTF-8: %s", locale_string,
+                  error->message);
+        g_error_free(error);
+    }
+    return retval;
+}
+#endif /* G_OS_WIN32 */
+
+gchar *
+qof_format_time(const gchar *format, const struct tm *tm)
+{
+    gchar *locale_format, *tmpbuf, *retval;
+    gsize tmplen, tmpbufsize;
+
+    g_return_val_if_fail(format, 0);
+    g_return_val_if_fail(tm, 0);
+
+    locale_format = qof_time_format_from_utf8(format);
+    if (!locale_format)
+        return NULL;
+
+    tmpbufsize = MAX(128, strlen(locale_format) * 2);
+    while (TRUE) {
+        tmpbuf = g_malloc(tmpbufsize);
+
+        /* Set the first byte to something other than '\0', to be able to
+         * recognize whether strftime actually failed or just returned "".
+         */
+        tmpbuf[0] = '\1';
+        tmplen = strftime(tmpbuf, tmpbufsize, locale_format, tm);
+
+        if (tmplen == 0 && tmpbuf[0] != '\0') {
+            g_free(tmpbuf);
+            tmpbufsize *= 2;
+
+            if (tmpbufsize > 65536) {
+                g_warning("Maximum buffer size for qof_format_time "
+                          "exceeded: giving up");
+                g_free(locale_format);
+
+                return NULL;
+            }
+        } else {
+            break;
+        }
+    }
+    g_free(locale_format);
+
+    retval = qof_formatted_time_to_utf8(tmpbuf);
+    g_free(tmpbuf);
+
+    return retval;
+}
+
+gsize
+qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
+{
+    gsize convlen, retval;
+    gchar *convbuf;
+    GError *error = NULL;
+
+    g_return_val_if_fail(buf, 0);
+    g_return_val_if_fail(max > 0, 0);
+    g_return_val_if_fail(format, 0);
+    g_return_val_if_fail(tm, 0);
+
+    convbuf = qof_format_time(format, tm);
+    if (!convbuf) {
+        buf[0] = '\0';
+        return 0;
+    }
+
+    convlen = strlen(convbuf);
+
+    if (max <= convlen) {
+        /* Ensure only whole characters are copied into the buffer. */
+        gchar *end = g_utf8_find_prev_char(convbuf, convbuf + max);
+        g_assert(end != NULL);
+        convlen = end - convbuf;
+
+        /* Return 0 because the buffer isn't large enough. */
+        retval = 0;
+    } else {
+        retval = convlen;
+    }
+
+    memcpy(buf, convbuf, convlen);
+    buf[convlen] = '\0';
+    g_free(convbuf);
+
+    return retval;
+}
+
+
 /********************************************************************\
 \********************************************************************/
                                                                                 

Modified: gnucash/trunk/lib/libqof/qof/gnc-date.h
===================================================================
--- gnucash/trunk/lib/libqof/qof/gnc-date.h	2007-04-04 17:58:23 UTC (rev 15794)
+++ gnucash/trunk/lib/libqof/qof/gnc-date.h	2007-04-04 18:48:42 UTC (rev 15795)
@@ -328,6 +328,35 @@
  * itself, instead of depending on the routines here.
  */
 
+/** qof_format_time takes a format specification in UTF-8 and a broken-down time,
+ *  tries to call strftime with a sufficiently large buffer and, if successful,
+ *  return a newly allocated string in UTF-8 for the printing result.
+ *
+ *  @param format A format specification in UTF-8.
+ *
+ *  @param tm A broken-down time.
+ *
+ *  @return A newly allocated string on success, or NULL otherwise.
+ */
+gchar *qof_format_time(const gchar *format, const struct tm *tm);
+
+/** qof_strftime calls qof_format_time to print a given time and afterwards tries
+ *  to put the result into a buffer of fixed size.
+ *
+ *  @param buf A buffer.
+ *
+ *  @param max The size of buf in bytes.
+ *
+ *  @param format A format specification in UTF-8.
+ *
+ *  @param tm A broken-down time.
+ *
+ *  @return The number of characters written, not include the null byte, if the
+ *  complete string, including the null byte, fits into the buffer.  Otherwise 0.
+ */
+gsize qof_strftime(gchar *buf, gsize max, const gchar *format,
+                   const struct tm *tm);
+
 /** qof_print_date_dmy_buff
  *    Convert a date as day / month / year integers into a localized string
  *    representation

Added: gnucash/trunk/lib/libqof/qof/qof-win32.c
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof-win32.c	2007-04-04 17:58:23 UTC (rev 15794)
+++ gnucash/trunk/lib/libqof/qof/qof-win32.c	2007-04-04 18:48:42 UTC (rev 15795)
@@ -0,0 +1,81 @@
+/* 
+ * qof-win32.c
+ *
+ * Copyright (C) 2007 Andreas Koehler <andi5.py at gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include "gnc-date-p.h"
+#include <windows.h>
+
+gchar *
+qof_time_format_from_utf8(const gchar *utf8_format)
+{
+    gunichar2 *utf16_format;
+    gchar *retval;
+    gsize count;
+
+    utf16_format = g_utf8_to_utf16(utf8_format, -1, NULL, NULL, NULL);
+    if (!utf16_format)
+        return NULL;
+
+    /* get number of resulting wide characters */
+    count = wcstombs(NULL, utf16_format, 0);
+    if (count <= 0)
+        return NULL;
+
+    /* malloc and convert */
+    retval = g_malloc((count+1) * sizeof(gchar));
+    count = wcstombs(retval, utf16_format, count+1);
+    g_free(utf16_format);
+    if (count <= 0) {
+        g_free(retval);
+        return NULL;
+    }
+
+    return retval;
+}
+
+gchar *
+qof_formatted_time_to_utf8(const gchar *locale_string)
+{
+    gunichar2 *utf16_string;
+    gchar *retval;
+    gsize count;
+
+    /* get number of resulting wide characters */
+    count = mbstowcs(NULL, locale_string, 0);
+    if (count <= 0)
+        return NULL;
+
+    /* malloc and convert */
+    utf16_string = g_malloc((count+1) * sizeof(gunichar2));
+    count = mbstowcs(utf16_string, locale_string, count+1);
+    if (count <= 0) {
+        g_free(utf16_string);
+        return NULL;
+    }
+
+    retval = g_utf16_to_utf8(utf16_string, -1, NULL, NULL, NULL);
+    g_free(utf16_string);
+
+    return retval;
+}



More information about the gnucash-changes mailing list