diff --git a/configure.in b/configure.in index fddb92dc34..3452c8c62f 100644 --- a/configure.in +++ b/configure.in @@ -193,6 +193,25 @@ then AC_MSG_ERROR([Cannot find glib. Check config.log]) fi +AC_MSG_CHECKING(for GLIB - version >= 2.8.0) +if $PKG_CONFIG 'glib-2.0 >= 2.8.0' +then + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GLIB_2_8,1,[System has glib 2.8.0 or better]) + HAVE_GLIB_2_8=yes +else + AC_MSG_RESULT(no) + if test "x${native_win32}" = "xyes"; then + AC_MSG_ERROR([*** GLIB >= 2.8 is required to build Gnucash on Windows.]) + else + GLIB26_LIBS="$GLIB_LIBS" + GLIB_LIBS="\${top_builddir}/lib/glib28/libgnc-glib.la $GLIB_LIBS" + GLIB_CFLAGS="-I\${top_srcdir}/lib/glib28 $GLIB_CFLAGS" + AC_SUBST(GLIB26_LIBS) + fi +fi +AM_CONDITIONAL(HAVE_GLIB_2_8, test "x$HAVE_GLIB_2_8" = "xyes") + AC_MSG_CHECKING(for GLIB - version >= 2.9.0) if $PKG_CONFIG 'glib-2.0 >= 2.9.0' then @@ -2050,6 +2069,7 @@ AC_CONFIG_FILES(po/Makefile.in doc/examples/Makefile intl-scm/Makefile lib/Makefile + lib/glib28/Makefile lib/guile-www/Makefile lib/srfi/Makefile lib/libc/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index 056da09acc..5035aeff10 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = libc guile-www srfi -DIST_SUBDIRS = libc guile-www srfi libgsf-1.12.3 goffice-0.0.4 libqof +SUBDIRS = libc glib28 guile-www srfi +DIST_SUBDIRS = libc glib28 guile-www srfi libgsf-1.12.3 goffice-0.0.4 libqof if !HAVE_GOFFICE if !HAVE_LIBGSF diff --git a/lib/glib28/Makefile.am b/lib/glib28/Makefile.am new file mode 100644 index 0000000000..9d63d2a634 --- /dev/null +++ b/lib/glib28/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libgnc-glib.la + +DUMMYSRCS = dummy.c +REALSRCS = gfileutils-2.8.c gwin32-2.8.c +REALHDRS = gfileutils-2.8.h gstdio-2.8.h gwin32-2.8.h + +if HAVE_GLIB_2_8 +libgnc_glib_la_SOURCES = ${DUMMYSRCS} +else +libgnc_glib_la_SOURCES = ${REALSRCS} +noinst_HEADERS = ${REALHDRS} +endif + +libgnc_glib_la_LIBADD = ${GLIB26_LIBS} + +AM_CFLAGS = ${GLIB_CFLAGS} + +EXTRA_DIST = $(DUMMYSRCS) ${REALSRCS} $(REALHDRS) diff --git a/lib/glib28/dummy.c b/lib/glib28/dummy.c new file mode 100644 index 0000000000..1ee5ccbc23 --- /dev/null +++ b/lib/glib28/dummy.c @@ -0,0 +1,6 @@ +void g_my_dummy_function (void); + +void +g_my_dummy_function (void) +{ +} diff --git a/lib/glib28/gfileutils-2.8.c b/lib/glib28/gfileutils-2.8.c new file mode 100644 index 0000000000..d76afbdabe --- /dev/null +++ b/lib/glib28/gfileutils-2.8.c @@ -0,0 +1,433 @@ +/* gfileutils.c - File utility functions + * + * Copyright 2000 Red Hat, Inc. + * + * GLib 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. + * + * GLib 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 GLib; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Contains all #includes, but otherwise only relevant differences between + * GLib 2.6 and GLib 2.8 */ + +#include "config.h" + +#include + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifndef G_OS_WIN32 +#include +#endif +#include +#include + +#ifdef G_OS_WIN32 +#include +#include +#endif /* G_OS_WIN32 */ + +#ifndef S_ISLNK +#define S_ISLNK(x) 0 +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include + + +static gboolean +rename_file (const char *old_name, + const char *new_name, + GError **err) +{ + errno = 0; + if (g_rename (old_name, new_name) == -1) + { + int save_errno = errno; + gchar *display_old_name = g_filename_display_name (old_name); + gchar *display_new_name = g_filename_display_name (new_name); + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to rename file '%s' to '%s': g_rename() failed: %s", + display_old_name, + display_new_name, + g_strerror (save_errno)); + + g_free (display_old_name); + g_free (display_new_name); + + return FALSE; + } + + return TRUE; +} + +static gboolean +set_umask_permissions (int fd, + GError **err) +{ +#ifdef G_OS_WIN32 + + return TRUE; + +#else + /* All of this function is just to work around the fact that + * there is no way to get the umask without changing it. + * + * We can't just change-and-reset the umask because that would + * lead to a race condition if another thread tried to change + * the umask in between the getting and the setting of the umask. + * So we have to do the whole thing in a child process. + */ + + int save_errno; + pid_t pid; + + pid = fork (); + + if (pid == -1) + { + save_errno = errno; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + _("Could not change file mode: fork() failed: %s"), + g_strerror (save_errno)); + + return FALSE; + } + else if (pid == 0) + { + /* child */ + mode_t mask = umask (0666); + + errno = 0; + if (fchmod (fd, 0666 & ~mask) == -1) + _exit (errno); + else + _exit (0); + + return TRUE; /* To quiet gcc */ + } + else + { + /* parent */ + int status; + + errno = 0; + if (waitpid (pid, &status, 0) == -1) + { + save_errno = errno; + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + _("Could not change file mode: waitpid() failed: %s"), + g_strerror (save_errno)); + + return FALSE; + } + + if (WIFEXITED (status)) + { + save_errno = WEXITSTATUS (status); + + if (save_errno == 0) + { + return TRUE; + } + else + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + _("Could not change file mode: chmod() failed: %s"), + g_strerror (save_errno)); + + return FALSE; + } + } + else if (WIFSIGNALED (status)) + { + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Could not change file mode: Child terminated by signal: %s"), + g_strsignal (WTERMSIG (status))); + + return FALSE; + } + else + { + /* This shouldn't happen */ + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Could not change file mode: Child terminated abnormally")); + return FALSE; + } + } +#endif +} + +static gchar * +write_to_temp_file (const gchar *contents, + gssize length, + const gchar *template, + GError **err) +{ + gchar *tmp_name; + gchar *display_name; + gchar *retval; + FILE *file; + gint fd; + int save_errno; + + retval = NULL; + + tmp_name = g_strdup_printf ("%s.XXXXXX", template); + + errno = 0; + fd = g_mkstemp (tmp_name); + display_name = g_filename_display_name (tmp_name); + + if (fd == -1) + { + save_errno = errno; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to create file '%s': %s", + display_name, g_strerror (save_errno)); + + goto out; + } + + if (!set_umask_permissions (fd, err)) + { + close (fd); + g_unlink (tmp_name); + + goto out; + } + + errno = 0; + file = fdopen (fd, "wb"); + if (!file) + { + save_errno = errno; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to open file '%s' for writing: fdopen() failed: %s", + display_name, + g_strerror (save_errno)); + + close (fd); + g_unlink (tmp_name); + + goto out; + } + + if (length > 0) + { + size_t n_written; + + errno = 0; + + n_written = fwrite (contents, 1, length, file); + + if (n_written < length) + { + save_errno = errno; + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to write file '%s': fwrite() failed: %s", + display_name, + g_strerror (save_errno)); + + fclose (file); + g_unlink (tmp_name); + + goto out; + } + } + + errno = 0; + if (fclose (file) == EOF) + { + save_errno = 0; + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to close file '%s': fclose() failed: %s", + display_name, + g_strerror (save_errno)); + + g_unlink (tmp_name); + + goto out; + } + + retval = g_strdup (tmp_name); + + out: + g_free (tmp_name); + g_free (display_name); + + return retval; +} + +/** + * g_file_set_contents: + * @filename: name of a file to write @contents to, in the GLib file name + * encoding + * @contents: string to write to the file + * @length: length of @contents, or -1 if @contents is a nul-terminated string + * @error: return location for a #GError, or %NULL + * + * Writes all of @contents to a file named @filename, with good error checking. + * If a file called @filename already exists it will be overwritten. + * + * This write is atomic in the sense that it is first written to a temporary + * file which is then renamed to the final name. Notes: + * + * + * On Unix, if @filename already exists hard links to @filename will break. + * Also since the file is recreated, existing permissions, access control + * lists, metadata etc. may be lost. If @filename is a symbolic link, + * the link itself will be replaced, not the linked file. + * + * + * On Windows renaming a file will not remove an existing file with the + * new name, so on Windows there is a race condition between the existing + * file being removed and the temporary file being renamed. + * + * + * On Windows there is no way to remove a file that is open to some + * process, or mapped into memory. Thus, this function will fail if + * @filename already exists and is open. + * + * + * + * If the call was sucessful, it returns %TRUE. If the call was not successful, + * it returns %FALSE and sets @error. The error domain is #G_FILE_ERROR. + * Possible error codes are those in the #GFileError enumeration. + * + * Return value: %TRUE on success, %FALSE if an error occurred + * + * Since: 2.8 + **/ +gboolean +g_file_set_contents (const gchar *filename, + const gchar *contents, + gssize length, + GError **error) +{ + gchar *tmp_filename; + gboolean retval; + GError *rename_error = NULL; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (contents != NULL || length == 0, FALSE); + g_return_val_if_fail (length >= -1, FALSE); + + if (length == -1) + length = strlen (contents); + + tmp_filename = write_to_temp_file (contents, length, filename, error); + + if (!tmp_filename) + { + retval = FALSE; + goto out; + } + + if (!rename_file (tmp_filename, filename, &rename_error)) + { +#ifndef G_OS_WIN32 + + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; + +#else /* G_OS_WIN32 */ + + /* Renaming failed, but on Windows this may just mean + * the file already exists. So if the target file + * exists, try deleting it and do the rename again. + */ + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; + } + + g_error_free (rename_error); + + if (g_unlink (filename) == -1) + { + gchar *display_filename = g_filename_display_name (filename); + + int save_errno = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Existing file '%s' could not be removed: g_unlink() failed: %s", + display_filename, + g_strerror (save_errno)); + + g_free (display_filename); + g_unlink (tmp_filename); + retval = FALSE; + goto out; + } + + if (!rename_file (tmp_filename, filename, error)) + { + g_unlink (tmp_filename); + retval = FALSE; + goto out; + } + +#endif + } + + retval = TRUE; + + out: + g_free (tmp_filename); + return retval; +} diff --git a/lib/glib28/gfileutils-2.8.h b/lib/glib28/gfileutils-2.8.h new file mode 100644 index 0000000000..6b4c5891de --- /dev/null +++ b/lib/glib28/gfileutils-2.8.h @@ -0,0 +1,34 @@ +/* gfileutils.h - File utility functions + * + * Copyright 2000 Red Hat, Inc. + * + * GLib 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. + * + * GLib 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 GLib; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Contains only relevant differences between GLib 2.6 and GLib 2.8 */ + +#ifndef __G_FILEUTILS_2_8_H__ +#define __G_FILEUTILS_2_8_H__ + +G_BEGIN_DECLS + +gboolean g_file_set_contents (const gchar *filename, + const gchar *contents, + gssize length, + GError **error); + +G_END_DECLS + +#endif /* __G_FILEUTILS_2_8_H__ */ diff --git a/lib/glib28/gstdio-2.8.h b/lib/glib28/gstdio-2.8.h new file mode 100644 index 0000000000..f834aebcc1 --- /dev/null +++ b/lib/glib28/gstdio-2.8.h @@ -0,0 +1,38 @@ +/* gstdio.h - GFilename wrappers for C library functions + * + * Copyright 2004 Tor Lillqvist + * + * GLib 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. + * + * GLib 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 GLib; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Contains only relevant differences between GLib 2.6 and GLib 2.8 */ + +#ifndef __G_STDIO_28_H__ +#define __G_STDIO_28_H__ + +#ifdef G_OS_WIN32 +#error "On Windows HAVE_GLIB_2_8 must be defined and gstdio28.h not included." +#endif + +G_BEGIN_DECLS + +#define g_access access +#define g_chmod chmod +#define g_creat creat +#define g_chdir chdir + +G_END_DECLS + +#endif /* __G_STDIO_H__ */ diff --git a/lib/glib28/gwin32-2.8.c b/lib/glib28/gwin32-2.8.c new file mode 100644 index 0000000000..d26dd8451b --- /dev/null +++ b/lib/glib28/gwin32-2.8.c @@ -0,0 +1,101 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 1998-1999 Tor Lillqvist + * + * 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. + */ +/* Contains all #includes, but otherwise only relevant differences between + * GLib 2.6 and GLib 2.8 */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include + +#define STRICT /* Strict typing, please */ +#include +#undef STRICT +#ifndef G_WITH_CYGWIN +#include +#endif +#include +#include +#ifdef _MSC_VER +# include +#endif /* _MSC_VER */ + +#include + + +/** + * g_win32_locale_filename_from_utf8: + * @utf8filename: a UTF-8 encoded filename. + * + * Converts a filename from UTF-8 to the system codepage. + * + * On NT-based Windows, on NTFS file systems, file names are in + * Unicode. It is quite possible that Unicode file names contain + * characters not representable in the system codepage. (For instance, + * Greek or Cyrillic characters on Western European or US Windows + * installations, or various less common CJK characters on CJK Windows + * installations.) + * + * In such a case, and if the filename refers to an existing file, and + * the file system stores alternate short (8.3) names for directory + * entries, the short form of the filename is returned. Note that the + * "short" name might in fact be longer than the Unicode name if the + * Unicode name has very short pathname components containing + * non-ASCII characters. If no system codepage name for the file is + * possible, %NULL is returned. + * + * The return value is dynamically allocated and should be freed with + * g_free() when no longer needed. + * + * Return value: The converted filename, or %NULL on conversion + * failure and lack of short names. + * + * Since: 2.8 + */ +gchar * +g_win32_locale_filename_from_utf8 (const gchar *utf8filename) +{ + gchar *retval = g_locale_from_utf8 (utf8filename, -1, NULL, NULL, NULL); + + if (retval == NULL && G_WIN32_HAVE_WIDECHAR_API ()) + { + /* Conversion failed, so convert to wide chars, check if there + * is a 8.3 version, and use that. + */ + wchar_t *wname = g_utf8_to_utf16 (utf8filename, -1, NULL, NULL, NULL); + if (wname != NULL) + { + wchar_t wshortname[MAX_PATH + 1]; + if (GetShortPathNameW (wname, wshortname, G_N_ELEMENTS (wshortname))) + { + gchar *tem = g_utf16_to_utf8 (wshortname, -1, NULL, NULL, NULL); + retval = g_locale_from_utf8 (tem, -1, NULL, NULL, NULL); + g_free (tem); + } + g_free (wname); + } + } + return retval; +} diff --git a/lib/glib28/gwin32-2.8.h b/lib/glib28/gwin32-2.8.h new file mode 100644 index 0000000000..5f6e4982d0 --- /dev/null +++ b/lib/glib28/gwin32-2.8.h @@ -0,0 +1,30 @@ +/* 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. + */ +/* Contains only relevant differences between GLib 2.6 and GLib 2.8 */ + +#ifndef __G_WIN32_2_8_H__ +#define __G_WIN32_2_8_H__ + +G_BEGIN_DECLS + +gchar* g_win32_locale_filename_from_utf8 (const gchar *utf8filename); + +G_END_DECLS + +#endif /* __G_WIN32_2_8_H__ */ diff --git a/src/backend/file/gnc-backend-file.c b/src/backend/file/gnc-backend-file.c index a54632fc76..80cf6c47de 100644 --- a/src/backend/file/gnc-backend-file.c +++ b/src/backend/file/gnc-backend-file.c @@ -33,9 +33,11 @@ #include #include #include +#ifndef HAVE_GLIB_2_8 +#include +#endif #include #include -#include #include #include #include @@ -288,18 +290,7 @@ file_session_end(QofBackend *be_start) #ifdef G_OS_WIN32 /* On windows, we need to allow write-access before g_unlink() can succeed */ - wchar_t *wlock = g_utf8_to_utf16 (be->lockfile, -1, NULL, NULL, NULL); - if (wlock) { - rv = _wchmod (wlock, _S_IWRITE); - } else { - errno = EINVAL; - rv = 1; - } - if (rv) { - PWARN("Error on chmod(%s): %d: %s", be->lockfile, - errno, strerror(errno) ? strerror(errno) : ""); - } - g_free (wlock); + rv = g_chmod (be->lockfile, S_IWRITE | S_IREAD); #endif rv = g_unlink (be->lockfile); if (rv) { diff --git a/src/import-export/qif-import/druid-qif-import.c b/src/import-export/qif-import/druid-qif-import.c index a1801edf25..e4a24d5c7b 100644 --- a/src/import-export/qif-import/druid-qif-import.c +++ b/src/import-export/qif-import/druid-qif-import.c @@ -26,8 +26,11 @@ #include #include +#include +#ifndef HAVE_GLIB_2_8 +#include +#endif #include -#include #include #include @@ -477,17 +480,7 @@ gnc_ui_qif_import_load_file_next_cb(GnomeDruidPage * page, gnc_error_dialog(wind->window, _("Please select a file to load.")); return TRUE; } - -#ifdef G_OS_WIN32 - { - wchar_t *wpath = g_utf8_to_utf16(path_to_load, -1, NULL, NULL, NULL); - rv = wpath ? _waccess(wpath, R_OK) : -1; - g_free(wpath); - } -#else - rv = access(path_to_load, R_OK); -#endif - if (rv < 0) { + else if (g_access(path_to_load, R_OK) < 0) { /* stay here if bad file */ gnc_error_dialog(wind->window, _("File not found or read permission denied. "