Initial check-in to git.
[XDGStart.git] / src / xxdgautostart.c
blob57bcaeeab17622f7260aa6b5aed30d3239a3c114
1 /*
2 * By Tony Houghton, <h@realh.co.uk>.
3 */
5 #include "config.h"
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
16 #include "dialogs.h"
17 #include "xxdgautostart.h"
19 extern char *locale_name;
20 extern char *short_locale_name;
22 void xdesktop_entry_read_name_from_keyfile(DesktopEntry *xde)
24 g_free(xde->name);
25 if (locale_name)
27 xde->name = g_key_file_get_locale_string(xde->kf, _DE, "Name",
28 locale_name, NULL);
30 if (!xde->name && short_locale_name)
32 xde->name = g_key_file_get_locale_string(xde->kf, _DE, "Name",
33 short_locale_name, NULL);
35 if (!xde->name)
36 xde->name = g_key_file_get_string(xde->kf, _DE, "Name", NULL);
37 if (!xde->name)
38 xde->name = g_strdup(_("No name"));
41 DesktopEntry *xdesktop_entry_new(const char *pathname, const char *basename,
42 gboolean savable)
44 DesktopEntry *xde = g_new0(DesktopEntry, 1);
46 if (!desktop_entry_load(xde, pathname, basename, TRUE, savable))
48 desktop_entry_delete(xde);
49 xde = NULL;
51 if (xde)
52 xdesktop_entry_read_name_from_keyfile(xde);
53 return xde;
56 gboolean xdesktop_entry_get_only_in_rox(DesktopEntry *xde)
58 return (xde->only_show_in && xde->only_show_in[0] &&
59 !strcmp(xde->only_show_in[0], "ROX") &&
60 (!xde->only_show_in[1] || !xde->only_show_in[1][0]));
63 gboolean xdesktop_entry_can_set_only_in_rox(DesktopEntry *xde)
65 return xdesktop_entry_get_only_in_rox(xde) ||
66 (desktop_entry_get_show_in_rox(xde) &&
67 str_v_is_empty(xde->only_show_in));
70 static int str_v_len(char const **str_v)
72 int n;
74 for (n = 0; str_v && str_v[n]; ++n);
75 return n;
78 static void update_kf_from_show_ins(DesktopEntry *xde)
80 if (!xde->only_show_in)
82 g_key_file_remove_key(xde->kf, _DE, "OnlyShowIn", NULL);
84 else
86 g_key_file_set_string_list(xde->kf, _DE, "OnlyShowIn",
87 (char const **) xde->only_show_in,
88 str_v_len((char const **) xde->only_show_in));
90 if (!xde->not_show_in)
92 g_key_file_remove_key(xde->kf, _DE, "NotShowIn", NULL);
94 else
96 g_key_file_set_string_list(xde->kf, _DE, "NotShowIn",
97 (char const **) xde->not_show_in,
98 str_v_len((char const **) xde->not_show_in));
102 void xdesktop_entry_set_only_in_rox(DesktopEntry *xde, gboolean state)
104 g_strfreev(xde->only_show_in);
105 xde->only_show_in = NULL;
106 g_strfreev(xde->not_show_in);
107 xde->not_show_in = NULL;
108 if (state)
110 xde->only_show_in = g_new0(char *, 2);
111 xde->only_show_in[0] = g_strdup("ROX");
113 update_kf_from_show_ins(xde);
116 static void remove_rox_from_strv(char ***p_str_v)
118 if (!str_v_is_empty(*p_str_v))
120 char **ps;
121 gboolean moving = FALSE;
123 for (ps = *p_str_v; *ps; ++ps)
125 if (moving)
127 *(ps - 1) = *ps;
128 *ps = NULL;
130 else if (!strcmp(*ps, "ROX"))
132 g_free(*ps);
133 *ps = NULL;
134 moving = TRUE;
137 if (str_v_is_empty(*p_str_v))
139 g_strfreev(*p_str_v);
140 *p_str_v = NULL;
145 static void add_rox_to_strv(char ***p_str_v, gboolean force)
147 char **ps;
149 if (!str_v_is_empty(*p_str_v))
151 gboolean contains_rox = FALSE;
153 for (ps = *p_str_v; *ps; ++ps)
155 if (!strcmp(*ps, "ROX"))
157 contains_rox = TRUE;
158 break;
161 if (!contains_rox)
163 int l = str_v_len((char const **) *p_str_v);
165 *p_str_v = g_renew(char *, *p_str_v, l + 2);
166 (*p_str_v)[l] = g_strdup("ROX");
167 (*p_str_v)[l + 1] = NULL;
170 else if (force)
172 g_strfreev(*p_str_v);
173 *p_str_v = g_new0(char *, 2);
174 (*p_str_v)[0] = g_strdup("ROX");
179 static void show_strv(char **strv)
181 char **ps;
183 for (ps = strv; ps && *ps; ++ps)
185 g_print("%s ; ", *ps);
187 g_print("\n");
191 void xdesktop_entry_set_start_in_rox(DesktopEntry *xde, gboolean state)
193 if (state)
195 add_rox_to_strv(&xde->only_show_in, FALSE);
196 remove_rox_from_strv(&xde->not_show_in);
198 else
200 remove_rox_from_strv(&xde->only_show_in);
201 add_rox_to_strv(&xde->not_show_in, TRUE);
203 update_kf_from_show_ins(xde);
206 void xdesktop_entry_save(DesktopEntry *xde)
208 char *buffer = NULL;
209 gsize buflen = 0;
210 GError *err = NULL;
212 if (!xde->savable)
214 g_free(xde->pathname);
215 xde->pathname = g_build_filename(g_get_user_config_dir(),
216 xde->basename, NULL);
217 xde->savable = TRUE;
219 buffer = g_key_file_to_data(xde->kf, &buflen, &err);
220 if (!buffer || err)
222 message_dialog(NULL, GTK_MESSAGE_ERROR,
223 _("Unable to prepare Desktop Entry for saving to '%s': %s"),
224 xde->pathname, err ? err->message : _("unknown reason"));
225 g_error_free(err);
226 g_free(buffer);
227 return;
229 if (!g_file_set_contents(xde->pathname, buffer, buflen, &err))
231 message_dialog(NULL, GTK_MESSAGE_ERROR,
232 _("Unable to save '%s': %s"),
233 xde->pathname, err ? err->message : _("unknown reason"));
234 g_error_free(err);
235 g_free(buffer);
236 return;
240 inline static char *add_desktop_extension(const char *basename)
242 return g_strjoin(".", basename, "desktop", NULL);
245 /* basename doesn't include .desktop extension */
246 static char *xdg_autostart_pathname(const char *basename)
248 char *leafname = add_desktop_extension(basename);
249 char *pathname = g_build_filename(g_get_user_config_dir(), "autostart",
250 leafname, NULL);
252 g_free(leafname);
253 return pathname;
256 /* This has side-effect of creating file if it doesn't clash,
257 * so it must be overwritten or deleted */
258 static int filename_clash(const char *basename)
260 static char *dir = NULL;
261 char *pathname;
262 const char * const *dirs;
263 const char * const *d;
264 int result = 0;
266 dirs = g_get_system_config_dirs();
267 for (d = dirs; *d; ++d)
269 char *leafname = add_desktop_extension(basename);
271 pathname = g_build_filename(*d, "autostart", leafname, NULL);
272 g_free(leafname);
273 if (g_file_test(pathname, G_FILE_TEST_EXISTS))
274 result = -1;
275 g_free(pathname);
276 if (result == -1)
277 return -1;
279 if (!dir)
281 dir = g_build_filename(g_get_user_config_dir(), "autostart", NULL);
282 if (g_mkdir_with_parents(dir, 0755) == -1)
284 message_dialog(NULL, GTK_MESSAGE_ERROR,
285 _("Unable to create '%s' directory"), dir);
288 if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
289 return -2;
290 pathname = xdg_autostart_pathname(basename);
291 result = open(pathname, O_WRONLY| O_EXCL|O_CREAT, 0644);
292 if (result != -1)
293 close(result);
294 g_free(pathname);
295 return result;
298 static char *add_number_ext(const char *basename)
300 int l = strlen(basename);
301 int n;
302 int number = 1;
304 for (n = l - 1; n >= 0 && isdigit(basename[n]); --n);
305 /* This will also accept a trailing - with no subsequent digits,
306 * but that's OK */
307 if (basename[n] == '-')
309 if (n != l - 1)
311 number = atoi(basename + n + 1) + 1;
313 l = n;
315 return g_strdup_printf("%.*s-%d", l, basename, number);
318 void xdesktop_entry_filenames_from_name(DesktopEntry *de, const char *leafname)
320 int n;
321 char *basename = NULL;
322 int fd;
324 if (leafname)
326 char *sep;
328 basename = g_strdup(leafname);
329 sep = strrchr(basename, '.');
330 if (sep && sep[1] && !strcmp(sep + 1, "desktop"))
331 *sep = 0;
333 else
335 basename = g_filename_from_utf8(de->name, -1, NULL, NULL, NULL);
337 if (!basename || !basename[0])
339 if (basename)
340 g_free(basename);
341 basename = g_strdup(_("rox-session-autostart"));
343 else
345 for (n = 0; basename[n]; ++n)
347 if (isspace(basename[n]))
348 basename[n] = '-';
349 else
350 basename[n] = tolower(basename[n]);
353 while ((fd = filename_clash(basename)) == -1)
355 char *old = basename;
357 old = basename;
358 basename = add_number_ext(basename);
359 g_free(old);
361 g_free(de->basename);
362 de->basename = add_desktop_extension(basename);
363 g_free(de->pathname);
364 de->pathname = xdg_autostart_pathname(basename);
365 g_free(basename);
366 de->savable = TRUE;