1 /**
2  * @file    utils.c
3  * @brief
4  *
5  * Copyright (C) 2009 Gummi Developers
6  * All Rights reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following
15  * conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  * OTHER DEALINGS IN THE SOFTWARE.
28  */
29 
30 
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <glib.h>
36 #include <glib/gstdio.h>
37 #include <glib/gprintf.h>
38 #include <gtk/gtk.h>
39 #include <unistd.h>
40 
41 #ifdef WIN32
42     #include <windows.h>
43 #else
44     #include <sys/types.h>
45     #include <sys/wait.h>
46 #endif
47 
48 #ifndef WEXITSTATUS
49 #   define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
50 #endif
51 
52 #include "constants.h"
53 #include "environment.h"
54 #include "utils.h"
55 
56 #ifdef WIN32
57     static gchar *slogmsg_info = "[Info] ";
58     static gchar *slogmsg_thread = "[Thread]";
59     static gchar *slogmsg_debug = "[Debug] ";
60     static gchar *slogmsg_fatal = "[Fatal] ";
61     static gchar *slogmsg_error = "[Error] ";
62     static gchar *slogmsg_warning = "[Warning] ";
63 #else
64     static gchar *slogmsg_info = "\e[1;34m[Info]\e[0m ";
65     static gchar *slogmsg_thread = "\e[1;31m[Thread]\e[0m";
66     static gchar *slogmsg_debug = "\e[1;32m[Debug]\e[0m ";
67     static gchar *slogmsg_fatal = "\e[1;37;41m[Fatal]\e[0m ";
68     static gchar *slogmsg_error = "\e[1;31m[Error]\e[0m ";
69     static gchar *slogmsg_warning = "\e[1;33m[Warning]\e[0m ";
70 #endif
71 
72 
73 static gint slog_debug = 0;
74 static GtkWindow* parent = 0;
75 GThread* main_thread = 0;
76 extern pid_t typesetter_pid;
77 
78 
slog_init(gint debug)79 void slog_init (gint debug) {
80     slog_debug = debug;
81     main_thread = g_thread_self ();
82 }
83 
in_debug_mode()84 gboolean in_debug_mode() {
85     return slog_debug;
86 }
87 
slog_set_gui_parent(GtkWindow * p)88 void slog_set_gui_parent (GtkWindow* p) {
89     parent = p;
90 }
91 
slog(gint level,const gchar * fmt,...)92 void slog (gint level, const gchar *fmt, ...) {
93     gchar message[BUFSIZ];
94     va_list vap;
95 
96     if (L_IS_TYPE (level, L_DEBUG) && !slog_debug) return;
97 
98     if (g_thread_self () != main_thread)
99         g_fprintf (stderr, "%s", slogmsg_thread);
100 
101     if (L_IS_TYPE (level, L_DEBUG))
102         g_fprintf (stderr, "%s", slogmsg_debug);
103     else if (L_IS_TYPE (level, L_FATAL) || L_IS_TYPE (level, L_G_FATAL))
104         g_fprintf (stderr, "%s", slogmsg_fatal);
105     else if (L_IS_TYPE (level, L_ERROR) || L_IS_TYPE (level, L_G_ERROR))
106         g_fprintf (stderr, "%s", slogmsg_error);
107     else if (L_IS_TYPE (level, L_WARNING))
108         g_fprintf (stderr, "%s", slogmsg_warning);
109     else
110         g_fprintf (stderr, "%s", slogmsg_info);
111 
112     va_start (vap, fmt);
113     vsnprintf (message, BUFSIZ, fmt, vap);
114     va_end (vap);
115     fprintf (stderr, "%s", message);
116 
117     if (L_IS_GUI (level)) {
118         GtkWidget* dialog;
119 
120         dialog = gtk_message_dialog_new (parent,
121                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
122                 L_IS_TYPE (level,L_G_INFO)? GTK_MESSAGE_INFO: GTK_MESSAGE_ERROR,
123                 GTK_BUTTONS_OK,
124                 "%s", message);
125 
126         if (L_IS_TYPE (level, L_G_ERROR))
127             gtk_window_set_title (GTK_WINDOW (dialog), "Error!");
128         else if (L_IS_TYPE (level, L_G_FATAL))
129             gtk_window_set_title (GTK_WINDOW (dialog), "Fatal Error!");
130         else if (L_IS_TYPE (level, L_G_INFO))
131             gtk_window_set_title (GTK_WINDOW (dialog), "Info");
132 
133         gtk_dialog_run (GTK_DIALOG (dialog));
134         gtk_widget_destroy (dialog);
135     }
136 
137     if (!L_IS_TYPE (level, L_INFO) &&
138         !L_IS_TYPE (level, L_DEBUG) &&
139         !L_IS_TYPE (level, L_ERROR) &&
140         !L_IS_TYPE (level, L_G_INFO) &&
141         !L_IS_TYPE (level, L_G_ERROR))
142         exit (1);
143 }
144 
utils_save_reload_dialog(const gchar * message)145 gint utils_save_reload_dialog (const gchar* message) {
146     GtkWidget* dialog;
147     gint ret = 0;
148 
149     g_return_val_if_fail (message != NULL, 0);
150 
151     dialog = gtk_message_dialog_new (parent,
152                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
153                  GTK_MESSAGE_QUESTION,
154                  GTK_BUTTONS_NONE,
155                  "%s", message);
156     gtk_dialog_add_buttons(GTK_DIALOG (dialog), "Reload", GTK_RESPONSE_YES, "Save", GTK_RESPONSE_NO, NULL);
157 
158     gtk_window_set_title (GTK_WINDOW (dialog), _("Confirmation"));
159     ret = gtk_dialog_run (GTK_DIALOG (dialog));
160     gtk_widget_destroy (dialog);
161 
162     return ret;
163 }
164 
utils_yes_no_dialog(const gchar * message)165 gint utils_yes_no_dialog (const gchar* message) {
166     GtkWidget* dialog;
167     gint ret = 0;
168 
169     g_return_val_if_fail (message != NULL, 0);
170 
171     dialog = gtk_message_dialog_new (parent,
172                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
173                  GTK_MESSAGE_QUESTION,
174                  GTK_BUTTONS_YES_NO,
175                  "%s", message);
176 
177     gtk_window_set_title (GTK_WINDOW (dialog), _("Confirmation"));
178     ret = gtk_dialog_run (GTK_DIALOG (dialog));
179     gtk_widget_destroy (dialog);
180 
181     return ret;
182 }
183 
utils_path_exists(const gchar * path)184 gboolean utils_path_exists (const gchar* path) {
185     if (NULL == path) return FALSE;
186     gboolean result = FALSE;
187     GFile* file = g_file_new_for_path (path);
188     result = g_file_query_exists (file, NULL);
189     g_object_unref (file);
190     return result;
191 }
192 
utils_uri_path_exists(const gchar * uri)193 gboolean utils_uri_path_exists (const gchar* uri) {
194     gboolean result = FALSE;
195 
196     gchar *filepath = g_filename_from_uri(uri, NULL, NULL);
197     result = utils_path_exists(filepath);
198     g_free(filepath);
199     return(result);
200 }
201 
utils_set_file_contents(const gchar * filename,const gchar * text,gssize length)202 gboolean utils_set_file_contents (const gchar *filename, const gchar *text,
203                                   gssize length) {
204     /* g_file_set_contents may not work correctly on Windows. See the
205      * API documentation of this function for details. Should Gummi
206      * be affected, we might have to implement an alternative */
207         GError* error = NULL;
208         if (!g_file_set_contents(filename, text, length, &error)) {
209             slog (L_ERROR, "%s\n", error->message);
210             g_error_free(error);
211             return FALSE;
212         }
213         return TRUE;
214 }
215 
utils_copy_file(const gchar * source,const gchar * dest,GError ** err)216 gboolean utils_copy_file (const gchar* source, const gchar* dest, GError** err) {
217     gchar* contents;
218     gsize length;
219 
220     g_return_val_if_fail (source != NULL, FALSE);
221     g_return_val_if_fail (dest != NULL, FALSE);
222     g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
223 
224     if (!g_file_get_contents (source, &contents, &length, err))
225         return FALSE;
226 
227     if (!g_file_set_contents (dest, contents, length, err))
228         return FALSE;
229 
230     g_free (contents);
231 
232     return TRUE;
233 }
234 
utils_popen_r(const gchar * cmd,const gchar * chdir)235 Tuple2 utils_popen_r (const gchar* cmd, const gchar* chdir) {
236     gchar buf[BUFSIZ];
237     int pout = 0;
238     gchar* ret = NULL;
239     gchar* rot = NULL;
240     glong len = 0;
241     gint status = 0;
242     int n_args = 0;
243     gchar** args = NULL;
244     GError* error = NULL;
245 
246     g_assert (cmd != NULL);
247 
248     /* XXX: Set process pid, ugly... */
249     if (!g_shell_parse_argv(cmd, &n_args, &args, &error)) {
250         slog(L_G_FATAL, "%s", error->message);
251         /* Not reached */
252     }
253 
254     if (!g_spawn_async_with_pipes (chdir, args, NULL,
255                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
256                 NULL, NULL, &typesetter_pid, NULL, &pout, NULL, &error)) {
257         slog(L_G_FATAL, "%s", error->message);
258         /* Not reached */
259     }
260 
261     // TODO: replace with GIOChannel implementation:
262     while ((len = read (pout, buf, BUFSIZ)) > 0) {
263         buf[len - (len == BUFSIZ)] = 0;
264         rot = g_strdup (ret);
265         g_free (ret);
266         if (ret)
267             ret = g_strconcat (rot, buf, NULL);
268         else
269             ret = g_strdup (buf);
270         g_free (rot);
271     }
272 
273     // close the file descriptor:
274     close(pout);
275 
276     #ifdef WIN32 // TODO: check this
277         status = WaitForSingleObject(typesetter_pid, INFINITE);
278     #else
279         waitpid(typesetter_pid, &status, 0);
280     #endif
281 
282     // See bug 446:
283     if (ret) {
284         if (!g_utf8_validate (ret, -1, NULL)) {
285             ret = g_convert_with_fallback (ret, -1, "UTF-8",
286                     "ISO-8859-1", NULL, NULL, NULL, NULL);
287         }
288     }
289 
290     return (Tuple2){NULL, (gpointer)(glong)status, (gpointer)ret};
291 }
292 
utils_path_to_relative(const gchar * root,const gchar * target)293 gchar* utils_path_to_relative (const gchar* root, const gchar* target) {
294     gchar* tstr = NULL;
295     if ( (root != NULL) && (0 == strncmp (target, root, strlen (root))))
296         tstr = g_strdup (target + strlen (root) + 1);
297     else
298         tstr = g_strdup (target);
299     return tstr;
300 }
301 
utils_get_tmp_tmp_dir(void)302 gchar* utils_get_tmp_tmp_dir (void) {
303 	/* brb, gonna go punch a wall */
304     gchar *tmp_tmp = g_build_path
305 						(C_DIRSEP, g_get_home_dir(), "gtmp", NULL);
306     g_mkdir_with_parents (tmp_tmp, DIR_PERMS);
307 
308     return tmp_tmp;
309 }
310 
311 
utils_glist_is_member(GList * list,gchar * item)312 gboolean utils_glist_is_member (GList* list, gchar* item) {
313     int nrofitems = g_list_length (list);
314     int i;
315 
316     for (i=0;i<nrofitems;i++) {
317         if (STR_EQU (item, g_list_nth_data (list,i))) {
318             return TRUE;
319         }
320     }
321     return FALSE;
322 }
323 
utils_subinstr(const gchar * substr,const gchar * target,gboolean case_insens)324 gboolean utils_subinstr (const gchar* substr, const gchar* target,
325         gboolean case_insens) {
326     if (target != NULL && substr != NULL) {
327         if (case_insens) {
328             gchar* ntarget = g_utf8_strup(target, -1);
329             gchar* nsubstr = g_utf8_strup(substr, -1);
330             gboolean result = g_strstr_len(ntarget, -1, nsubstr) != NULL;
331             g_free(ntarget);
332             g_free(nsubstr);
333             return result;
334         }
335         else {
336             return g_strstr_len(target, -1, substr) != NULL;
337         }
338     }
339     return FALSE;
340 }
341 
g_substr(gchar * src,gint start,gint end)342 gchar* g_substr(gchar* src, gint start, gint end) {
343     gint len = end - start + 1;
344     char* dst = g_malloc(len * sizeof(gchar));
345     memset(dst, 0, len);
346     return strncpy(dst, &src[start], end - start);
347 }
348 
slist_find(slist * head,const gchar * term,gboolean n,gboolean create)349 slist* slist_find (slist* head, const gchar* term, gboolean n, gboolean create) {
350     slist* current = head;
351     slist* prev = 0;
352 
353     while (current) {
354         if (n) {
355             if (0 == strncmp (current->first, term, strlen (term)))
356                 return current;
357         } else {
358             if (STR_EQU (current->first, term))
359                 return current;
360         }
361         prev = current;
362         current = current->next;
363     }
364     if (create) {
365         slog (L_WARNING, "can't find `%s', creating new field for it...\n",
366                 term);
367         prev->next = g_new0 (slist, 1);
368         current = prev->next;
369         current->first = g_strdup (term);
370         current->second = g_strdup ("");
371     } else
372         current = NULL;
373     return current;
374 }
375 
slist_append(slist * head,slist * node)376 slist* slist_append (slist* head, slist* node) {
377     slist* current = head;
378     slist* prev = NULL;
379 
380     while (current) {
381         prev = current;
382         current = current->next;
383     }
384     prev->next = node;
385     return head;
386 }
387 
slist_remove(slist * head,slist * node)388 slist* slist_remove (slist* head, slist* node) {
389     slist* current = head;
390     slist* prev = NULL;
391 
392     while (current) {
393         if (current == node) break;
394         prev = current;
395         current = current->next;
396     }
397     if (current) {
398         if (current == head)
399             head = head->next;
400         else
401             prev->next = current->next;
402     }
403     return head;
404 }
405