diff --git a/lib/libqof/qof/Makefile.am b/lib/libqof/qof/Makefile.am index a76d0f2cfa..45dd6bfcad 100644 --- a/lib/libqof/qof/Makefile.am +++ b/lib/libqof/qof/Makefile.am @@ -39,6 +39,7 @@ qofincludedir = ${pkgincludedir} qofinclude_HEADERS = \ deprecated.h \ gnc-date.h \ + gnc-date-p.h \ gnc-numeric.h \ guid.h \ kvp_frame.h \ @@ -86,6 +87,12 @@ EXTRA_DIST = \ 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 \ diff --git a/lib/libqof/qof/gnc-date-p.h b/lib/libqof/qof/gnc-date-p.h new file mode 100644 index 0000000000..4c8d895b2c --- /dev/null +++ b/lib/libqof/qof/gnc-date-p.h @@ -0,0 +1,45 @@ +/* + * gnc-date-p.h + * + * Copyright (C) 2007 Andreas Koehler + * + * 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@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__ */ diff --git a/lib/libqof/qof/gnc-date.c b/lib/libqof/qof/gnc-date.c index dae1b00f35..b0e005736f 100644 --- a/lib/libqof/qof/gnc-date.c +++ b/lib/libqof/qof/gnc-date.c @@ -46,7 +46,7 @@ #include -#include "gnc-date.h" +#include "gnc-date-p.h" #include "qof.h" #ifndef HAVE_STRPTIME @@ -439,7 +439,7 @@ qof_print_date_dmy_buff (char * buff, size_t len, int day, int month, int year) 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 @@ qof_print_date_time_buff (char * buff, size_t len, time_t secs) case QOF_DATE_FORMAT_UTC: { gtm = *gmtime (&secs); - flen = strftime (buff, len, QOF_UTC_DATE_FORMAT, >m); + flen = qof_strftime (buff, len, QOF_UTC_DATE_FORMAT, >m); break; } case QOF_DATE_FORMAT_LOCALE: { - flen = strftime (buff, len, GNC_D_T_FMT, <m); + flen = qof_strftime (buff, len, GNC_D_T_FMT, <m); } break; @@ -645,11 +645,11 @@ qof_print_time_buff (char * buff, size_t len, time_t secs) if(dateFormat == QOF_DATE_FORMAT_UTC) { gtm = *gmtime (&secs); - flen = strftime(buff, len, QOF_UTC_DATE_FORMAT, >m); + flen = qof_strftime(buff, len, QOF_UTC_DATE_FORMAT, >m); return flen; } ltm = *localtime (&secs); - flen = strftime (buff, len, GNC_T_FMT, <m); + flen = qof_strftime (buff, len, GNC_T_FMT, <m); return flen; } @@ -909,7 +909,7 @@ char dateSeparator (void) 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 @@ char dateSeparator (void) 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; +} + + /********************************************************************\ \********************************************************************/ diff --git a/lib/libqof/qof/gnc-date.h b/lib/libqof/qof/gnc-date.h index 22822658fc..eadc00361f 100644 --- a/lib/libqof/qof/gnc-date.h +++ b/lib/libqof/qof/gnc-date.h @@ -328,6 +328,35 @@ gchar dateSeparator(void); * 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 diff --git a/lib/libqof/qof/qof-win32.c b/lib/libqof/qof/qof-win32.c new file mode 100644 index 0000000000..4bd124d865 --- /dev/null +++ b/lib/libqof/qof/qof-win32.c @@ -0,0 +1,81 @@ +/* + * qof-win32.c + * + * Copyright (C) 2007 Andreas Koehler + * + * 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@gnu.org + */ + +#include "config.h" + +#include +#include "gnc-date-p.h" +#include + +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; +}