1 /*
2 *
3 * GeanyGenDoc, a Geany plugin to ease generation of source code documentation
4 * Copyright (C) 2009-2011 Colomban Wendling <ban@herbesfolles.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h" /* for the gettext domain */
24 #endif
25
26 #include "ggd-utils.h"
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <stdio.h> /* for BUFSIZ */
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <gio/gio.h> /* for G_FILE_ERROR and friends */
37 #include <geanyplugin.h>
38
39 #include "ggd-plugin.h" /* to access Geany data/funcs */
40
41
42 /*
43 * set_file_error_from_errno:
44 * @error: A #GError
45 * @errnum: An errno value
46 * @filename: The file name for which the error applies in the GLib file names
47 * encoding
48 *
49 * Sets a #GError from an errno value, prefixed with a file's name.
50 */
51 #define set_file_error_from_errno(error, errnum, filename) \
52 G_STMT_START { \
53 gchar *s_e_f_e_filename; \
54 gint s_e_f_e_errum = errnum; /* need if @errnum is errno */ \
55 \
56 s_e_f_e_filename = g_filename_display_name (filename); \
57 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (s_e_f_e_errum), \
58 "%s: %s", s_e_f_e_filename, g_strerror (s_e_f_e_errum)); \
59 g_free (s_e_f_e_filename); \
60 } G_STMT_END
61
62 /*
63 * ggd_copy_file:
64 * @input: Path of the file to copy, in the GLib file names encoding
65 * @output: Path of the destination, in the GLib file names encoding
66 * @exclusive: %FALSE to override the destination if it already exist, %TRUE
67 * otherwise
68 * @mode: Mode to use for creating the file
69 * @error: Return location for errors, or %NULL to ignore them
70 *
71 * Copies a file to a destination. If @exlusive is %TRUE, the destination is not
72 * overwritten if it exists in a safe way. Otherwise, the destination file is
73 * simply truncated before the copy, with all the problems that can happen (such
74 * as partially overwritten file and so).
75 *
76 * Returns: %TRUE on success, %FALSE otherwise.
77 */
78 static gboolean
ggd_copy_file(const gchar * input,const gchar * output,gboolean exclusive,mode_t mode,GError ** error)79 ggd_copy_file (const gchar *input,
80 const gchar *output,
81 gboolean exclusive,
82 mode_t mode,
83 GError **error)
84 {
85 gboolean success = FALSE;
86 gint fd_in;
87
88 fd_in = g_open (input, O_RDONLY, 0);
89 if (fd_in < 0) {
90 set_file_error_from_errno (error, errno, input);
91 } else {
92 gint fd_out;
93 gint flags_out;
94
95 flags_out = O_WRONLY | O_CREAT | O_TRUNC;
96 if (exclusive) flags_out |= O_EXCL;
97 fd_out = g_open (output, flags_out, mode);
98 if (fd_out < 0) {
99 set_file_error_from_errno (error, errno, output);
100 } else {
101 gchar buf[BUFSIZ];
102 gsize buf_size = sizeof buf;
103 gssize size_in;
104 gssize size_out;
105
106 success = TRUE;
107 do {
108 size_in = read (fd_in, buf, buf_size);
109 if (size_in < 0) {
110 set_file_error_from_errno (error, errno, input);
111 success = FALSE;
112 } else {
113 size_out = write (fd_out, buf, (size_t)size_in);
114 if (size_out < 0) {
115 set_file_error_from_errno (error, errno, output);
116 success = FALSE;
117 } else if (size_out < size_in) {
118 gchar *display_input;
119
120 display_input = g_filename_display_name (input);
121 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
122 "%s: failed to write %"G_GSSIZE_FORMAT" bytes "
123 "(read %"G_GSSIZE_FORMAT", wrote %"G_GSSIZE_FORMAT")",
124 display_input, size_in - size_out, size_in, size_out);
125 g_free (display_input);
126 success = FALSE;
127 }
128 }
129 } while (success && (size_t)size_in >= buf_size);
130 close (fd_out);
131 }
132 close (fd_in);
133 }
134
135 return success;
136 }
137
138 static gchar *
get_data_dir_path(const gchar * filename)139 get_data_dir_path (const gchar *filename)
140 {
141 gchar *prefix = NULL;
142 gchar *path;
143
144 #ifdef G_OS_WIN32
145 prefix = g_win32_get_package_installation_directory_of_module (NULL);
146 #elif defined(__APPLE__)
147 if (g_getenv ("GEANY_PLUGINS_SHARE_PATH"))
148 return g_build_filename (g_getenv ("GEANY_PLUGINS_SHARE_PATH"),
149 PLUGIN, filename, NULL);
150 #endif
151 path = g_build_filename (prefix ? prefix : "", PLUGINDATADIR, filename, NULL);
152 g_free (prefix);
153 return path;
154 }
155
156 /**
157 * ggd_get_config_file:
158 * @name: The name of the configuration file to get (ASCII string)
159 * @section: The name of the configuration section of the file, or %NULL for the
160 * default one (ASCII string)
161 * @perms_req: Requested permissions on the configuration file
162 * @error: Return location for errors, or %NULL to ignore them
163 *
164 * Gets the configuration file path from its name.
165 * Configuration files may be either the system-wide or the user-specific ones,
166 * depending on their existence and on the requested permissions.
167 *
168 * If @GGD_PERM_NOCREAT is not given in @perms_req and @GGD_PERM_W is, the file
169 * at the returned path will be copied from the system configuration directory,
170 * or created empty if the system file doesn't exist.
171 *
172 * Returns: The path for the requested configuration file in the GLib file names
173 * encoding, or %NULL if path cannot be found.
174 */
175 gchar *
ggd_get_config_file(const gchar * name,const gchar * section,GgdPerms perms_req,GError ** error)176 ggd_get_config_file (const gchar *name,
177 const gchar *section,
178 GgdPerms perms_req,
179 GError **error)
180 {
181 gchar *path = NULL;
182 gchar *user_dir;
183 gchar *user_path;
184 gchar *system_dir;
185 gchar *system_path;
186
187 g_return_val_if_fail (name != NULL, NULL);
188 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
189
190 /* here we guess the locale encoding is ASCII-compatible, anyway it's the case
191 * on Windows since we use UTF-8 and on UNIX it would cause too much troubles
192 * everywhere if it is not anyway */
193 user_dir = g_build_filename (geany->app->configdir, "plugins",
194 GGD_PLUGIN_CNAME, section, NULL);
195 system_dir = get_data_dir_path (section);
196 user_path = g_build_filename (user_dir, name, NULL);
197 system_path = g_build_filename (system_dir, name, NULL);
198 if (perms_req & GGD_PERM_R) {
199 if (g_file_test (user_path, G_FILE_TEST_EXISTS)) {
200 if (! g_file_test (user_path, G_FILE_TEST_IS_REGULAR)) {
201 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
202 _("File \"%s\" exists but is not a regular file"),
203 user_path);
204 } else {
205 path = user_path;
206 }
207 }
208 if (! path) {
209 if (g_file_test (system_path, G_FILE_TEST_EXISTS)) {
210 if (! g_file_test (system_path, G_FILE_TEST_IS_REGULAR)) {
211 g_clear_error (error);
212 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
213 _("File \"%s\" exists but is not a regular file"),
214 system_path);
215 } else {
216 path = system_path;
217 }
218 }
219 }
220 if (! path && error && ! *error) {
221 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
222 _("%s: no such file or directory"), user_path);
223 }
224 }
225 if (perms_req & GGD_PERM_W) {
226 if (path == user_path) {
227 /* nothing to do, write should succeed on user's path */
228 } else {
229 path = NULL;
230 if (g_mkdir_with_parents (user_dir, 0750) < 0) {
231 gint errnum = errno;
232
233 g_clear_error (error);
234 set_file_error_from_errno (error, errnum, user_dir);
235 } else if (perms_req & GGD_PERM_NOCREAT) {
236 /* just give the user path if user don't want the copy to be done */
237 path = user_path;
238 } else {
239 GError *gerr = NULL;
240
241 /* try to copy the system file to the user's configuration directory */
242 if (ggd_copy_file (system_path, user_path, TRUE, 0640, &gerr) ||
243 /* the file already exists (unlikely if GGD_PERMS_R is set) */
244 gerr->code == G_FILE_ERROR_EXIST) {
245 path = user_path;
246 if (gerr) g_clear_error (&gerr);
247 g_clear_error (error);
248 } else if (gerr->code == G_FILE_ERROR_NOENT) {
249 /* the system file doesn't exist. No problem, just try to create the
250 * file (if it does not already exist) */
251 gint fd;
252
253 g_clear_error (&gerr);
254 fd = g_open (user_path, O_CREAT | O_WRONLY, 0640);
255 if (fd < 0) {
256 set_file_error_from_errno (&gerr, errno, user_path);
257 } else {
258 close (fd);
259 path = user_path;
260 g_clear_error (error);
261 }
262 }
263 if (gerr) {
264 g_clear_error (error);
265 g_propagate_error (error, gerr);
266 }
267 }
268 }
269 }
270 if (path != user_path) g_free (user_path);
271 if (path != system_path) g_free (system_path);
272 g_free (user_dir);
273 g_free (system_dir);
274
275 return path;
276 }
277