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