/* * * GeanyGenDoc, a Geany plugin to ease generation of source code documentation * Copyright (C) 2009-2011 Colomban Wendling * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H # include "config.h" /* for the gettext domain */ #endif #include "ggd-utils.h" #include #include #include #include #include #include /* for BUFSIZ */ #include #include #include /* for G_FILE_ERROR and friends */ #include #include "ggd-plugin.h" /* to access Geany data/funcs */ /* * set_file_error_from_errno: * @error: A #GError * @errnum: An errno value * @filename: The file name for which the error applies in the GLib file names * encoding * * Sets a #GError from an errno value, prefixed with a file's name. */ #define set_file_error_from_errno(error, errnum, filename) \ G_STMT_START { \ gchar *s_e_f_e_filename; \ gint s_e_f_e_errum = errnum; /* need if @errnum is errno */ \ \ s_e_f_e_filename = g_filename_display_name (filename); \ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (s_e_f_e_errum), \ "%s: %s", s_e_f_e_filename, g_strerror (s_e_f_e_errum)); \ g_free (s_e_f_e_filename); \ } G_STMT_END /* * ggd_copy_file: * @input: Path of the file to copy, in the GLib file names encoding * @output: Path of the destination, in the GLib file names encoding * @exclusive: %FALSE to override the destination if it already exist, %TRUE * otherwise * @mode: Mode to use for creating the file * @error: Return location for errors, or %NULL to ignore them * * Copies a file to a destination. If @exlusive is %TRUE, the destination is not * overwritten if it exists in a safe way. Otherwise, the destination file is * simply truncated before the copy, with all the problems that can happen (such * as partially overwritten file and so). * * Returns: %TRUE on success, %FALSE otherwise. */ static gboolean ggd_copy_file (const gchar *input, const gchar *output, gboolean exclusive, mode_t mode, GError **error) { gboolean success = FALSE; gint fd_in; fd_in = g_open (input, O_RDONLY, 0); if (fd_in < 0) { set_file_error_from_errno (error, errno, input); } else { gint fd_out; gint flags_out; flags_out = O_WRONLY | O_CREAT | O_TRUNC; if (exclusive) flags_out |= O_EXCL; fd_out = g_open (output, flags_out, mode); if (fd_out < 0) { set_file_error_from_errno (error, errno, output); } else { gchar buf[BUFSIZ]; gsize buf_size = sizeof buf; gssize size_in; gssize size_out; success = TRUE; do { size_in = read (fd_in, buf, buf_size); if (size_in < 0) { set_file_error_from_errno (error, errno, input); success = FALSE; } else { size_out = write (fd_out, buf, (size_t)size_in); if (size_out < 0) { set_file_error_from_errno (error, errno, output); success = FALSE; } else if (size_out < size_in) { gchar *display_input; display_input = g_filename_display_name (input); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s: failed to write %"G_GSSIZE_FORMAT" bytes " "(read %"G_GSSIZE_FORMAT", wrote %"G_GSSIZE_FORMAT")", display_input, size_in - size_out, size_in, size_out); g_free (display_input); success = FALSE; } } } while (success && (size_t)size_in >= buf_size); close (fd_out); } close (fd_in); } return success; } static gchar * get_data_dir_path (const gchar *filename) { gchar *prefix = NULL; gchar *path; #ifdef G_OS_WIN32 prefix = g_win32_get_package_installation_directory_of_module (NULL); #elif defined(__APPLE__) if (g_getenv ("GEANY_PLUGINS_SHARE_PATH")) return g_build_filename (g_getenv ("GEANY_PLUGINS_SHARE_PATH"), PLUGIN, filename, NULL); #endif path = g_build_filename (prefix ? prefix : "", PLUGINDATADIR, filename, NULL); g_free (prefix); return path; } /** * ggd_get_config_file: * @name: The name of the configuration file to get (ASCII string) * @section: The name of the configuration section of the file, or %NULL for the * default one (ASCII string) * @perms_req: Requested permissions on the configuration file * @error: Return location for errors, or %NULL to ignore them * * Gets the configuration file path from its name. * Configuration files may be either the system-wide or the user-specific ones, * depending on their existence and on the requested permissions. * * If @GGD_PERM_NOCREAT is not given in @perms_req and @GGD_PERM_W is, the file * at the returned path will be copied from the system configuration directory, * or created empty if the system file doesn't exist. * * Returns: The path for the requested configuration file in the GLib file names * encoding, or %NULL if path cannot be found. */ gchar * ggd_get_config_file (const gchar *name, const gchar *section, GgdPerms perms_req, GError **error) { gchar *path = NULL; gchar *user_dir; gchar *user_path; gchar *system_dir; gchar *system_path; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* here we guess the locale encoding is ASCII-compatible, anyway it's the case * on Windows since we use UTF-8 and on UNIX it would cause too much troubles * everywhere if it is not anyway */ user_dir = g_build_filename (geany->app->configdir, "plugins", GGD_PLUGIN_CNAME, section, NULL); system_dir = get_data_dir_path (section); user_path = g_build_filename (user_dir, name, NULL); system_path = g_build_filename (system_dir, name, NULL); if (perms_req & GGD_PERM_R) { if (g_file_test (user_path, G_FILE_TEST_EXISTS)) { if (! g_file_test (user_path, G_FILE_TEST_IS_REGULAR)) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("File \"%s\" exists but is not a regular file"), user_path); } else { path = user_path; } } if (! path) { if (g_file_test (system_path, G_FILE_TEST_EXISTS)) { if (! g_file_test (system_path, G_FILE_TEST_IS_REGULAR)) { g_clear_error (error); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("File \"%s\" exists but is not a regular file"), system_path); } else { path = system_path; } } } if (! path && error && ! *error) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("%s: no such file or directory"), user_path); } } if (perms_req & GGD_PERM_W) { if (path == user_path) { /* nothing to do, write should succeed on user's path */ } else { path = NULL; if (g_mkdir_with_parents (user_dir, 0750) < 0) { gint errnum = errno; g_clear_error (error); set_file_error_from_errno (error, errnum, user_dir); } else if (perms_req & GGD_PERM_NOCREAT) { /* just give the user path if user don't want the copy to be done */ path = user_path; } else { GError *gerr = NULL; /* try to copy the system file to the user's configuration directory */ if (ggd_copy_file (system_path, user_path, TRUE, 0640, &gerr) || /* the file already exists (unlikely if GGD_PERMS_R is set) */ gerr->code == G_FILE_ERROR_EXIST) { path = user_path; if (gerr) g_clear_error (&gerr); g_clear_error (error); } else if (gerr->code == G_FILE_ERROR_NOENT) { /* the system file doesn't exist. No problem, just try to create the * file (if it does not already exist) */ gint fd; g_clear_error (&gerr); fd = g_open (user_path, O_CREAT | O_WRONLY, 0640); if (fd < 0) { set_file_error_from_errno (&gerr, errno, user_path); } else { close (fd); path = user_path; g_clear_error (error); } } if (gerr) { g_clear_error (error); g_propagate_error (error, gerr); } } } } if (path != user_path) g_free (user_path); if (path != system_path) g_free (system_path); g_free (user_dir); g_free (system_dir); return path; }