Merge pull request #418 from b4n/spell-windows-windows
[geany-mirror.git] / src / utils.c
blob6f71ec78d1ca7f680739ec2e07e0e77759733fbb
1 /*
2 * utils.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * General utility functions, non-GTK related.
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "utils.h"
32 #include "app.h"
33 #include "dialogs.h"
34 #include "document.h"
35 #include "prefs.h"
36 #include "sciwrappers.h"
37 #include "support.h"
38 #include "templates.h"
39 #include "ui_utils.h"
40 #include "win32.h"
41 #include "osx.h"
43 #include <stdlib.h>
44 #include <ctype.h>
45 #include <math.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include <errno.h>
49 #include <stdarg.h>
51 #ifdef HAVE_SYS_STAT_H
52 # include <sys/stat.h>
53 #endif
54 #ifdef HAVE_SYS_TYPES_H
55 # include <sys/types.h>
56 #endif
58 #include <glib/gstdio.h>
59 #include <gio/gio.h>
62 /**
63 * Tries to open the given URI in a browser.
64 * On Windows, the system's default browser is opened.
65 * On non-Windows systems, the browser command set in the preferences dialog is used. In case
66 * that fails or it is unset, the user is asked to correct or fill it.
68 * @param uri The URI to open in the web browser.
70 * @since 0.16
71 **/
72 void utils_open_browser(const gchar *uri)
74 #ifdef G_OS_WIN32
75 g_return_if_fail(uri != NULL);
76 win32_open_browser(uri);
77 #else
78 gboolean again = TRUE;
80 g_return_if_fail(uri != NULL);
82 while (again)
84 gchar *cmdline = g_strconcat(tool_prefs.browser_cmd, " \"", uri, "\"", NULL);
86 if (g_spawn_command_line_async(cmdline, NULL))
87 again = FALSE;
88 else
90 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
91 _("Failed to spawn the configured browser command. "
92 "Please correct it or enter another one."),
93 tool_prefs.browser_cmd);
95 if (new_cmd == NULL) /* user canceled */
96 again = FALSE;
97 else
98 SETPTR(tool_prefs.browser_cmd, new_cmd);
100 g_free(cmdline);
102 #endif
106 /* taken from anjuta, to determine the EOL mode of the file */
107 gint utils_get_line_endings(const gchar* buffer, gsize size)
109 gsize i;
110 guint cr, lf, crlf, max_mode;
111 gint mode;
113 cr = lf = crlf = 0;
115 for (i = 0; i < size ; i++)
117 if (buffer[i] == 0x0a)
119 /* LF */
120 lf++;
122 else if (buffer[i] == 0x0d)
124 if (i >= (size - 1))
126 /* Last char, CR */
127 cr++;
129 else
131 if (buffer[i + 1] != 0x0a)
133 /* CR */
134 cr++;
136 else
138 /* CRLF */
139 crlf++;
141 i++;
146 /* Vote for the maximum */
147 mode = SC_EOL_LF;
148 max_mode = lf;
149 if (crlf > max_mode)
151 mode = SC_EOL_CRLF;
152 max_mode = crlf;
154 if (cr > max_mode)
156 mode = SC_EOL_CR;
157 max_mode = cr;
160 return mode;
164 gboolean utils_isbrace(gchar c, gboolean include_angles)
166 switch (c)
168 case '<':
169 case '>':
170 return include_angles;
172 case '(':
173 case ')':
174 case '{':
175 case '}':
176 case '[':
177 case ']': return TRUE;
178 default: return FALSE;
183 gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
185 switch (c)
187 case '<':
188 return include_angles;
190 case '(':
191 case '{':
192 case '[': return TRUE;
193 default: return FALSE;
199 * Writes @a text into a file named @a filename.
200 * If the file doesn't exist, it will be created.
201 * If it already exists, it will be overwritten.
203 * @warning You should use @c g_file_set_contents() instead if you don't need
204 * file permissions and other metadata to be preserved, as that always handles
205 * disk exhaustion safely.
207 * @param filename The filename of the file to write, in locale encoding.
208 * @param text The text to write into the file.
210 * @return 0 if the file was successfully written, otherwise the @c errno of the
211 * failed operation is returned.
213 gint utils_write_file(const gchar *filename, const gchar *text)
215 g_return_val_if_fail(filename != NULL, ENOENT);
216 g_return_val_if_fail(text != NULL, EINVAL);
218 if (file_prefs.use_safe_file_saving)
220 GError *error = NULL;
221 if (! g_file_set_contents(filename, text, -1, &error))
223 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
224 g_error_free(error);
225 return EIO;
228 else
230 FILE *fp;
231 gsize bytes_written, len;
232 gboolean fail = FALSE;
234 if (filename == NULL)
235 return ENOENT;
237 len = strlen(text);
238 errno = 0;
239 fp = g_fopen(filename, "w");
240 if (fp == NULL)
241 fail = TRUE;
242 else
244 bytes_written = fwrite(text, sizeof(gchar), len, fp);
246 if (len != bytes_written)
248 fail = TRUE;
249 geany_debug(
250 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
251 bytes_written, len, filename);
253 if (fclose(fp) != 0)
254 fail = TRUE;
256 if (fail)
258 geany_debug("utils_write_file(): could not write to file %s (%s)",
259 filename, g_strerror(errno));
260 return FALLBACK(errno, EIO);
263 return 0;
267 /** Searches backward through @a size bytes looking for a '<'.
268 * @param sel .
269 * @param size .
270 * @return The tag name (newly allocated) or @c NULL if no opening tag was found.
272 gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
274 const gchar *cur, *begin;
275 gsize len;
277 cur = utils_find_open_xml_tag_pos(sel, size);
278 if (cur == NULL)
279 return NULL;
281 cur++; /* skip the bracket */
282 begin = cur;
283 while (strchr(":_-.", *cur) || isalnum(*cur))
284 cur++;
286 len = (gsize)(cur - begin);
287 return len ? g_strndup(begin, len) : NULL;
291 /** Searches backward through @a size bytes looking for a '<'.
292 * @param sel .
293 * @param size .
294 * @return pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
296 const gchar *utils_find_open_xml_tag_pos(const gchar sel[], gint size)
298 /* stolen from anjuta and modified */
299 const gchar *begin, *cur;
301 if (G_UNLIKELY(size < 3))
302 { /* Smallest tag is "<p>" which is 3 characters */
303 return NULL;
305 begin = &sel[0];
306 cur = &sel[size - 1];
308 /* Skip to the character before the closing brace */
309 while (cur > begin)
311 if (*cur == '>')
312 break;
313 --cur;
315 --cur;
316 /* skip whitespace */
317 while (cur > begin && isspace(*cur))
318 cur--;
319 if (*cur == '/')
320 return NULL; /* we found a short tag which doesn't need to be closed */
321 while (cur > begin)
323 if (*cur == '<')
324 break;
325 /* exit immediately if such non-valid XML/HTML is detected, e.g. "<script>if a >" */
326 else if (*cur == '>')
327 break;
328 --cur;
331 /* if the found tag is an opening, not a closing tag or empty <> */
332 if (*cur == '<' && *(cur + 1) != '/' && *(cur + 1) != '>')
333 return cur;
335 return NULL;
339 /* Returns true if tag_name is a self-closing tag */
340 gboolean utils_is_short_html_tag(const gchar *tag_name)
342 const gchar names[][20] = {
343 "area",
344 "base",
345 "basefont", /* < or not < */
346 "br",
347 "col",
348 "command",
349 "embed",
350 "frame",
351 "hr",
352 "img",
353 "input",
354 "keygen",
355 "link",
356 "meta",
357 "param",
358 "source",
359 "track",
360 "wbr"
363 if (tag_name)
365 if (bsearch(tag_name, names, G_N_ELEMENTS(names), 20,
366 (GCompareFunc)g_ascii_strcasecmp))
367 return TRUE;
369 return FALSE;
373 const gchar *utils_get_eol_name(gint eol_mode)
375 switch (eol_mode)
377 case SC_EOL_CRLF: return _("Windows (CRLF)"); break;
378 case SC_EOL_CR: return _("Classic Mac (CR)"); break;
379 default: return _("Unix (LF)"); break;
384 const gchar *utils_get_eol_char(gint eol_mode)
386 switch (eol_mode)
388 case SC_EOL_CRLF: return "\r\n"; break;
389 case SC_EOL_CR: return "\r"; break;
390 default: return "\n"; break;
395 /* Converts line endings to @a target_eol_mode. */
396 void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
398 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
400 /* first convert data to LF only */
401 utils_string_replace_all(string, "\r\n", "\n");
402 utils_string_replace_all(string, "\r", "\n");
404 if (target_eol_mode == SC_EOL_LF)
405 return;
407 /* now convert to desired line endings */
408 utils_string_replace_all(string, "\n", eol_str);
412 gboolean utils_atob(const gchar *str)
414 if (G_UNLIKELY(str == NULL))
415 return FALSE;
416 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
417 return TRUE;
418 return FALSE;
422 /* NULL-safe version of g_path_is_absolute(). */
423 gboolean utils_is_absolute_path(const gchar *path)
425 if (G_UNLIKELY(EMPTY(path)))
426 return FALSE;
428 return g_path_is_absolute(path);
432 /* Skips root if path is absolute, do nothing otherwise.
433 * This is a relative-safe version of g_path_skip_root().
435 const gchar *utils_path_skip_root(const gchar *path)
437 const gchar *path_relative;
439 path_relative = g_path_skip_root(path);
441 return (path_relative != NULL) ? path_relative : path;
445 gdouble utils_scale_round(gdouble val, gdouble factor)
447 /*val = floor(val * factor + 0.5);*/
448 val = floor(val);
449 val = MAX(val, 0);
450 val = MIN(val, factor);
452 return val;
456 /* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
457 * returns NULL on charset conversion failure */
458 static gchar *utf8_strdown(const gchar *str)
460 gchar *down;
462 if (g_utf8_validate(str, -1, NULL))
463 down = g_utf8_strdown(str, -1);
464 else
466 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
467 if (down)
468 SETPTR(down, g_utf8_strdown(down, -1));
471 return down;
476 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
477 * It converts both strings into lowercase using g_utf8_strdown() and then compare
478 * both strings using strcmp().
479 * This is not completely accurate regarding locale-specific case sorting rules
480 * but seems to be a good compromise between correctness and performance.
482 * The input strings should be in UTF-8 or locale encoding.
484 * @param s1 Pointer to first string or @c NULL.
485 * @param s2 Pointer to second string or @c NULL.
487 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
488 * to be less than, to match, or to be greater than @a s2.
490 * @since 0.16
492 gint utils_str_casecmp(const gchar *s1, const gchar *s2)
494 gchar *tmp1, *tmp2;
495 gint result;
497 g_return_val_if_fail(s1 != NULL, 1);
498 g_return_val_if_fail(s2 != NULL, -1);
500 /* ensure strings are UTF-8 and lowercase */
501 tmp1 = utf8_strdown(s1);
502 if (! tmp1)
503 return 1;
504 tmp2 = utf8_strdown(s2);
505 if (! tmp2)
507 g_free(tmp1);
508 return -1;
511 /* compare */
512 result = strcmp(tmp1, tmp2);
514 g_free(tmp1);
515 g_free(tmp2);
516 return result;
521 * Truncates the input string to a given length.
522 * Characters are removed from the middle of the string, so the start and the end of string
523 * won't change.
525 * @param string Input string.
526 * @param truncate_length The length in characters of the resulting string.
528 * @return A copy of @a string which is truncated to @a truncate_length characters,
529 * should be freed when no longer needed.
531 * @since 0.17
533 /* This following function is taken from Gedit. */
534 gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
536 GString *truncated;
537 guint length;
538 guint n_chars;
539 guint num_left_chars;
540 guint right_offset;
541 guint delimiter_length;
542 const gchar *delimiter = "\342\200\246";
544 g_return_val_if_fail(string != NULL, NULL);
546 length = strlen(string);
548 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
550 /* It doesnt make sense to truncate strings to less than the size of the delimiter plus 2
551 * characters (one on each side) */
552 delimiter_length = g_utf8_strlen(delimiter, -1);
553 if (truncate_length < (delimiter_length + 2))
554 return g_strdup(string);
556 n_chars = g_utf8_strlen(string, length);
558 /* Make sure the string is not already small enough. */
559 if (n_chars <= truncate_length)
560 return g_strdup (string);
562 /* Find the 'middle' where the truncation will occur. */
563 num_left_chars = (truncate_length - delimiter_length) / 2;
564 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
566 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
567 g_string_append(truncated, delimiter);
568 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
570 return g_string_free(truncated, FALSE);
575 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
576 * or if @a a and @a b refer to valid strings which are equal.
578 * @param a Pointer to first string or @c NULL.
579 * @param b Pointer to second string or @c NULL.
581 * @return @c TRUE if @a a equals @a b, else @c FALSE.
583 gboolean utils_str_equal(const gchar *a, const gchar *b)
585 /* (taken from libexo from os-cillation) */
586 if (a == NULL && b == NULL) return TRUE;
587 else if (a == NULL || b == NULL) return FALSE;
589 return strcmp(a, b) == 0;
594 * Removes the extension from @a filename and return the result in a newly allocated string.
596 * @param filename The filename to operate on.
598 * @return A newly-allocated string, should be freed when no longer needed.
600 gchar *utils_remove_ext_from_filename(const gchar *filename)
602 gchar *last_dot;
603 gchar *result;
604 gsize len;
606 g_return_val_if_fail(filename != NULL, NULL);
608 last_dot = strrchr(filename, '.');
609 if (! last_dot)
610 return g_strdup(filename);
612 len = (gsize) (last_dot - filename);
613 result = g_malloc(len + 1);
614 memcpy(result, filename, len);
615 result[len] = 0;
617 return result;
621 gchar utils_brace_opposite(gchar ch)
623 switch (ch)
625 case '(': return ')';
626 case ')': return '(';
627 case '[': return ']';
628 case ']': return '[';
629 case '{': return '}';
630 case '}': return '{';
631 case '<': return '>';
632 case '>': return '<';
633 default: return '\0';
638 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
639 * Returns 0 if it can be written, otherwise it returns errno */
640 gint utils_is_file_writable(const gchar *locale_filename)
642 gchar *file;
643 gint ret;
645 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
646 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
647 /* get the file's directory to check for write permission if it doesn't yet exist */
648 file = g_path_get_dirname(locale_filename);
649 else
650 file = g_strdup(locale_filename);
652 #ifdef G_OS_WIN32
653 /* use _waccess on Windows, access() doesn't accept special characters */
654 ret = win32_check_write_permission(file);
655 #else
657 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
658 * errno only when access() explicitly returns an error */
659 if (access(file, R_OK | W_OK) != 0)
660 ret = errno;
661 else
662 ret = 0;
663 #endif
664 g_free(file);
665 return ret;
669 /* Replaces all occurrences of needle in haystack with replacement.
670 * Warning: *haystack must be a heap address; it may be freed and reassigned.
671 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
672 * than @a needle.
673 * All strings have to be NULL-terminated.
674 * See utils_string_replace_all() for details. */
675 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
677 GString *str;
679 g_return_if_fail(*haystack != NULL);
681 str = g_string_new(*haystack);
683 g_free(*haystack);
684 utils_string_replace_all(str, needle, replacement);
686 *haystack = g_string_free(str, FALSE);
690 gint utils_strpos(const gchar *haystack, const gchar *needle)
692 const gchar *sub;
694 if (! *needle)
695 return -1;
697 sub = strstr(haystack, needle);
698 if (! sub)
699 return -1;
701 return sub - haystack;
706 * Retrieves a formatted date/time string from strftime().
707 * This function should be preferred to directly calling strftime() since this function
708 * works on UTF-8 encoded strings.
710 * @param format The format string to pass to strftime(3). See the strftime(3)
711 * documentation for details, in UTF-8 encoding.
712 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
714 * @return A newly-allocated string, should be freed when no longer needed.
716 * @since 0.16
718 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
720 const struct tm *tm;
721 static gchar date[1024];
722 gchar *locale_format;
723 gsize len;
725 g_return_val_if_fail(format != NULL, NULL);
727 if (! g_utf8_validate(format, -1, NULL))
729 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
730 if (locale_format == NULL)
731 return NULL;
733 else
734 locale_format = g_strdup(format);
736 if (time_to_use != NULL)
737 tm = localtime(time_to_use);
738 else
740 time_t tp = time(NULL);
741 tm = localtime(&tp);
744 len = strftime(date, 1024, locale_format, tm);
745 g_free(locale_format);
746 if (len == 0)
747 return NULL;
749 if (! g_utf8_validate(date, len, NULL))
750 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
751 else
752 return g_strdup(date);
756 gchar *utils_get_initials(const gchar *name)
758 gint i = 1, j = 1;
759 gchar *initials = g_malloc0(5);
761 initials[0] = name[0];
762 while (name[i] != '\0' && j < 4)
764 if (name[i] == ' ' && name[i + 1] != ' ')
766 initials[j++] = name[i + 1];
768 i++;
770 return initials;
775 * Wraps g_key_file_get_integer() to add a default value argument.
777 * @param config A GKeyFile object.
778 * @param section The group name to look in for the key.
779 * @param key The key to find.
780 * @param default_value The default value which will be returned when @a section or @a key
781 * don't exist.
783 * @return The value associated with @a key as an integer, or the given default value if the value
784 * could not be retrieved.
786 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
787 const gint default_value)
789 gint tmp;
790 GError *error = NULL;
792 g_return_val_if_fail(config, default_value);
794 tmp = g_key_file_get_integer(config, section, key, &error);
795 if (error)
797 g_error_free(error);
798 return default_value;
800 return tmp;
805 * Wraps g_key_file_get_boolean() to add a default value argument.
807 * @param config A GKeyFile object.
808 * @param section The group name to look in for the key.
809 * @param key The key to find.
810 * @param default_value The default value which will be returned when @c section or @c key
811 * don't exist.
813 * @return The value associated with @a key as a boolean, or the given default value if the value
814 * could not be retrieved.
816 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
817 const gboolean default_value)
819 gboolean tmp;
820 GError *error = NULL;
822 g_return_val_if_fail(config, default_value);
824 tmp = g_key_file_get_boolean(config, section, key, &error);
825 if (error)
827 g_error_free(error);
828 return default_value;
830 return tmp;
835 * Wraps g_key_file_get_string() to add a default value argument.
837 * @param config A GKeyFile object.
838 * @param section The group name to look in for the key.
839 * @param key The key to find.
840 * @param default_value The default value which will be returned when @a section or @a key
841 * don't exist.
843 * @return A newly allocated string, either the value for @a key or a copy of the given
844 * default value if it could not be retrieved.
846 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
847 const gchar *default_value)
849 gchar *tmp;
851 g_return_val_if_fail(config, g_strdup(default_value));
853 tmp = g_key_file_get_string(config, section, key, NULL);
854 if (!tmp)
856 return g_strdup(default_value);
858 return tmp;
862 gchar *utils_get_hex_from_color(GdkColor *color)
864 g_return_val_if_fail(color != NULL, NULL);
866 return g_strdup_printf("#%02X%02X%02X",
867 (guint) (utils_scale_round(color->red / 256, 255)),
868 (guint) (utils_scale_round(color->green / 256, 255)),
869 (guint) (utils_scale_round(color->blue / 256, 255)));
873 /* Get directory from current file in the notebook.
874 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
875 * Returned string is in UTF-8 encoding */
876 gchar *utils_get_current_file_dir_utf8(void)
878 GeanyDocument *doc = document_get_current();
880 if (doc != NULL)
882 /* get current filename */
883 const gchar *cur_fname = doc->file_name;
885 if (cur_fname != NULL)
887 /* get folder part from current filename */
888 return g_path_get_dirname(cur_fname); /* returns "." if no path */
892 return NULL; /* no file open */
896 /* very simple convenience function */
897 void utils_beep(void)
899 if (prefs.beep_on_errors)
900 gdk_beep();
904 /* taken from busybox, thanks */
905 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
906 gulong display_unit)
908 /* The code will adjust for additional (appended) units. */
909 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
910 static const gchar fmt[] = "%Lu %c%c";
911 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
913 guint64 val;
914 gint frac;
915 const gchar *u;
916 const gchar *f;
918 u = zero_and_units;
919 f = fmt;
920 frac = 0;
922 val = size * block_size;
923 if (val == 0)
924 return g_strdup(u);
926 if (display_unit)
928 val += display_unit/2; /* Deal with rounding. */
929 val /= display_unit; /* Don't combine with the line above!!! */
931 else
933 ++u;
934 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
936 f = fmt_tenths;
937 ++u;
938 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
939 val /= 1024;
941 if (frac >= 10)
942 { /* We need to round up here. */
943 ++val;
944 frac = 0;
948 /* If f==fmt then 'frac' and 'u' are ignored. */
949 return g_strdup_printf(f, val, frac, *u, 'b');
953 /* converts a color representation using gdk_color_parse(), with additional
954 * support of the "0x" prefix as a synonym for "#" */
955 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
957 gchar buf[64] = {0};
959 g_return_val_if_fail(spec != NULL, -1);
961 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
963 /* convert to # format for GDK to understand it */
964 buf[0] = '#';
965 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
966 spec = buf;
969 return gdk_color_parse(spec, color);
973 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
974 * returns a 24 bits BGR color, or -1 on failure */
975 gint utils_color_to_bgr(const GdkColor *c)
977 g_return_val_if_fail(c != NULL, -1);
978 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
982 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
983 * utils_color_to_bgr() */
984 gint utils_parse_color_to_bgr(const gchar *spec)
986 GdkColor color;
987 if (utils_parse_color(spec, &color))
988 return utils_color_to_bgr(&color);
989 else
990 return -1;
994 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
995 gchar *utils_get_current_time_string(void)
997 const time_t tp = time(NULL);
998 const struct tm *tmval = localtime(&tp);
999 gchar *result = g_malloc0(9);
1001 strftime(result, 9, "%H:%M:%S", tmval);
1002 result[8] = '\0';
1003 return result;
1007 GIOChannel *utils_set_up_io_channel(
1008 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1010 GIOChannel *ioc;
1011 /*const gchar *encoding;*/
1013 #ifdef G_OS_WIN32
1014 ioc = g_io_channel_win32_new_fd(fd);
1015 #else
1016 ioc = g_io_channel_unix_new(fd);
1017 #endif
1019 if (nblock)
1020 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1022 g_io_channel_set_encoding(ioc, NULL, NULL);
1024 if (! g_get_charset(&encoding))
1025 { // hope this works reliably
1026 GError *error = NULL;
1027 g_io_channel_set_encoding(ioc, encoding, &error);
1028 if (error)
1030 geany_debug("%s: %s", G_STRFUNC, error->message);
1031 g_error_free(error);
1032 return ioc;
1036 /* "auto-close" ;-) */
1037 g_io_channel_set_close_on_unref(ioc, TRUE);
1039 g_io_add_watch(ioc, cond, func, data);
1040 g_io_channel_unref(ioc);
1042 return ioc;
1046 gchar **utils_read_file_in_array(const gchar *filename)
1048 gchar **result = NULL;
1049 gchar *data;
1051 g_return_val_if_fail(filename != NULL, NULL);
1053 g_file_get_contents(filename, &data, NULL, NULL);
1055 if (data != NULL)
1057 result = g_strsplit_set(data, "\r\n", -1);
1058 g_free(data);
1061 return result;
1065 /* Contributed by Stefan Oltmanns, thanks.
1066 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1067 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1068 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1070 gsize i, j, len;
1071 guint unicodechar;
1073 g_return_val_if_fail(string != NULL, FALSE);
1075 j = 0;
1076 len = strlen(string);
1077 for (i = 0; i < len; i++)
1079 if (string[i]=='\\')
1081 if (i++ >= strlen(string))
1083 return FALSE;
1085 switch (string[i])
1087 case '\\':
1088 if (keep_backslash)
1089 string[j++] = '\\';
1090 string[j] = '\\';
1091 break;
1092 case 'n':
1093 string[j] = '\n';
1094 break;
1095 case 'r':
1096 string[j] = '\r';
1097 break;
1098 case 't':
1099 string[j] = '\t';
1100 break;
1101 #if 0
1102 case 'x': /* Warning: May produce illegal utf-8 string! */
1103 i += 2;
1104 if (i >= strlen(string))
1106 return FALSE;
1108 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1109 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1110 else return FALSE;
1111 string[j] <<= 4;
1112 if (isdigit(string[i])) string[j] |= string[i] - 48;
1113 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1114 else return FALSE;
1115 break;
1116 #endif
1117 case 'u':
1119 i += 2;
1120 if (i >= strlen(string))
1122 return FALSE;
1124 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1125 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1126 else return FALSE;
1127 unicodechar <<= 4;
1128 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1129 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1130 else return FALSE;
1131 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1132 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1134 i += 2;
1135 unicodechar <<= 8;
1136 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1137 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1138 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1139 else unicodechar |= tolower(string[i])-87;
1141 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1142 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1144 i += 2;
1145 unicodechar <<= 8;
1146 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1147 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1148 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1149 else unicodechar |= tolower(string[i])-87;
1151 if (unicodechar < 0x80)
1153 string[j] = unicodechar;
1155 else if (unicodechar < 0x800)
1157 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1158 j++;
1159 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1161 else if (unicodechar < 0x10000)
1163 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1164 j++;
1165 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1166 j++;
1167 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1169 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1171 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1172 j++;
1173 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1174 j++;
1175 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1176 j++;
1177 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1179 else
1181 return FALSE;
1183 break;
1185 default:
1186 /* unnecessary escapes are allowed */
1187 if (keep_backslash)
1188 string[j++] = '\\';
1189 string[j] = string[i];
1192 else
1194 string[j] = string[i];
1196 j++;
1198 while (j < i)
1200 string[j] = 0;
1201 j++;
1203 return TRUE;
1207 /* Wraps a string in place, replacing a space with a newline character.
1208 * wrapstart is the minimum position to start wrapping or -1 for default */
1209 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1211 gchar *pos, *linestart;
1212 gboolean ret = FALSE;
1214 if (wrapstart < 0)
1215 wrapstart = 80;
1217 for (pos = linestart = string; *pos != '\0'; pos++)
1219 if (pos - linestart >= wrapstart && *pos == ' ')
1221 *pos = '\n';
1222 linestart = pos;
1223 ret = TRUE;
1226 return ret;
1231 * Converts the given UTF-8 encoded string into locale encoding.
1232 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1234 * @param utf8_text UTF-8 encoded text.
1236 * @return The converted string in locale encoding, or a copy of the input string if conversion
1237 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1239 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1241 #ifdef G_OS_WIN32
1242 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1243 * which would result in wrongly converted strings */
1244 return g_strdup(utf8_text);
1245 #else
1246 gchar *locale_text;
1248 if (! utf8_text)
1249 return NULL;
1250 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1251 if (locale_text == NULL)
1252 locale_text = g_strdup(utf8_text);
1253 return locale_text;
1254 #endif
1259 * Converts the given string (in locale encoding) into UTF-8 encoding.
1260 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1262 * @param locale_text Text in locale encoding.
1264 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1265 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1267 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1269 #ifdef G_OS_WIN32
1270 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1271 * which would result in wrongly converted strings */
1272 return g_strdup(locale_text);
1273 #else
1274 gchar *utf8_text;
1276 if (! locale_text)
1277 return NULL;
1278 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1279 if (utf8_text == NULL)
1280 utf8_text = g_strdup(locale_text);
1281 return utf8_text;
1282 #endif
1286 /* Pass pointers to free after arg_count.
1287 * The last argument must be NULL as an extra check that arg_count is correct. */
1288 void utils_free_pointers(gsize arg_count, ...)
1290 va_list a;
1291 gsize i;
1292 gpointer ptr;
1294 va_start(a, arg_count);
1295 for (i = 0; i < arg_count; i++)
1297 ptr = va_arg(a, gpointer);
1298 g_free(ptr);
1300 ptr = va_arg(a, gpointer);
1301 if (ptr)
1302 g_warning("Wrong arg_count!");
1303 va_end(a);
1307 /* currently unused */
1308 #if 0
1309 /* Creates a string array deep copy of a series of non-NULL strings.
1310 * The first argument is nothing special.
1311 * The list must be ended with NULL.
1312 * If first is NULL, NULL is returned. */
1313 gchar **utils_strv_new(const gchar *first, ...)
1315 gsize strvlen, i;
1316 va_list args;
1317 gchar *str;
1318 gchar **strv;
1320 g_return_val_if_fail(first != NULL, NULL);
1322 strvlen = 1; /* for first argument */
1324 /* count other arguments */
1325 va_start(args, first);
1326 for (; va_arg(args, gchar*) != NULL; strvlen++);
1327 va_end(args);
1329 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1330 strv[0] = g_strdup(first);
1332 va_start(args, first);
1333 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1335 strv[i] = g_strdup(str);
1337 va_end(args);
1339 strv[i] = NULL;
1340 return strv;
1342 #endif
1346 * Creates a directory if it doesn't already exist.
1347 * Creates intermediate parent directories as needed, too.
1348 * The permissions of the created directory are set 0700.
1350 * @param path The path of the directory to create, in locale encoding.
1351 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1353 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1354 * failed operation is returned.
1356 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1358 gint mode = 0700;
1359 gint result;
1361 if (path == NULL || strlen(path) == 0)
1362 return EFAULT;
1364 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1365 if (result != 0)
1366 return errno;
1367 return 0;
1372 * Gets a list of files from the specified directory.
1373 * Locale encoding is expected for @a path and used for the file list. The list and the data
1374 * in the list should be freed after use, e.g.:
1375 * @code
1376 * g_slist_foreach(list, (GFunc) g_free, NULL);
1377 * g_slist_free(list); @endcode
1379 * @note If you don't need a list you should use the foreach_dir() macro instead -
1380 * it's more efficient.
1382 * @param path The path of the directory to scan, in locale encoding.
1383 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1384 * will use more memory.
1385 * @param sort Whether to sort alphabetically (UTF-8 safe).
1386 * @param error The location for storing a possible error, or @c NULL.
1388 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1389 * freed when no longer needed.
1390 * @see utils_get_file_list().
1392 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1394 GSList *list = NULL;
1395 GDir *dir;
1396 const gchar *filename;
1398 if (error)
1399 *error = NULL;
1400 g_return_val_if_fail(path != NULL, NULL);
1402 dir = g_dir_open(path, 0, error);
1403 if (dir == NULL)
1404 return NULL;
1406 foreach_dir(filename, dir)
1408 list = g_slist_prepend(list, full_path ?
1409 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1411 g_dir_close(dir);
1412 /* sorting last is quicker than on insertion */
1413 if (sort)
1414 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1415 return list;
1420 * Gets a sorted list of files from the specified directory.
1421 * Locale encoding is expected for @a path and used for the file list. The list and the data
1422 * in the list should be freed after use, e.g.:
1423 * @code
1424 * g_slist_foreach(list, (GFunc) g_free, NULL);
1425 * g_slist_free(list); @endcode
1427 * @param path The path of the directory to scan, in locale encoding.
1428 * @param length The location to store the number of non-@c NULL data items in the list,
1429 * unless @c NULL.
1430 * @param error The location for storing a possible error, or @c NULL.
1432 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1433 * freed when no longer needed.
1434 * @see utils_get_file_list_full().
1436 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1438 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1440 if (length)
1441 *length = g_slist_length(list);
1442 return list;
1446 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1447 gboolean utils_str_has_upper(const gchar *str)
1449 gunichar c;
1451 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1452 return FALSE;
1454 while (*str != '\0')
1456 c = g_utf8_get_char(str);
1457 /* check only letters and stop once the first non-capital was found */
1458 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1459 return TRUE;
1460 /* FIXME don't write a const string */
1461 str = g_utf8_next_char(str);
1463 return FALSE;
1467 /* end can be -1 for haystack->len.
1468 * returns: position of found text or -1. */
1469 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1471 gint pos;
1473 g_return_val_if_fail(haystack != NULL, -1);
1474 if (haystack->len == 0)
1475 return -1;
1477 g_return_val_if_fail(start >= 0, -1);
1478 if (start >= (gint)haystack->len)
1479 return -1;
1481 g_return_val_if_fail(!EMPTY(needle), -1);
1483 if (end < 0)
1484 end = haystack->len;
1486 pos = utils_strpos(haystack->str + start, needle);
1487 if (pos == -1)
1488 return -1;
1490 pos += start;
1491 if (pos >= end)
1492 return -1;
1493 return pos;
1497 /* Replaces @len characters from offset @a pos.
1498 * len can be -1 to replace the remainder of @a str.
1499 * returns: pos + strlen(replace). */
1500 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1502 g_string_erase(str, pos, len);
1503 if (replace)
1505 g_string_insert(str, pos, replace);
1506 pos += strlen(replace);
1508 return pos;
1513 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1514 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1515 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1517 * @param haystack The input string to operate on. This string is modified in place.
1518 * @param needle The string which should be replaced.
1519 * @param replace The replacement for @a needle.
1521 * @return Number of replacements made.
1523 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1525 guint count = 0;
1526 gint pos = 0;
1527 gsize needle_length = strlen(needle);
1529 while (1)
1531 pos = utils_string_find(haystack, pos, -1, needle);
1533 if (pos == -1)
1534 break;
1536 pos = utils_string_replace(haystack, pos, needle_length, replace);
1537 count++;
1539 return count;
1544 * Replaces only the first occurrence of @a needle in @a haystack
1545 * with @a replace.
1546 * For details, see utils_string_replace_all().
1548 * @param haystack The input string to operate on. This string is modified in place.
1549 * @param needle The string which should be replaced.
1550 * @param replace The replacement for @a needle.
1552 * @return Number of replacements made.
1554 * @since 0.16
1556 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1558 gint pos = utils_string_find(haystack, 0, -1, needle);
1560 if (pos == -1)
1561 return 0;
1563 utils_string_replace(haystack, pos, strlen(needle), replace);
1564 return 1;
1568 /* Similar to g_regex_replace but allows matching a subgroup.
1569 * match_num: which match to replace, 0 for whole match.
1570 * literal: FALSE to interpret escape sequences in @a replace.
1571 * returns: number of replacements.
1572 * bug: replaced text can affect matching of ^ or \b */
1573 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1574 guint match_num, const gchar *replace, gboolean literal)
1576 GMatchInfo *minfo;
1577 guint ret = 0;
1578 gint start = 0;
1580 g_assert(literal); /* escapes not implemented yet */
1581 g_return_val_if_fail(replace, 0);
1583 /* ensure haystack->str is not null */
1584 if (haystack->len == 0)
1585 return 0;
1587 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1588 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1590 gint end, len;
1592 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1593 len = end - start;
1594 utils_string_replace(haystack, start, len, replace);
1595 ret++;
1597 /* skip past whole match */
1598 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1599 start = end - len + strlen(replace);
1600 g_match_info_free(minfo);
1602 g_match_info_free(minfo);
1603 return ret;
1607 /* Get project or default startup directory (if set), or NULL. */
1608 const gchar *utils_get_default_dir_utf8(void)
1610 if (app->project && !EMPTY(app->project->base_path))
1612 return app->project->base_path;
1615 if (!EMPTY(prefs.default_open_path))
1617 return prefs.default_open_path;
1619 return NULL;
1624 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1625 * systems. On Win32 platforms, it uses the Windows API.
1627 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1628 * @param argv The child's argument vector.
1629 * @param env The child's environment, or @a NULL to inherit parent's.
1630 * @param flags Flags from GSpawnFlags.
1631 * @param child_setup A function to run in the child just before exec().
1632 * @param user_data The user data for child_setup.
1633 * @param std_out The return location for child output, or @a NULL.
1634 * @param std_err The return location for child error messages, or @a NULL.
1635 * @param exit_status The child exit status, as returned by waitpid(), or @a NULL.
1636 * @param error The return location for error or @a NULL.
1638 * @return @c TRUE on success, @c FALSE if an error was set.
1640 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1641 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1642 gchar **std_err, gint *exit_status, GError **error)
1644 gboolean result;
1646 if (argv == NULL)
1648 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1649 return FALSE;
1652 if (std_out)
1653 *std_out = NULL;
1655 if (std_err)
1656 *std_err = NULL;
1658 #ifdef G_OS_WIN32
1659 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1660 #else
1661 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1662 #endif
1664 return result;
1669 * Wraps g_spawn_async() and internally calls this function on Unix-like
1670 * systems. On Win32 platforms, it uses the Windows API.
1672 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1673 * @param argv The child's argument vector.
1674 * @param env The child's environment, or @a NULL to inherit parent's.
1675 * @param flags Flags from GSpawnFlags.
1676 * @param child_setup A function to run in the child just before exec().
1677 * @param user_data The user data for child_setup.
1678 * @param child_pid The return location for child process ID, or NULL.
1679 * @param error The return location for error or @a NULL.
1681 * @return @c TRUE on success, @c FALSE if an error was set.
1683 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1684 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1685 GError **error)
1687 gboolean result;
1689 if (argv == NULL)
1691 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1692 return FALSE;
1695 #ifdef G_OS_WIN32
1696 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1697 #else
1698 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1699 #endif
1700 return result;
1704 /* Retrieves the path for the given URI.
1705 * It returns:
1706 * - the path which was determined by g_filename_from_uri() or GIO
1707 * - NULL if the URI is non-local and gvfs-fuse is not installed
1708 * - a new copy of 'uri' if it is not an URI. */
1709 gchar *utils_get_path_from_uri(const gchar *uri)
1711 gchar *locale_filename;
1713 g_return_val_if_fail(uri != NULL, NULL);
1715 if (! utils_is_uri(uri))
1716 return g_strdup(uri);
1718 /* this will work only for 'file://' URIs */
1719 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1720 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1721 if (locale_filename == NULL)
1723 GFile *file = g_file_new_for_uri(uri);
1724 locale_filename = g_file_get_path(file);
1725 g_object_unref(file);
1726 if (locale_filename == NULL)
1728 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1729 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1733 return locale_filename;
1737 gboolean utils_is_uri(const gchar *uri)
1739 g_return_val_if_fail(uri != NULL, FALSE);
1741 return (strstr(uri, "://") != NULL);
1745 /* path should be in locale encoding */
1746 gboolean utils_is_remote_path(const gchar *path)
1748 g_return_val_if_fail(path != NULL, FALSE);
1750 /* if path is an URI and it doesn't start "file://", we take it as remote */
1751 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1752 return TRUE;
1754 #ifndef G_OS_WIN32
1756 static gchar *fuse_path = NULL;
1757 static gsize len = 0;
1759 if (G_UNLIKELY(fuse_path == NULL))
1761 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1762 len = strlen(fuse_path);
1764 /* Comparing the file path against a hardcoded path is not the most elegant solution
1765 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1766 * proper GFile objects for Fuse paths, but it only does in future GVFS
1767 * versions (gvfs 1.1.1). */
1768 return (strncmp(path, fuse_path, len) == 0);
1770 #endif
1772 return FALSE;
1776 /* Remove all relative and untidy elements from the path of @a filename.
1777 * @param filename must be a valid absolute path.
1778 * @see tm_get_real_path() - also resolves links. */
1779 void utils_tidy_path(gchar *filename)
1781 GString *str;
1782 const gchar *needle;
1783 gboolean preserve_double_backslash = FALSE;
1785 g_return_if_fail(g_path_is_absolute(filename));
1787 str = g_string_new(filename);
1789 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1790 preserve_double_backslash = TRUE;
1792 #ifdef G_OS_WIN32
1793 /* using MSYS we can get Unix-style separators */
1794 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1795 #endif
1796 /* replace "/./" and "//" */
1797 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1798 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1800 if (preserve_double_backslash)
1801 g_string_prepend(str, "\\");
1803 /* replace "/../" */
1804 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1805 while (1)
1807 const gchar *c = strstr(str->str, needle);
1808 if (c == NULL)
1809 break;
1810 else
1812 gssize pos, sub_len;
1814 pos = c - str->str;
1815 if (pos <= 3)
1816 break; /* bad path */
1818 /* replace "/../" */
1819 g_string_erase(str, pos, strlen(needle));
1820 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1822 /* search for last "/" before found "/../" */
1823 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1824 sub_len = pos - (c - str->str);
1825 if (! c)
1826 break; /* bad path */
1828 pos = c - str->str; /* position of previous "/" */
1829 g_string_erase(str, pos, sub_len);
1832 if (str->len <= strlen(filename))
1833 memcpy(filename, str->str, str->len + 1);
1834 else
1835 g_warn_if_reached();
1836 g_string_free(str, TRUE);
1841 * Removes characters from a string, in place.
1843 * @param string String to search.
1844 * @param chars Characters to remove.
1846 * @return @a string - return value is only useful when nesting function calls, e.g.:
1847 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1849 * @see @c g_strdelimit.
1851 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1853 const gchar *r;
1854 gchar *w = string;
1856 g_return_val_if_fail(string, NULL);
1857 if (G_UNLIKELY(EMPTY(chars)))
1858 return string;
1860 foreach_str(r, string)
1862 if (!strchr(chars, *r))
1863 *w++ = *r;
1865 *w = 0x0;
1866 return string;
1870 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1871 GSList *utils_get_config_files(const gchar *subdir)
1873 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1874 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1875 GSList *syslist, *node;
1877 if (!list)
1879 utils_mkdir(path, FALSE);
1881 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1882 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1883 /* merge lists */
1884 list = g_slist_concat(list, syslist);
1886 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1887 /* remove duplicates (next to each other after sorting) */
1888 foreach_slist(node, list)
1890 if (node->next && utils_str_equal(node->next->data, node->data))
1892 GSList *old = node->next;
1894 g_free(old->data);
1895 node->next = old->next;
1896 g_slist_free1(old);
1899 g_free(path);
1900 return list;
1904 /* Suffix can be NULL or a string which should be appended to the Help URL like
1905 * an anchor link, e.g. "#some_anchor". */
1906 gchar *utils_get_help_url(const gchar *suffix)
1908 gint skip;
1909 gchar *uri;
1911 #ifdef G_OS_WIN32
1912 skip = 8;
1913 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1914 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1915 #else
1916 skip = 7;
1917 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1918 #endif
1920 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1921 { /* fall back to online documentation if it is not found on the hard disk */
1922 g_free(uri);
1923 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1926 if (suffix != NULL)
1928 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1931 return uri;
1935 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1937 const gchar **p;
1939 for (p = haystack; *p != NULL; ++p)
1941 if (utils_str_equal(*p, needle))
1942 return TRUE;
1944 return FALSE;
1949 * Copies the current environment into a new array.
1950 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1951 * All further arguments are key, value pairs of variables which should be added to
1952 * the environment.
1954 * The argument list must be @c NULL-terminated.
1956 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1957 * @param first_varname Name of the first variable to copy into the new array.
1958 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1960 * @return The new environment array.
1962 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1964 gchar **result;
1965 gchar **p;
1966 gchar **env;
1967 va_list args;
1968 const gchar *key, *value;
1969 guint n, o;
1971 /* count the additional variables */
1972 va_start(args, first_varname);
1973 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1974 va_end(args);
1975 /* the passed arguments should be even (key, value pairs) */
1976 g_return_val_if_fail(o % 2 == 0, NULL);
1978 o /= 2;
1980 /* get all the environ variables */
1981 env = g_listenv();
1983 /* create an array large enough to hold the new environment */
1984 n = g_strv_length(env);
1985 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1986 result = g_new(gchar *, n + o + 1);
1988 /* copy the environment */
1989 for (n = 0, p = env; *p != NULL; ++p)
1991 /* copy the variable */
1992 value = g_getenv(*p);
1993 if (G_LIKELY(value != NULL))
1995 /* skip excluded variables */
1996 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1997 continue;
1999 result[n++] = g_strconcat(*p, "=", value, NULL);
2002 g_strfreev(env);
2004 /* now add additional variables */
2005 va_start(args, first_varname);
2006 key = first_varname;
2007 value = va_arg(args, gchar*);
2008 while (key != NULL)
2010 result[n++] = g_strconcat(key, "=", value, NULL);
2012 key = va_arg(args, gchar*);
2013 if (key == NULL)
2014 break;
2015 value = va_arg(args, gchar*);
2017 va_end(args);
2019 result[n] = NULL;
2021 return result;
2025 /* Joins @a first and @a second into a new string vector, freeing the originals.
2026 * The original contents are reused. */
2027 gchar **utils_strv_join(gchar **first, gchar **second)
2029 gchar **strv;
2030 gchar **rptr, **wptr;
2032 if (!first)
2033 return second;
2034 if (!second)
2035 return first;
2037 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2038 wptr = strv;
2040 foreach_strv(rptr, first)
2041 *wptr++ = *rptr;
2042 foreach_strv(rptr, second)
2043 *wptr++ = *rptr;
2045 g_free(first);
2046 g_free(second);
2047 return strv;
2051 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2052 * obviously g_date_set_parse() uses some magic.
2053 * The returned GDate object must be freed. */
2054 GDate *utils_parse_date(const gchar *input)
2056 GDate *date = g_date_new();
2058 g_date_set_parse(date, input);
2060 if (g_date_valid(date))
2061 return date;
2063 g_date_free(date);
2064 return NULL;
2068 gchar *utils_parse_and_format_build_date(const gchar *input)
2070 gchar date_buf[255];
2071 GDate *date = utils_parse_date(input);
2073 if (date != NULL)
2075 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2076 g_date_free(date);
2077 return g_strdup(date_buf);
2080 return g_strdup(input);
2084 gchar *utils_get_user_config_dir(void)
2086 #ifdef G_OS_WIN32
2087 return win32_get_user_config_dir();
2088 #else
2089 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
2090 #endif
2094 static gboolean is_osx_bundle(void)
2096 #ifdef MAC_INTEGRATION
2097 gchar *bundle_id = gtkosx_application_get_bundle_id();
2098 if (bundle_id)
2100 g_free(bundle_id);
2101 return TRUE;
2103 #endif
2104 return FALSE;
2108 const gchar *utils_resource_dir(GeanyResourceDirType type)
2110 static const gchar *resdirs[RESOURCE_DIR_COUNT] = {NULL};
2112 if (!resdirs[RESOURCE_DIR_DATA])
2114 #ifdef G_OS_WIN32
2115 gchar *prefix = win32_get_installation_dir();
2117 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "data", NULL);
2118 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2119 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "doc", NULL);
2120 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2121 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", NULL);
2122 g_free(prefix);
2123 #else
2124 if (is_osx_bundle())
2126 # ifdef MAC_INTEGRATION
2127 gchar *prefix = gtkosx_application_get_resource_path();
2129 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "share", "geany", NULL);
2130 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2131 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2132 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2133 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2134 g_free(prefix);
2135 # endif
2137 else
2139 resdirs[RESOURCE_DIR_DATA] = g_build_filename(GEANY_DATADIR, "geany", NULL);
2140 resdirs[RESOURCE_DIR_ICON] = g_build_filename(GEANY_DATADIR, "icons", NULL);
2141 resdirs[RESOURCE_DIR_DOC] = g_build_filename(GEANY_DOCDIR, "html", NULL);
2142 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(GEANY_LOCALEDIR, NULL);
2143 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(GEANY_LIBDIR, "geany", NULL);
2145 #endif
2148 return resdirs[type];
2152 void utils_start_new_geany_instance(gchar *doc_path)
2154 gchar **argv;
2155 const gchar *command = is_osx_bundle() ? "open" : "geany";
2156 gchar *exec_path = g_find_program_in_path(command);
2158 if (exec_path)
2160 GError *err = NULL;
2162 if (is_osx_bundle())
2164 gchar *osx_argv[] = {exec_path, "-n", "-a", "Geany", doc_path, NULL};
2165 argv = osx_argv;
2167 else
2169 gchar *unix_argv[] = {exec_path, "-i", doc_path, NULL};
2170 argv = unix_argv;
2173 if (!utils_spawn_async(NULL, argv, NULL, 0, NULL, NULL, NULL, &err))
2175 g_printerr("Unable to open new window: %s", err->message);
2176 g_error_free(err);
2178 g_free(exec_path);
2180 else
2181 g_printerr("Unable to find 'geany'");