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