Added missing release notes to Changes file.
[rox-filer.git] / ROX-Filer / src / choices.c
blob7026b3fbadd1471e95bf06b6cfa076e2dfea20ae
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
19 /* choices.c - code for handling loading and saving of user choices */
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/param.h>
26 #include <fcntl.h>
27 #include <errno.h>
29 #include "global.h"
31 #include "gui_support.h"
33 #include "choices.h"
35 static gboolean saving_disabled = TRUE;
36 static gchar **dir_list = NULL;
37 static gchar **xdg_dir_list = NULL;
38 static int xdg_dir_count= 0 ;
40 static struct migration {
41 const char *dir;
42 const char *site;
43 int symlink;
44 } to_migrate[]={
45 {"ROX-Filer", SITE, TRUE},
46 {"SendTo", SITE, TRUE},
47 {"Templates", SITE, TRUE},
48 {"MIME-types", SITE, TRUE},
49 {"MIME-icons", SITE, TRUE},
50 {"MIME-thumb", SITE, TRUE},
52 {NULL, NULL, 0}
55 /* Static prototypes */
56 static gboolean exists(char *path);
57 static void migrate_choices(void);
59 /****************************************************************
60 * EXTERNAL INTERFACE *
61 ****************************************************************/
64 /* Reads in CHOICESPATH and constructs the directory list table.
65 * You must call this before using any other choices_* functions.
67 * If CHOICESPATH does not exist then a suitable default is used.
69 void choices_init(void)
71 char *choices;
72 const char *env;
73 char **dirs;
74 int i, n;
76 g_return_if_fail(dir_list == NULL);
78 /* Initialize old system */
79 choices = getenv("CHOICESPATH");
81 if (choices)
83 if (*choices != ':' && *choices != '\0')
84 saving_disabled = FALSE;
86 while (*choices == ':')
87 choices++;
89 if (*choices == '\0')
91 dir_list = g_new(char *, 1);
92 dir_list[0] = NULL;
94 else
95 dir_list = g_strsplit(choices, ":", 0);
97 else
99 saving_disabled = FALSE;
101 dir_list = g_new(gchar *, 4);
102 dir_list[0] = g_build_filename(g_get_home_dir(), "Choices",
103 NULL);
104 dir_list[1] = g_strdup("/usr/local/share/Choices");
105 dir_list[2] = g_strdup("/usr/share/Choices");
106 dir_list[3] = NULL;
109 /* Initialize new system */
110 env = getenv("XDG_CONFIG_DIRS");
111 if (!env)
112 env = "/etc/xdg";
113 dirs = g_strsplit(env, ":", 0);
114 g_return_if_fail(dirs != NULL);
115 for (n = 0; dirs[n]; n++)
117 for (i = n; i > 0; i--)
118 dirs[i] = dirs[i - 1];
119 env = getenv("XDG_CONFIG_HOME");
120 if (env)
121 dirs[0] = g_strdup(env);
122 else
123 dirs[0] = g_build_filename(g_get_home_dir(), ".config", NULL);
125 xdg_dir_list = dirs;
126 xdg_dir_count = n + 1;
128 #if 0
130 gchar **cdir = dir_list;
132 for(i=0; i<xdg_dir_count; i++)
133 g_print("[ XDG dir '%s' ]\n", xdg_dir_list[i]);
135 while (*cdir)
137 g_print("[ choices dir '%s' ]\n", *cdir);
138 cdir++;
141 g_print("[ saving is %s ]\n", saving_disabled ? "disabled"
142 : "enabled");
144 #endif
148 /* If our XDG choices directory does not yet exist, offer to move the
149 * old config over
151 void choices_migrate(void)
153 gchar *newpath;
155 /* Attempt migration */
156 newpath=choices_find_xdg_path_save(".", PROJECT, SITE, FALSE);
157 if(!exists(newpath) && !saving_disabled)
158 migrate_choices();
159 g_free(newpath);
162 void choices_free_list(GPtrArray *list)
164 guint i;
166 g_return_if_fail(list != NULL);
168 for (i = 0; i < list->len; i++)
169 g_free(g_ptr_array_index(list, i));
171 g_ptr_array_free(list, TRUE);
174 /* Get the pathname of a choices file to load. Eg:
176 * choices_find_path_load("menus", "ROX-Filer")
177 * -> "/usr/local/share/Choices/ROX-Filer/menus".
179 * The return values may be NULL - use built-in defaults.
180 * g_free() the result.
182 static gchar *choices_find_path_load(const char *leaf, const char *dir)
184 gchar **cdir = dir_list;
186 g_return_val_if_fail(dir_list != NULL, NULL);
188 for (; *cdir; cdir++)
190 gchar *path;
192 path = g_build_filename(*cdir, dir, leaf, NULL);
194 if (exists(path))
195 return path;
197 g_free(path);
200 return NULL;
203 /* Get the pathname of a choices file to load, using the XDG paths. Eg:
205 * choices_find_xdg_path_load("menus", "ROX-Filer", "rox.sourceforge.net")
206 * -> "/etc/xdg/rox.sourceforge.net/ROX-Filer/menus".
208 * Falls back on choices_find_path_load(leaf, dir) if it fails
209 * The return values may be NULL - use built-in defaults.
210 * g_free() the result.
212 gchar *choices_find_xdg_path_load(const char *leaf, const char *dir,
213 const char *site)
215 int i;
217 g_return_val_if_fail(dir_list != NULL, NULL);
219 for (i=0; i<xdg_dir_count; i++)
221 gchar *path;
223 if(site)
224 path = g_build_filename(xdg_dir_list[i], site,
225 dir, leaf, NULL);
226 else
227 path = g_build_filename(xdg_dir_list[i], dir,
228 leaf, NULL);
230 if (exists(path))
231 return path;
233 g_free(path);
236 return choices_find_path_load(leaf, dir);
239 /* Returns the pathname of a file to save to, or NULL if saving is
240 * disabled. If 'create' is TRUE then intermediate directories will
241 * be created (set this to FALSE if you just want to find out where
242 * a saved file would go without actually altering the filesystem).
244 * g_free() the result.
246 static gchar *choices_find_path_save(const char *leaf, const char *dir,
247 gboolean create)
249 gchar *path, *retval;
251 g_return_val_if_fail(dir_list != NULL, NULL);
253 if (saving_disabled)
254 return NULL;
256 if (create && !exists(dir_list[0]))
258 if (mkdir(dir_list[0], 0777))
259 g_warning("mkdir(%s): %s\n", dir_list[0],
260 g_strerror(errno));
263 path = g_build_filename(dir_list[0], dir, NULL);
264 if (create && !exists(path))
266 if (mkdir(path, 0777))
267 g_warning("mkdir(%s): %s\n", path, g_strerror(errno));
270 retval = g_build_filename(path, leaf, NULL);
271 g_free(path);
273 return retval;
276 /* Returns the pathname of a file to save to, or NULL if saving is
277 * disabled. If 'create' is TRUE then intermediate directories will
278 * be created (set this to FALSE if you just want to find out where
279 * a saved file would go without actually altering the filesystem).
281 * g_free() the result.
283 gchar *choices_find_xdg_path_save(const char *leaf, const char *dir,
284 const char *site, gboolean create)
286 gchar *path, *retval, *tmp;
288 g_return_val_if_fail(xdg_dir_list != NULL, NULL);
290 if (create && !exists(xdg_dir_list[0]))
292 if (mkdir(xdg_dir_list[0], 0777))
293 g_warning("mkdir(%s): %s\n", xdg_dir_list[0],
294 g_strerror(errno));
297 if(site)
299 path = g_build_filename(xdg_dir_list[0], site, NULL);
300 if (create && !exists(path))
302 if (mkdir(path, 0777))
303 g_warning("mkdir(%s): %s\n", path,
304 g_strerror(errno));
306 tmp=path;
307 } else {
308 tmp=g_strdup(xdg_dir_list[0]);
311 path = g_build_filename(tmp, dir, NULL);
312 g_free(tmp);
313 if (create && !exists(path))
315 if (mkdir(path, 0777))
316 g_warning("mkdir(%s): %s\n", path, g_strerror(errno));
319 retval = g_build_filename(path, leaf, NULL);
320 g_free(path);
322 return retval;
326 * Returns an array of the directories in XDG_CONFIG_HOME and XDG_CONFIG_DIRS
327 * which contain a subdirectory called 'dir' (optionally in a subdirectory
328 * called site).
330 * Lower-indexed results should override higher-indexed ones.
332 * Free the list using choices_free_list().
334 GPtrArray *choices_list_xdg_dirs(char *dir, char *site)
336 GPtrArray *list;
337 int i;
339 g_return_val_if_fail(xdg_dir_list != NULL, NULL);
341 list = g_ptr_array_new();
343 for (i=0; i<xdg_dir_count; i++)
345 guchar *path;
347 if(site)
348 path = g_build_filename(xdg_dir_list[i], site,
349 dir, NULL);
350 else
351 path = g_build_filename(xdg_dir_list[i], dir, NULL);
353 if (exists(path))
354 g_ptr_array_add(list, path);
355 else
356 g_free(path);
359 return list;
362 /****************************************************************
363 * INTERNAL FUNCTIONS *
364 ****************************************************************/
367 /* Returns TRUE if the object exists, FALSE if it doesn't */
368 static gboolean exists(char *path)
370 struct stat info;
372 return stat(path, &info) == 0;
375 #include <unistd.h>
376 #include <gtk/gtk.h>
378 static void migrate_choices(void)
380 gchar *opath, *npath;
381 int failed=0;
382 int i;
383 gchar *src, *dest;
384 gboolean migrated_something = FALSE;
386 npath=choices_find_xdg_path_save("...", PROJECT, SITE, FALSE);
387 opath=choices_find_path_save("...", PROJECT,FALSE);
390 dest=choices_find_xdg_path_save(".", PROJECT, SITE, TRUE);
391 g_free(dest);
394 for(i=0; to_migrate[i].dir; i++) {
395 src=g_build_filename(dir_list[0], to_migrate[i].dir, NULL);
396 dest=choices_find_xdg_path_save(NULL, NULL,
397 to_migrate[i].site, TRUE);
398 g_free(dest);
399 dest=choices_find_xdg_path_save(NULL,
400 to_migrate[i].dir,
401 to_migrate[i].site,
402 FALSE);
403 errno=0;
404 if(exists(src)) {
405 if(rename(src, dest)==0) {
406 if(to_migrate[i].symlink)
407 symlink(dest, src);
408 migrated_something = TRUE;
409 } else {
410 g_warning("rename(%s, %s): %s\n",
411 src, dest,
412 g_strerror(errno));
413 failed++;
415 } else if(to_migrate[i].symlink) {
417 if(!exists(dir_list[0])) {
418 if (mkdir(dir_list[0], 0777))
419 g_warning("mkdir(%s): %s\n",
420 dir_list[0],
421 g_strerror(errno));
423 symlink(dest, src);
426 g_free(src);
427 g_free(dest);
430 if (migrated_something)
432 gchar *failed_msg = NULL;
433 if (failed)
434 failed_msg = g_strdup_printf(_("%d directories could not be migrated"),
435 failed);
436 info_message(_("Choices have been moved from \n"
437 "%s\n "
438 "to the new location \n"
439 "%s\n%s"),
440 opath, npath, failed_msg ? failed_msg : "");
441 g_free(failed_msg);
444 g_free(opath);
445 g_free(npath);