add --install and --archive
[dana/obconf.git] / src / theme.c
1 #include "theme.h"
2 #include "main.h"
3 #include "gettext.h"
4
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <zlib.h>
12 #include <libtar.h>
13
14 static gzFile gzf = NULL;
15
16 #define gtk_msg(type, args...) \
17 {                                                                        \
18     GtkWidget *msgw;                                                     \
19     msgw = gtk_message_dialog_new(GTK_WINDOW(mainwin),                   \
20                                   GTK_DIALOG_DESTROY_WITH_PARENT |       \
21                                   GTK_DIALOG_MODAL,                      \
22                                   type,                                  \
23                                   GTK_BUTTONS_OK,                        \
24                                   args);                                 \
25     gtk_dialog_run(GTK_DIALOG(msgw));                                    \
26     gtk_widget_destroy(msgw);                                            \
27 }
28
29 static int gzopen_frontend(const char *path, int oflags, int mode);
30 static int gzclose_frontend(int nothing);
31 static ssize_t gzread_frontend(int nothing, void *buf, size_t s);
32 static ssize_t gzwrite_frontend(int nothing, const void *buf, size_t s);
33 static gchar *get_theme_dir();
34 static gboolean change_dir(const gchar *dir);
35 static gboolean install_theme_to(gchar *theme, gchar *file, gchar *to);
36 static gchar* name_from_file(const gchar *path);
37 static gchar* name_from_dir(const gchar *dir);
38 static gboolean create_theme_archive(gchar *dir, gchar *name, gchar *to);
39
40 tartype_t funcs = {
41     (openfunc_t) gzopen_frontend,
42     (closefunc_t) gzclose_frontend,
43     (readfunc_t) gzread_frontend,
44     (writefunc_t) gzwrite_frontend
45 };
46
47 gchar* theme_install(gchar *path)
48 {
49     gchar *dest;
50     gchar *name;
51
52     if (!(dest = get_theme_dir()))
53         return NULL;
54
55     if (!(name = name_from_file(path)))
56         return NULL;
57
58     if (install_theme_to(name, path, dest)) {
59         gtk_msg(GTK_MESSAGE_INFO, _("\"%s\" was installed to %s"), name, dest);
60     } else {
61         g_free(name);
62         name = NULL;
63     }
64
65     g_free(dest);
66
67     return name;
68 }
69
70 void theme_archive(gchar *path)
71 {
72     gchar *name;
73     gchar *dest;
74
75     if (!(name = name_from_dir(path)))
76         return;
77
78     {
79         gchar *file;
80         file = g_strdup_printf("%s.obt", name);
81         dest = g_build_path(G_DIR_SEPARATOR_S,
82                             g_get_current_dir(), file, NULL);
83         g_free(file);
84     }
85
86     if (create_theme_archive(path, name, dest))
87         gtk_msg(GTK_MESSAGE_INFO, _("\"%s\" was successfully created"),
88                 dest);
89
90     g_free(dest);
91     g_free(name);
92 }
93
94 static gboolean create_theme_archive(gchar *dir, gchar *name, gchar *to)
95 {
96     TAR *t;
97     gint r;
98
99     if (tar_open(&t, to, &funcs, O_WRONLY | O_CREAT, 0666, TAR_GNU) == -1) {
100         gtk_msg(GTK_MESSAGE_ERROR,
101                 _("Unable to create the file \"%s\": %s"),
102                 to, strerror(errno));
103         return;
104     }
105
106     r = tar_append_tree(t, dir, name);
107     tar_close(t);
108
109     if (r != 0) {
110         gtk_msg(GTK_MESSAGE_ERROR,
111                 _("Unable to create the theme archive \"%s\": %s"),
112                 to, strerror(errno));
113     }
114
115     return r == 0;
116 }
117
118 static gchar *get_theme_dir()
119 {
120     gchar *dir;
121     gint r;
122
123     dir = g_build_path(G_DIR_SEPARATOR_S, g_get_home_dir(), ".themes", NULL);
124     r = mkdir(dir, 0777);
125     if (r == -1 && errno != EEXIST) {
126         gtk_msg(GTK_MESSAGE_ERROR,
127                 _("Unable to create directory \"%s\": %s"),
128                 dir, strerror(errno));
129         g_free(dir);
130         dir = NULL;
131     }
132
133     return dir;
134 }
135
136 static gchar* name_from_dir(const gchar *dir)
137 {
138     gchar *rc;
139     struct stat st;
140     gboolean r;
141
142     rc = g_build_path(G_DIR_SEPARATOR_S, dir, "openbox-3", "themerc", NULL);
143
144     r = (stat(rc, &st) == 0 && S_ISREG(st.st_mode));
145     g_free(rc);
146
147     if (!r) {
148         gtk_msg(GTK_MESSAGE_ERROR,
149                 _("\"%s\" does not appear to be a valid Openbox theme directory"),
150                 dir);
151         return NULL;
152     }
153     return g_path_get_basename(dir);
154 }
155
156 static gchar* name_from_file(const gchar *path)
157 {
158     /* decipher the theme name from the file name */
159     gchar *fname = g_path_get_basename(path);
160     gint len = strlen(fname);
161     gchar *name = NULL;
162
163     if (len > 4 &&
164         (fname[len-4] == '.' && fname[len-3] == 'o' &&
165          fname[len-2] == 'b' && fname[len-1] == 't'))
166     {
167         fname[len-4] = '\0';
168         name = g_strdup(fname);
169         fname[len-4] = '.';
170     }
171
172     if (name == NULL)
173         gtk_msg(GTK_MESSAGE_ERROR,
174                 _("Unable to determine the theme's name from \"%s\".  File name should be ThemeName.obt."), fname);
175
176     return name;
177 }
178
179 static gboolean change_dir(const gchar *dir)
180 {
181     if (chdir(dir) == -1) {
182         gtk_msg(GTK_MESSAGE_ERROR, _("Unable to move to directory \"%s\": %s"),
183                 dir, strerror(errno));
184         return FALSE;
185     }
186     return TRUE;
187 }
188
189 static gboolean install_theme_to(gchar *theme, gchar *file, gchar *to)
190 {
191     TAR *t;
192     gchar *glob;
193     gint r;
194     gchar *curdir;
195
196     if (tar_open(&t, file, &funcs, O_RDONLY, 0666, TAR_GNU) == -1) {
197         gtk_msg(GTK_MESSAGE_ERROR,
198                 _("Unable to open the file \"%s\": %s"),
199                 file, strerror(errno));
200         return FALSE;
201     }
202
203     curdir = g_get_current_dir();
204     if (!change_dir(to)) {
205         g_free(curdir);
206         tar_close(t);
207         return FALSE;
208     }
209
210     glob = g_strdup_printf("%s/openbox-3/*", theme);
211     r = tar_extract_glob(t, glob, ".");
212     g_free(glob);
213
214     change_dir(curdir);
215
216     g_free(curdir);
217     tar_close(t);
218
219     if (r != 0)
220         gtk_msg(GTK_MESSAGE_ERROR,
221                 _("Unable to extract the file \"%s\".\nPlease ensure that \"%s\" is writable and that the file is a valid Openbox theme archive"),
222                 file, strerror(errno));
223
224     return r == 0;
225 }
226
227 static int gzopen_frontend(const char *path, int oflags, int mode)
228 {
229     int fd;
230     const char *gzflags;
231
232     if ((oflags & O_ACCMODE) == O_RDONLY)
233         gzflags = "rb";
234     else if ((oflags & O_ACCMODE) == O_WRONLY)
235         gzflags = "wb";
236     else
237         g_assert_not_reached();
238
239     if ((fd = open(path, oflags, mode)) < 0) return -1;
240     if (!(gzf = gzdopen(fd, gzflags))) return -1;
241     return 1;
242 }
243
244 static int gzclose_frontend(int nothing)
245 {
246     g_return_val_if_fail(gzf != NULL, 0);
247     return gzclose(gzf);
248 }
249
250 static ssize_t gzread_frontend(int nothing, void *buf, size_t s)
251 {
252     return gzread(gzf, buf, s);
253 }
254
255 static ssize_t gzwrite_frontend(int nothing, const void *buf, size_t s)
256 {
257     return gzwrite(gzf, buf, s);
258 }
259