Merge pull request #457 from techee/icon2
[geany-mirror.git] / src / utils.c
blob92e906458f0a73da43b7b27928f324f0bbbe4241
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 GEANY_API_SYMBOL
73 void utils_open_browser(const gchar *uri)
75 #ifdef G_OS_WIN32
76 g_return_if_fail(uri != NULL);
77 win32_open_browser(uri);
78 #else
79 gboolean again = TRUE;
81 g_return_if_fail(uri != NULL);
83 while (again)
85 gchar *cmdline = g_strconcat(tool_prefs.browser_cmd, " \"", uri, "\"", NULL);
87 if (g_spawn_command_line_async(cmdline, NULL))
88 again = FALSE;
89 else
91 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
92 _("Failed to spawn the configured browser command. "
93 "Please correct it or enter another one."),
94 tool_prefs.browser_cmd);
96 if (new_cmd == NULL) /* user canceled */
97 again = FALSE;
98 else
99 SETPTR(tool_prefs.browser_cmd, new_cmd);
101 g_free(cmdline);
103 #endif
107 /* taken from anjuta, to determine the EOL mode of the file */
108 gint utils_get_line_endings(const gchar* buffer, gsize size)
110 gsize i;
111 guint cr, lf, crlf, max_mode;
112 gint mode;
114 cr = lf = crlf = 0;
116 for (i = 0; i < size ; i++)
118 if (buffer[i] == 0x0a)
120 /* LF */
121 lf++;
123 else if (buffer[i] == 0x0d)
125 if (i >= (size - 1))
127 /* Last char, CR */
128 cr++;
130 else
132 if (buffer[i + 1] != 0x0a)
134 /* CR */
135 cr++;
137 else
139 /* CRLF */
140 crlf++;
142 i++;
147 /* Vote for the maximum */
148 mode = SC_EOL_LF;
149 max_mode = lf;
150 if (crlf > max_mode)
152 mode = SC_EOL_CRLF;
153 max_mode = crlf;
155 if (cr > max_mode)
157 mode = SC_EOL_CR;
158 max_mode = cr;
161 return mode;
165 gboolean utils_isbrace(gchar c, gboolean include_angles)
167 switch (c)
169 case '<':
170 case '>':
171 return include_angles;
173 case '(':
174 case ')':
175 case '{':
176 case '}':
177 case '[':
178 case ']': return TRUE;
179 default: return FALSE;
184 gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
186 switch (c)
188 case '<':
189 return include_angles;
191 case '(':
192 case '{':
193 case '[': return TRUE;
194 default: return FALSE;
200 * Writes @a text into a file named @a filename.
201 * If the file doesn't exist, it will be created.
202 * If it already exists, it will be overwritten.
204 * @warning You should use @c g_file_set_contents() instead if you don't need
205 * file permissions and other metadata to be preserved, as that always handles
206 * disk exhaustion safely.
208 * @param filename The filename of the file to write, in locale encoding.
209 * @param text The text to write into the file.
211 * @return 0 if the file was successfully written, otherwise the @c errno of the
212 * failed operation is returned.
214 GEANY_API_SYMBOL
215 gint utils_write_file(const gchar *filename, const gchar *text)
217 g_return_val_if_fail(filename != NULL, ENOENT);
218 g_return_val_if_fail(text != NULL, EINVAL);
220 if (file_prefs.use_safe_file_saving)
222 GError *error = NULL;
223 if (! g_file_set_contents(filename, text, -1, &error))
225 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
226 g_error_free(error);
227 return EIO;
230 else
232 FILE *fp;
233 gsize bytes_written, len;
234 gboolean fail = FALSE;
236 if (filename == NULL)
237 return ENOENT;
239 len = strlen(text);
240 errno = 0;
241 fp = g_fopen(filename, "w");
242 if (fp == NULL)
243 fail = TRUE;
244 else
246 bytes_written = fwrite(text, sizeof(gchar), len, fp);
248 if (len != bytes_written)
250 fail = TRUE;
251 geany_debug(
252 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
253 bytes_written, len, filename);
255 if (fclose(fp) != 0)
256 fail = TRUE;
258 if (fail)
260 geany_debug("utils_write_file(): could not write to file %s (%s)",
261 filename, g_strerror(errno));
262 return FALLBACK(errno, EIO);
265 return 0;
269 /** Searches backward through @a size bytes looking for a '<'.
270 * @param sel .
271 * @param size .
272 * @return The tag name (newly allocated) or @c NULL if no opening tag was found.
274 GEANY_API_SYMBOL
275 gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
277 const gchar *cur, *begin;
278 gsize len;
280 cur = utils_find_open_xml_tag_pos(sel, size);
281 if (cur == NULL)
282 return NULL;
284 cur++; /* skip the bracket */
285 begin = cur;
286 while (strchr(":_-.", *cur) || isalnum(*cur))
287 cur++;
289 len = (gsize)(cur - begin);
290 return len ? g_strndup(begin, len) : NULL;
294 /** Searches backward through @a size bytes looking for a '<'.
295 * @param sel .
296 * @param size .
297 * @return pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
299 GEANY_API_SYMBOL
300 const gchar *utils_find_open_xml_tag_pos(const gchar sel[], gint size)
302 /* stolen from anjuta and modified */
303 const gchar *begin, *cur;
305 if (G_UNLIKELY(size < 3))
306 { /* Smallest tag is "<p>" which is 3 characters */
307 return NULL;
309 begin = &sel[0];
310 cur = &sel[size - 1];
312 /* Skip to the character before the closing brace */
313 while (cur > begin)
315 if (*cur == '>')
316 break;
317 --cur;
319 --cur;
320 /* skip whitespace */
321 while (cur > begin && isspace(*cur))
322 cur--;
323 if (*cur == '/')
324 return NULL; /* we found a short tag which doesn't need to be closed */
325 while (cur > begin)
327 if (*cur == '<')
328 break;
329 /* exit immediately if such non-valid XML/HTML is detected, e.g. "<script>if a >" */
330 else if (*cur == '>')
331 break;
332 --cur;
335 /* if the found tag is an opening, not a closing tag or empty <> */
336 if (*cur == '<' && *(cur + 1) != '/' && *(cur + 1) != '>')
337 return cur;
339 return NULL;
343 /* Returns true if tag_name is a self-closing tag */
344 gboolean utils_is_short_html_tag(const gchar *tag_name)
346 const gchar names[][20] = {
347 "area",
348 "base",
349 "basefont", /* < or not < */
350 "br",
351 "col",
352 "command",
353 "embed",
354 "frame",
355 "hr",
356 "img",
357 "input",
358 "keygen",
359 "link",
360 "meta",
361 "param",
362 "source",
363 "track",
364 "wbr"
367 if (tag_name)
369 if (bsearch(tag_name, names, G_N_ELEMENTS(names), 20,
370 (GCompareFunc)g_ascii_strcasecmp))
371 return TRUE;
373 return FALSE;
377 const gchar *utils_get_eol_name(gint eol_mode)
379 switch (eol_mode)
381 case SC_EOL_CRLF: return _("Windows (CRLF)"); break;
382 case SC_EOL_CR: return _("Classic Mac (CR)"); break;
383 default: return _("Unix (LF)"); break;
388 const gchar *utils_get_eol_short_name(gint eol_mode)
390 switch (eol_mode)
392 case SC_EOL_CRLF: return _("CRLF"); break;
393 case SC_EOL_CR: return _("CR"); break;
394 default: return _("LF"); break;
399 const gchar *utils_get_eol_char(gint eol_mode)
401 switch (eol_mode)
403 case SC_EOL_CRLF: return "\r\n"; break;
404 case SC_EOL_CR: return "\r"; break;
405 default: return "\n"; break;
410 /* Converts line endings to @a target_eol_mode. */
411 void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
413 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
415 /* first convert data to LF only */
416 utils_string_replace_all(string, "\r\n", "\n");
417 utils_string_replace_all(string, "\r", "\n");
419 if (target_eol_mode == SC_EOL_LF)
420 return;
422 /* now convert to desired line endings */
423 utils_string_replace_all(string, "\n", eol_str);
427 gboolean utils_atob(const gchar *str)
429 if (G_UNLIKELY(str == NULL))
430 return FALSE;
431 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
432 return TRUE;
433 return FALSE;
437 /* NULL-safe version of g_path_is_absolute(). */
438 gboolean utils_is_absolute_path(const gchar *path)
440 if (G_UNLIKELY(EMPTY(path)))
441 return FALSE;
443 return g_path_is_absolute(path);
447 /* Skips root if path is absolute, do nothing otherwise.
448 * This is a relative-safe version of g_path_skip_root().
450 const gchar *utils_path_skip_root(const gchar *path)
452 const gchar *path_relative;
454 path_relative = g_path_skip_root(path);
456 return (path_relative != NULL) ? path_relative : path;
460 gdouble utils_scale_round(gdouble val, gdouble factor)
462 /*val = floor(val * factor + 0.5);*/
463 val = floor(val);
464 val = MAX(val, 0);
465 val = MIN(val, factor);
467 return val;
471 /* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
472 * returns NULL on charset conversion failure */
473 static gchar *utf8_strdown(const gchar *str)
475 gchar *down;
477 if (g_utf8_validate(str, -1, NULL))
478 down = g_utf8_strdown(str, -1);
479 else
481 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
482 if (down)
483 SETPTR(down, g_utf8_strdown(down, -1));
486 return down;
491 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
492 * It converts both strings into lowercase using g_utf8_strdown() and then compare
493 * both strings using strcmp().
494 * This is not completely accurate regarding locale-specific case sorting rules
495 * but seems to be a good compromise between correctness and performance.
497 * The input strings should be in UTF-8 or locale encoding.
499 * @param s1 Pointer to first string or @c NULL.
500 * @param s2 Pointer to second string or @c NULL.
502 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
503 * to be less than, to match, or to be greater than @a s2.
505 * @since 0.16
507 GEANY_API_SYMBOL
508 gint utils_str_casecmp(const gchar *s1, const gchar *s2)
510 gchar *tmp1, *tmp2;
511 gint result;
513 g_return_val_if_fail(s1 != NULL, 1);
514 g_return_val_if_fail(s2 != NULL, -1);
516 /* ensure strings are UTF-8 and lowercase */
517 tmp1 = utf8_strdown(s1);
518 if (! tmp1)
519 return 1;
520 tmp2 = utf8_strdown(s2);
521 if (! tmp2)
523 g_free(tmp1);
524 return -1;
527 /* compare */
528 result = strcmp(tmp1, tmp2);
530 g_free(tmp1);
531 g_free(tmp2);
532 return result;
537 * Truncates the input string to a given length.
538 * Characters are removed from the middle of the string, so the start and the end of string
539 * won't change.
541 * @param string Input string.
542 * @param truncate_length The length in characters of the resulting string.
544 * @return A copy of @a string which is truncated to @a truncate_length characters,
545 * should be freed when no longer needed.
547 * @since 0.17
549 /* This following function is taken from Gedit. */
550 GEANY_API_SYMBOL
551 gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
553 GString *truncated;
554 guint length;
555 guint n_chars;
556 guint num_left_chars;
557 guint right_offset;
558 guint delimiter_length;
559 const gchar *delimiter = "\342\200\246";
561 g_return_val_if_fail(string != NULL, NULL);
563 length = strlen(string);
565 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
567 /* It doesnt make sense to truncate strings to less than the size of the delimiter plus 2
568 * characters (one on each side) */
569 delimiter_length = g_utf8_strlen(delimiter, -1);
570 if (truncate_length < (delimiter_length + 2))
571 return g_strdup(string);
573 n_chars = g_utf8_strlen(string, length);
575 /* Make sure the string is not already small enough. */
576 if (n_chars <= truncate_length)
577 return g_strdup (string);
579 /* Find the 'middle' where the truncation will occur. */
580 num_left_chars = (truncate_length - delimiter_length) / 2;
581 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
583 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
584 g_string_append(truncated, delimiter);
585 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
587 return g_string_free(truncated, FALSE);
592 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
593 * or if @a a and @a b refer to valid strings which are equal.
595 * @param a Pointer to first string or @c NULL.
596 * @param b Pointer to second string or @c NULL.
598 * @return @c TRUE if @a a equals @a b, else @c FALSE.
600 GEANY_API_SYMBOL
601 gboolean utils_str_equal(const gchar *a, const gchar *b)
603 /* (taken from libexo from os-cillation) */
604 if (a == NULL && b == NULL) return TRUE;
605 else if (a == NULL || b == NULL) return FALSE;
607 return strcmp(a, b) == 0;
612 * Removes the extension from @a filename and return the result in a newly allocated string.
614 * @param filename The filename to operate on.
616 * @return A newly-allocated string, should be freed when no longer needed.
618 GEANY_API_SYMBOL
619 gchar *utils_remove_ext_from_filename(const gchar *filename)
621 gchar *last_dot;
622 gchar *result;
623 gsize len;
625 g_return_val_if_fail(filename != NULL, NULL);
627 last_dot = strrchr(filename, '.');
628 if (! last_dot)
629 return g_strdup(filename);
631 len = (gsize) (last_dot - filename);
632 result = g_malloc(len + 1);
633 memcpy(result, filename, len);
634 result[len] = 0;
636 return result;
640 gchar utils_brace_opposite(gchar ch)
642 switch (ch)
644 case '(': return ')';
645 case ')': return '(';
646 case '[': return ']';
647 case ']': return '[';
648 case '{': return '}';
649 case '}': return '{';
650 case '<': return '>';
651 case '>': return '<';
652 default: return '\0';
657 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
658 * Returns 0 if it can be written, otherwise it returns errno */
659 gint utils_is_file_writable(const gchar *locale_filename)
661 gchar *file;
662 gint ret;
664 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
665 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
666 /* get the file's directory to check for write permission if it doesn't yet exist */
667 file = g_path_get_dirname(locale_filename);
668 else
669 file = g_strdup(locale_filename);
671 #ifdef G_OS_WIN32
672 /* use _waccess on Windows, access() doesn't accept special characters */
673 ret = win32_check_write_permission(file);
674 #else
676 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
677 * errno only when access() explicitly returns an error */
678 if (access(file, R_OK | W_OK) != 0)
679 ret = errno;
680 else
681 ret = 0;
682 #endif
683 g_free(file);
684 return ret;
688 /* Replaces all occurrences of needle in haystack with replacement.
689 * Warning: *haystack must be a heap address; it may be freed and reassigned.
690 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
691 * than @a needle.
692 * All strings have to be NULL-terminated.
693 * See utils_string_replace_all() for details. */
694 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
696 GString *str;
698 g_return_if_fail(*haystack != NULL);
700 str = g_string_new(*haystack);
702 g_free(*haystack);
703 utils_string_replace_all(str, needle, replacement);
705 *haystack = g_string_free(str, FALSE);
709 gint utils_strpos(const gchar *haystack, const gchar *needle)
711 const gchar *sub;
713 if (! *needle)
714 return -1;
716 sub = strstr(haystack, needle);
717 if (! sub)
718 return -1;
720 return sub - haystack;
725 * Retrieves a formatted date/time string from strftime().
726 * This function should be preferred to directly calling strftime() since this function
727 * works on UTF-8 encoded strings.
729 * @param format The format string to pass to strftime(3). See the strftime(3)
730 * documentation for details, in UTF-8 encoding.
731 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
733 * @return A newly-allocated string, should be freed when no longer needed.
735 * @since 0.16
737 GEANY_API_SYMBOL
738 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
740 const struct tm *tm;
741 static gchar date[1024];
742 gchar *locale_format;
743 gsize len;
745 g_return_val_if_fail(format != NULL, NULL);
747 if (! g_utf8_validate(format, -1, NULL))
749 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
750 if (locale_format == NULL)
751 return NULL;
753 else
754 locale_format = g_strdup(format);
756 if (time_to_use != NULL)
757 tm = localtime(time_to_use);
758 else
760 time_t tp = time(NULL);
761 tm = localtime(&tp);
764 len = strftime(date, 1024, locale_format, tm);
765 g_free(locale_format);
766 if (len == 0)
767 return NULL;
769 if (! g_utf8_validate(date, len, NULL))
770 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
771 else
772 return g_strdup(date);
776 gchar *utils_get_initials(const gchar *name)
778 gint i = 1, j = 1;
779 gchar *initials = g_malloc0(5);
781 initials[0] = name[0];
782 while (name[i] != '\0' && j < 4)
784 if (name[i] == ' ' && name[i + 1] != ' ')
786 initials[j++] = name[i + 1];
788 i++;
790 return initials;
795 * Wraps g_key_file_get_integer() to add a default value argument.
797 * @param config A GKeyFile object.
798 * @param section The group name to look in for the key.
799 * @param key The key to find.
800 * @param default_value The default value which will be returned when @a section or @a key
801 * don't exist.
803 * @return The value associated with @a key as an integer, or the given default value if the value
804 * could not be retrieved.
806 GEANY_API_SYMBOL
807 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
808 const gint default_value)
810 gint tmp;
811 GError *error = NULL;
813 g_return_val_if_fail(config, default_value);
815 tmp = g_key_file_get_integer(config, section, key, &error);
816 if (error)
818 g_error_free(error);
819 return default_value;
821 return tmp;
826 * Wraps g_key_file_get_boolean() to add a default value argument.
828 * @param config A GKeyFile object.
829 * @param section The group name to look in for the key.
830 * @param key The key to find.
831 * @param default_value The default value which will be returned when @c section or @c key
832 * don't exist.
834 * @return The value associated with @a key as a boolean, or the given default value if the value
835 * could not be retrieved.
837 GEANY_API_SYMBOL
838 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
839 const gboolean default_value)
841 gboolean tmp;
842 GError *error = NULL;
844 g_return_val_if_fail(config, default_value);
846 tmp = g_key_file_get_boolean(config, section, key, &error);
847 if (error)
849 g_error_free(error);
850 return default_value;
852 return tmp;
857 * Wraps g_key_file_get_string() to add a default value argument.
859 * @param config A GKeyFile object.
860 * @param section The group name to look in for the key.
861 * @param key The key to find.
862 * @param default_value The default value which will be returned when @a section or @a key
863 * don't exist.
865 * @return A newly allocated string, either the value for @a key or a copy of the given
866 * default value if it could not be retrieved.
868 GEANY_API_SYMBOL
869 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
870 const gchar *default_value)
872 gchar *tmp;
874 g_return_val_if_fail(config, g_strdup(default_value));
876 tmp = g_key_file_get_string(config, section, key, NULL);
877 if (!tmp)
879 return g_strdup(default_value);
881 return tmp;
885 gchar *utils_get_hex_from_color(GdkColor *color)
887 g_return_val_if_fail(color != NULL, NULL);
889 return g_strdup_printf("#%02X%02X%02X",
890 (guint) (utils_scale_round(color->red / 256, 255)),
891 (guint) (utils_scale_round(color->green / 256, 255)),
892 (guint) (utils_scale_round(color->blue / 256, 255)));
896 /* Get directory from current file in the notebook.
897 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
898 * Returned string is in UTF-8 encoding */
899 gchar *utils_get_current_file_dir_utf8(void)
901 GeanyDocument *doc = document_get_current();
903 if (doc != NULL)
905 /* get current filename */
906 const gchar *cur_fname = doc->file_name;
908 if (cur_fname != NULL)
910 /* get folder part from current filename */
911 return g_path_get_dirname(cur_fname); /* returns "." if no path */
915 return NULL; /* no file open */
919 /* very simple convenience function */
920 void utils_beep(void)
922 if (prefs.beep_on_errors)
923 gdk_beep();
927 /* taken from busybox, thanks */
928 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
929 gulong display_unit)
931 /* The code will adjust for additional (appended) units. */
932 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
933 static const gchar fmt[] = "%Lu %c%c";
934 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
936 guint64 val;
937 gint frac;
938 const gchar *u;
939 const gchar *f;
941 u = zero_and_units;
942 f = fmt;
943 frac = 0;
945 val = size * block_size;
946 if (val == 0)
947 return g_strdup(u);
949 if (display_unit)
951 val += display_unit/2; /* Deal with rounding. */
952 val /= display_unit; /* Don't combine with the line above!!! */
954 else
956 ++u;
957 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
959 f = fmt_tenths;
960 ++u;
961 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
962 val /= 1024;
964 if (frac >= 10)
965 { /* We need to round up here. */
966 ++val;
967 frac = 0;
971 /* If f==fmt then 'frac' and 'u' are ignored. */
972 return g_strdup_printf(f, val, frac, *u, 'b');
976 /* converts a color representation using gdk_color_parse(), with additional
977 * support of the "0x" prefix as a synonym for "#" */
978 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
980 gchar buf[64] = {0};
982 g_return_val_if_fail(spec != NULL, -1);
984 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
986 /* convert to # format for GDK to understand it */
987 buf[0] = '#';
988 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
989 spec = buf;
992 return gdk_color_parse(spec, color);
996 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
997 * returns a 24 bits BGR color, or -1 on failure */
998 gint utils_color_to_bgr(const GdkColor *c)
1000 g_return_val_if_fail(c != NULL, -1);
1001 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
1005 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
1006 * utils_color_to_bgr() */
1007 gint utils_parse_color_to_bgr(const gchar *spec)
1009 GdkColor color;
1010 if (utils_parse_color(spec, &color))
1011 return utils_color_to_bgr(&color);
1012 else
1013 return -1;
1017 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
1018 gchar *utils_get_current_time_string(void)
1020 const time_t tp = time(NULL);
1021 const struct tm *tmval = localtime(&tp);
1022 gchar *result = g_malloc0(9);
1024 strftime(result, 9, "%H:%M:%S", tmval);
1025 result[8] = '\0';
1026 return result;
1030 GIOChannel *utils_set_up_io_channel(
1031 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1033 GIOChannel *ioc;
1034 /*const gchar *encoding;*/
1036 #ifdef G_OS_WIN32
1037 ioc = g_io_channel_win32_new_fd(fd);
1038 #else
1039 ioc = g_io_channel_unix_new(fd);
1040 #endif
1042 if (nblock)
1043 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1045 g_io_channel_set_encoding(ioc, NULL, NULL);
1047 if (! g_get_charset(&encoding))
1048 { // hope this works reliably
1049 GError *error = NULL;
1050 g_io_channel_set_encoding(ioc, encoding, &error);
1051 if (error)
1053 geany_debug("%s: %s", G_STRFUNC, error->message);
1054 g_error_free(error);
1055 return ioc;
1059 /* "auto-close" ;-) */
1060 g_io_channel_set_close_on_unref(ioc, TRUE);
1062 g_io_add_watch(ioc, cond, func, data);
1063 g_io_channel_unref(ioc);
1065 return ioc;
1069 gchar **utils_read_file_in_array(const gchar *filename)
1071 gchar **result = NULL;
1072 gchar *data;
1074 g_return_val_if_fail(filename != NULL, NULL);
1076 g_file_get_contents(filename, &data, NULL, NULL);
1078 if (data != NULL)
1080 result = g_strsplit_set(data, "\r\n", -1);
1081 g_free(data);
1084 return result;
1088 /* Contributed by Stefan Oltmanns, thanks.
1089 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1090 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1091 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1093 gsize i, j, len;
1094 guint unicodechar;
1096 g_return_val_if_fail(string != NULL, FALSE);
1098 j = 0;
1099 len = strlen(string);
1100 for (i = 0; i < len; i++)
1102 if (string[i]=='\\')
1104 if (i++ >= strlen(string))
1106 return FALSE;
1108 switch (string[i])
1110 case '\\':
1111 if (keep_backslash)
1112 string[j++] = '\\';
1113 string[j] = '\\';
1114 break;
1115 case 'n':
1116 string[j] = '\n';
1117 break;
1118 case 'r':
1119 string[j] = '\r';
1120 break;
1121 case 't':
1122 string[j] = '\t';
1123 break;
1124 #if 0
1125 case 'x': /* Warning: May produce illegal utf-8 string! */
1126 i += 2;
1127 if (i >= strlen(string))
1129 return FALSE;
1131 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1132 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1133 else return FALSE;
1134 string[j] <<= 4;
1135 if (isdigit(string[i])) string[j] |= string[i] - 48;
1136 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1137 else return FALSE;
1138 break;
1139 #endif
1140 case 'u':
1142 i += 2;
1143 if (i >= strlen(string))
1145 return FALSE;
1147 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1148 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1149 else return FALSE;
1150 unicodechar <<= 4;
1151 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1152 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1153 else return FALSE;
1154 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1155 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1157 i += 2;
1158 unicodechar <<= 8;
1159 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1160 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1161 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1162 else unicodechar |= tolower(string[i])-87;
1164 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1165 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1167 i += 2;
1168 unicodechar <<= 8;
1169 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1170 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1171 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1172 else unicodechar |= tolower(string[i])-87;
1174 if (unicodechar < 0x80)
1176 string[j] = unicodechar;
1178 else if (unicodechar < 0x800)
1180 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1181 j++;
1182 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1184 else if (unicodechar < 0x10000)
1186 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1187 j++;
1188 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1189 j++;
1190 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1192 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1194 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1195 j++;
1196 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1197 j++;
1198 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1199 j++;
1200 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1202 else
1204 return FALSE;
1206 break;
1208 default:
1209 /* unnecessary escapes are allowed */
1210 if (keep_backslash)
1211 string[j++] = '\\';
1212 string[j] = string[i];
1215 else
1217 string[j] = string[i];
1219 j++;
1221 while (j < i)
1223 string[j] = 0;
1224 j++;
1226 return TRUE;
1230 /* Wraps a string in place, replacing a space with a newline character.
1231 * wrapstart is the minimum position to start wrapping or -1 for default */
1232 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1234 gchar *pos, *linestart;
1235 gboolean ret = FALSE;
1237 if (wrapstart < 0)
1238 wrapstart = 80;
1240 for (pos = linestart = string; *pos != '\0'; pos++)
1242 if (pos - linestart >= wrapstart && *pos == ' ')
1244 *pos = '\n';
1245 linestart = pos;
1246 ret = TRUE;
1249 return ret;
1254 * Converts the given UTF-8 encoded string into locale encoding.
1255 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1257 * @param utf8_text UTF-8 encoded text.
1259 * @return The converted string in locale encoding, or a copy of the input string if conversion
1260 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1262 GEANY_API_SYMBOL
1263 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1265 #ifdef G_OS_WIN32
1266 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1267 * which would result in wrongly converted strings */
1268 return g_strdup(utf8_text);
1269 #else
1270 gchar *locale_text;
1272 if (! utf8_text)
1273 return NULL;
1274 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1275 if (locale_text == NULL)
1276 locale_text = g_strdup(utf8_text);
1277 return locale_text;
1278 #endif
1283 * Converts the given string (in locale encoding) into UTF-8 encoding.
1284 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1286 * @param locale_text Text in locale encoding.
1288 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1289 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1291 GEANY_API_SYMBOL
1292 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1294 #ifdef G_OS_WIN32
1295 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1296 * which would result in wrongly converted strings */
1297 return g_strdup(locale_text);
1298 #else
1299 gchar *utf8_text;
1301 if (! locale_text)
1302 return NULL;
1303 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1304 if (utf8_text == NULL)
1305 utf8_text = g_strdup(locale_text);
1306 return utf8_text;
1307 #endif
1311 /* Pass pointers to free after arg_count.
1312 * The last argument must be NULL as an extra check that arg_count is correct. */
1313 void utils_free_pointers(gsize arg_count, ...)
1315 va_list a;
1316 gsize i;
1317 gpointer ptr;
1319 va_start(a, arg_count);
1320 for (i = 0; i < arg_count; i++)
1322 ptr = va_arg(a, gpointer);
1323 g_free(ptr);
1325 ptr = va_arg(a, gpointer);
1326 if (ptr)
1327 g_warning("Wrong arg_count!");
1328 va_end(a);
1332 /* currently unused */
1333 #if 0
1334 /* Creates a string array deep copy of a series of non-NULL strings.
1335 * The first argument is nothing special.
1336 * The list must be ended with NULL.
1337 * If first is NULL, NULL is returned. */
1338 gchar **utils_strv_new(const gchar *first, ...)
1340 gsize strvlen, i;
1341 va_list args;
1342 gchar *str;
1343 gchar **strv;
1345 g_return_val_if_fail(first != NULL, NULL);
1347 strvlen = 1; /* for first argument */
1349 /* count other arguments */
1350 va_start(args, first);
1351 for (; va_arg(args, gchar*) != NULL; strvlen++);
1352 va_end(args);
1354 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1355 strv[0] = g_strdup(first);
1357 va_start(args, first);
1358 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1360 strv[i] = g_strdup(str);
1362 va_end(args);
1364 strv[i] = NULL;
1365 return strv;
1367 #endif
1371 * Creates a directory if it doesn't already exist.
1372 * Creates intermediate parent directories as needed, too.
1373 * The permissions of the created directory are set 0700.
1375 * @param path The path of the directory to create, in locale encoding.
1376 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1378 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1379 * failed operation is returned.
1381 GEANY_API_SYMBOL
1382 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1384 gint mode = 0700;
1385 gint result;
1387 if (path == NULL || strlen(path) == 0)
1388 return EFAULT;
1390 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1391 if (result != 0)
1392 return errno;
1393 return 0;
1398 * Gets a list of files from the specified directory.
1399 * Locale encoding is expected for @a path and used for the file list. The list and the data
1400 * in the list should be freed after use, e.g.:
1401 * @code
1402 * g_slist_foreach(list, (GFunc) g_free, NULL);
1403 * g_slist_free(list); @endcode
1405 * @note If you don't need a list you should use the foreach_dir() macro instead -
1406 * it's more efficient.
1408 * @param path The path of the directory to scan, in locale encoding.
1409 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1410 * will use more memory.
1411 * @param sort Whether to sort alphabetically (UTF-8 safe).
1412 * @param error The location for storing a possible error, or @c NULL.
1414 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1415 * freed when no longer needed.
1416 * @see utils_get_file_list().
1418 GEANY_API_SYMBOL
1419 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1421 GSList *list = NULL;
1422 GDir *dir;
1423 const gchar *filename;
1425 if (error)
1426 *error = NULL;
1427 g_return_val_if_fail(path != NULL, NULL);
1429 dir = g_dir_open(path, 0, error);
1430 if (dir == NULL)
1431 return NULL;
1433 foreach_dir(filename, dir)
1435 list = g_slist_prepend(list, full_path ?
1436 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1438 g_dir_close(dir);
1439 /* sorting last is quicker than on insertion */
1440 if (sort)
1441 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1442 return list;
1447 * Gets a sorted list of files from the specified directory.
1448 * Locale encoding is expected for @a path and used for the file list. The list and the data
1449 * in the list should be freed after use, e.g.:
1450 * @code
1451 * g_slist_foreach(list, (GFunc) g_free, NULL);
1452 * g_slist_free(list); @endcode
1454 * @param path The path of the directory to scan, in locale encoding.
1455 * @param length The location to store the number of non-@c NULL data items in the list,
1456 * unless @c NULL.
1457 * @param error The location for storing a possible error, or @c NULL.
1459 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1460 * freed when no longer needed.
1461 * @see utils_get_file_list_full().
1463 GEANY_API_SYMBOL
1464 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1466 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1468 if (length)
1469 *length = g_slist_length(list);
1470 return list;
1474 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1475 gboolean utils_str_has_upper(const gchar *str)
1477 gunichar c;
1479 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1480 return FALSE;
1482 while (*str != '\0')
1484 c = g_utf8_get_char(str);
1485 /* check only letters and stop once the first non-capital was found */
1486 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1487 return TRUE;
1488 /* FIXME don't write a const string */
1489 str = g_utf8_next_char(str);
1491 return FALSE;
1495 /* end can be -1 for haystack->len.
1496 * returns: position of found text or -1. */
1497 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1499 gint pos;
1501 g_return_val_if_fail(haystack != NULL, -1);
1502 if (haystack->len == 0)
1503 return -1;
1505 g_return_val_if_fail(start >= 0, -1);
1506 if (start >= (gint)haystack->len)
1507 return -1;
1509 g_return_val_if_fail(!EMPTY(needle), -1);
1511 if (end < 0)
1512 end = haystack->len;
1514 pos = utils_strpos(haystack->str + start, needle);
1515 if (pos == -1)
1516 return -1;
1518 pos += start;
1519 if (pos >= end)
1520 return -1;
1521 return pos;
1525 /* Replaces @len characters from offset @a pos.
1526 * len can be -1 to replace the remainder of @a str.
1527 * returns: pos + strlen(replace). */
1528 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1530 g_string_erase(str, pos, len);
1531 if (replace)
1533 g_string_insert(str, pos, replace);
1534 pos += strlen(replace);
1536 return pos;
1541 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1542 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1543 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1545 * @param haystack The input string to operate on. This string is modified in place.
1546 * @param needle The string which should be replaced.
1547 * @param replace The replacement for @a needle.
1549 * @return Number of replacements made.
1551 GEANY_API_SYMBOL
1552 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1554 guint count = 0;
1555 gint pos = 0;
1556 gsize needle_length = strlen(needle);
1558 while (1)
1560 pos = utils_string_find(haystack, pos, -1, needle);
1562 if (pos == -1)
1563 break;
1565 pos = utils_string_replace(haystack, pos, needle_length, replace);
1566 count++;
1568 return count;
1573 * Replaces only the first occurrence of @a needle in @a haystack
1574 * with @a replace.
1575 * For details, see utils_string_replace_all().
1577 * @param haystack The input string to operate on. This string is modified in place.
1578 * @param needle The string which should be replaced.
1579 * @param replace The replacement for @a needle.
1581 * @return Number of replacements made.
1583 * @since 0.16
1585 GEANY_API_SYMBOL
1586 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1588 gint pos = utils_string_find(haystack, 0, -1, needle);
1590 if (pos == -1)
1591 return 0;
1593 utils_string_replace(haystack, pos, strlen(needle), replace);
1594 return 1;
1598 /* Similar to g_regex_replace but allows matching a subgroup.
1599 * match_num: which match to replace, 0 for whole match.
1600 * literal: FALSE to interpret escape sequences in @a replace.
1601 * returns: number of replacements.
1602 * bug: replaced text can affect matching of ^ or \b */
1603 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1604 guint match_num, const gchar *replace, gboolean literal)
1606 GMatchInfo *minfo;
1607 guint ret = 0;
1608 gint start = 0;
1610 g_assert(literal); /* escapes not implemented yet */
1611 g_return_val_if_fail(replace, 0);
1613 /* ensure haystack->str is not null */
1614 if (haystack->len == 0)
1615 return 0;
1617 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1618 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1620 gint end, len;
1622 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1623 len = end - start;
1624 utils_string_replace(haystack, start, len, replace);
1625 ret++;
1627 /* skip past whole match */
1628 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1629 start = end - len + strlen(replace);
1630 g_match_info_free(minfo);
1632 g_match_info_free(minfo);
1633 return ret;
1637 /* Get project or default startup directory (if set), or NULL. */
1638 const gchar *utils_get_default_dir_utf8(void)
1640 if (app->project && !EMPTY(app->project->base_path))
1642 return app->project->base_path;
1645 if (!EMPTY(prefs.default_open_path))
1647 return prefs.default_open_path;
1649 return NULL;
1654 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1655 * systems. On Win32 platforms, it uses the Windows API.
1657 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1658 * @param argv The child's argument vector.
1659 * @param env The child's environment, or @a NULL to inherit parent's.
1660 * @param flags Flags from GSpawnFlags.
1661 * @param child_setup A function to run in the child just before exec().
1662 * @param user_data The user data for child_setup.
1663 * @param std_out The return location for child output, or @a NULL.
1664 * @param std_err The return location for child error messages, or @a NULL.
1665 * @param exit_status The child exit status, as returned by waitpid(), or @a NULL.
1666 * @param error The return location for error or @a NULL.
1668 * @return @c TRUE on success, @c FALSE if an error was set.
1670 GEANY_API_SYMBOL
1671 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1672 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1673 gchar **std_err, gint *exit_status, GError **error)
1675 gboolean result;
1677 if (argv == NULL)
1679 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1680 return FALSE;
1683 if (std_out)
1684 *std_out = NULL;
1686 if (std_err)
1687 *std_err = NULL;
1689 #ifdef G_OS_WIN32
1690 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1691 #else
1692 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1693 #endif
1695 return result;
1700 * Wraps g_spawn_async() and internally calls this function on Unix-like
1701 * systems. On Win32 platforms, it uses the Windows API.
1703 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1704 * @param argv The child's argument vector.
1705 * @param env The child's environment, or @a NULL to inherit parent's.
1706 * @param flags Flags from GSpawnFlags.
1707 * @param child_setup A function to run in the child just before exec().
1708 * @param user_data The user data for child_setup.
1709 * @param child_pid The return location for child process ID, or NULL.
1710 * @param error The return location for error or @a NULL.
1712 * @return @c TRUE on success, @c FALSE if an error was set.
1714 GEANY_API_SYMBOL
1715 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1716 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1717 GError **error)
1719 gboolean result;
1721 if (argv == NULL)
1723 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1724 return FALSE;
1727 #ifdef G_OS_WIN32
1728 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1729 #else
1730 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1731 #endif
1732 return result;
1736 /* Retrieves the path for the given URI.
1737 * It returns:
1738 * - the path which was determined by g_filename_from_uri() or GIO
1739 * - NULL if the URI is non-local and gvfs-fuse is not installed
1740 * - a new copy of 'uri' if it is not an URI. */
1741 gchar *utils_get_path_from_uri(const gchar *uri)
1743 gchar *locale_filename;
1745 g_return_val_if_fail(uri != NULL, NULL);
1747 if (! utils_is_uri(uri))
1748 return g_strdup(uri);
1750 /* this will work only for 'file://' URIs */
1751 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1752 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1753 if (locale_filename == NULL)
1755 GFile *file = g_file_new_for_uri(uri);
1756 locale_filename = g_file_get_path(file);
1757 g_object_unref(file);
1758 if (locale_filename == NULL)
1760 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1761 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1765 return locale_filename;
1769 gboolean utils_is_uri(const gchar *uri)
1771 g_return_val_if_fail(uri != NULL, FALSE);
1773 return (strstr(uri, "://") != NULL);
1777 /* path should be in locale encoding */
1778 gboolean utils_is_remote_path(const gchar *path)
1780 g_return_val_if_fail(path != NULL, FALSE);
1782 /* if path is an URI and it doesn't start "file://", we take it as remote */
1783 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1784 return TRUE;
1786 #ifndef G_OS_WIN32
1788 static gchar *fuse_path = NULL;
1789 static gsize len = 0;
1791 if (G_UNLIKELY(fuse_path == NULL))
1793 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1794 len = strlen(fuse_path);
1796 /* Comparing the file path against a hardcoded path is not the most elegant solution
1797 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1798 * proper GFile objects for Fuse paths, but it only does in future GVFS
1799 * versions (gvfs 1.1.1). */
1800 return (strncmp(path, fuse_path, len) == 0);
1802 #endif
1804 return FALSE;
1808 /* Remove all relative and untidy elements from the path of @a filename.
1809 * @param filename must be a valid absolute path.
1810 * @see tm_get_real_path() - also resolves links. */
1811 void utils_tidy_path(gchar *filename)
1813 GString *str;
1814 const gchar *needle;
1815 gboolean preserve_double_backslash = FALSE;
1817 g_return_if_fail(g_path_is_absolute(filename));
1819 str = g_string_new(filename);
1821 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1822 preserve_double_backslash = TRUE;
1824 #ifdef G_OS_WIN32
1825 /* using MSYS we can get Unix-style separators */
1826 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1827 #endif
1828 /* replace "/./" and "//" */
1829 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1830 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1832 if (preserve_double_backslash)
1833 g_string_prepend(str, "\\");
1835 /* replace "/../" */
1836 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1837 while (1)
1839 const gchar *c = strstr(str->str, needle);
1840 if (c == NULL)
1841 break;
1842 else
1844 gssize pos, sub_len;
1846 pos = c - str->str;
1847 if (pos <= 3)
1848 break; /* bad path */
1850 /* replace "/../" */
1851 g_string_erase(str, pos, strlen(needle));
1852 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1854 /* search for last "/" before found "/../" */
1855 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1856 sub_len = pos - (c - str->str);
1857 if (! c)
1858 break; /* bad path */
1860 pos = c - str->str; /* position of previous "/" */
1861 g_string_erase(str, pos, sub_len);
1864 if (str->len <= strlen(filename))
1865 memcpy(filename, str->str, str->len + 1);
1866 else
1867 g_warn_if_reached();
1868 g_string_free(str, TRUE);
1873 * Removes characters from a string, in place.
1875 * @param string String to search.
1876 * @param chars Characters to remove.
1878 * @return @a string - return value is only useful when nesting function calls, e.g.:
1879 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1881 * @see @c g_strdelimit.
1883 GEANY_API_SYMBOL
1884 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1886 const gchar *r;
1887 gchar *w = string;
1889 g_return_val_if_fail(string, NULL);
1890 if (G_UNLIKELY(EMPTY(chars)))
1891 return string;
1893 foreach_str(r, string)
1895 if (!strchr(chars, *r))
1896 *w++ = *r;
1898 *w = 0x0;
1899 return string;
1903 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1904 GSList *utils_get_config_files(const gchar *subdir)
1906 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1907 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1908 GSList *syslist, *node;
1910 if (!list)
1912 utils_mkdir(path, FALSE);
1914 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1915 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1916 /* merge lists */
1917 list = g_slist_concat(list, syslist);
1919 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1920 /* remove duplicates (next to each other after sorting) */
1921 foreach_slist(node, list)
1923 if (node->next && utils_str_equal(node->next->data, node->data))
1925 GSList *old = node->next;
1927 g_free(old->data);
1928 node->next = old->next;
1929 g_slist_free1(old);
1932 g_free(path);
1933 return list;
1937 /* Suffix can be NULL or a string which should be appended to the Help URL like
1938 * an anchor link, e.g. "#some_anchor". */
1939 gchar *utils_get_help_url(const gchar *suffix)
1941 gint skip;
1942 gchar *uri;
1944 #ifdef G_OS_WIN32
1945 skip = 8;
1946 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1947 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1948 #else
1949 skip = 7;
1950 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1951 #endif
1953 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1954 { /* fall back to online documentation if it is not found on the hard disk */
1955 g_free(uri);
1956 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1959 if (suffix != NULL)
1961 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1964 return uri;
1968 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1970 const gchar **p;
1972 for (p = haystack; *p != NULL; ++p)
1974 if (utils_str_equal(*p, needle))
1975 return TRUE;
1977 return FALSE;
1982 * Copies the current environment into a new array.
1983 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1984 * All further arguments are key, value pairs of variables which should be added to
1985 * the environment.
1987 * The argument list must be @c NULL-terminated.
1989 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1990 * @param first_varname Name of the first variable to copy into the new array.
1991 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1993 * @return The new environment array.
1995 GEANY_API_SYMBOL
1996 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1998 gchar **result;
1999 gchar **p;
2000 gchar **env;
2001 va_list args;
2002 const gchar *key, *value;
2003 guint n, o;
2005 /* count the additional variables */
2006 va_start(args, first_varname);
2007 for (o = 1; va_arg(args, gchar*) != NULL; o++);
2008 va_end(args);
2009 /* the passed arguments should be even (key, value pairs) */
2010 g_return_val_if_fail(o % 2 == 0, NULL);
2012 o /= 2;
2014 /* get all the environ variables */
2015 env = g_listenv();
2017 /* create an array large enough to hold the new environment */
2018 n = g_strv_length(env);
2019 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
2020 result = g_new(gchar *, n + o + 1);
2022 /* copy the environment */
2023 for (n = 0, p = env; *p != NULL; ++p)
2025 /* copy the variable */
2026 value = g_getenv(*p);
2027 if (G_LIKELY(value != NULL))
2029 /* skip excluded variables */
2030 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
2031 continue;
2033 result[n++] = g_strconcat(*p, "=", value, NULL);
2036 g_strfreev(env);
2038 /* now add additional variables */
2039 va_start(args, first_varname);
2040 key = first_varname;
2041 value = va_arg(args, gchar*);
2042 while (key != NULL)
2044 result[n++] = g_strconcat(key, "=", value, NULL);
2046 key = va_arg(args, gchar*);
2047 if (key == NULL)
2048 break;
2049 value = va_arg(args, gchar*);
2051 va_end(args);
2053 result[n] = NULL;
2055 return result;
2059 /* Joins @a first and @a second into a new string vector, freeing the originals.
2060 * The original contents are reused. */
2061 gchar **utils_strv_join(gchar **first, gchar **second)
2063 gchar **strv;
2064 gchar **rptr, **wptr;
2066 if (!first)
2067 return second;
2068 if (!second)
2069 return first;
2071 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2072 wptr = strv;
2074 foreach_strv(rptr, first)
2075 *wptr++ = *rptr;
2076 foreach_strv(rptr, second)
2077 *wptr++ = *rptr;
2079 g_free(first);
2080 g_free(second);
2081 return strv;
2085 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2086 * obviously g_date_set_parse() uses some magic.
2087 * The returned GDate object must be freed. */
2088 GDate *utils_parse_date(const gchar *input)
2090 GDate *date = g_date_new();
2092 g_date_set_parse(date, input);
2094 if (g_date_valid(date))
2095 return date;
2097 g_date_free(date);
2098 return NULL;
2102 gchar *utils_parse_and_format_build_date(const gchar *input)
2104 gchar date_buf[255];
2105 GDate *date = utils_parse_date(input);
2107 if (date != NULL)
2109 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2110 g_date_free(date);
2111 return g_strdup(date_buf);
2114 return g_strdup(input);
2118 gchar *utils_get_user_config_dir(void)
2120 #ifdef G_OS_WIN32
2121 return win32_get_user_config_dir();
2122 #else
2123 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
2124 #endif
2128 static gboolean is_osx_bundle(void)
2130 #ifdef MAC_INTEGRATION
2131 gchar *bundle_id = gtkosx_application_get_bundle_id();
2132 if (bundle_id)
2134 g_free(bundle_id);
2135 return TRUE;
2137 #endif
2138 return FALSE;
2142 const gchar *utils_resource_dir(GeanyResourceDirType type)
2144 static const gchar *resdirs[RESOURCE_DIR_COUNT] = {NULL};
2146 if (!resdirs[RESOURCE_DIR_DATA])
2148 #ifdef G_OS_WIN32
2149 gchar *prefix = win32_get_installation_dir();
2151 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "data", NULL);
2152 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2153 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "doc", NULL);
2154 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2155 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", NULL);
2156 g_free(prefix);
2157 #else
2158 if (is_osx_bundle())
2160 # ifdef MAC_INTEGRATION
2161 gchar *prefix = gtkosx_application_get_resource_path();
2163 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "share", "geany", NULL);
2164 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2165 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2166 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2167 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2168 g_free(prefix);
2169 # endif
2171 else
2173 resdirs[RESOURCE_DIR_DATA] = g_build_filename(GEANY_DATADIR, "geany", NULL);
2174 resdirs[RESOURCE_DIR_ICON] = g_build_filename(GEANY_DATADIR, "icons", NULL);
2175 resdirs[RESOURCE_DIR_DOC] = g_build_filename(GEANY_DOCDIR, "html", NULL);
2176 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(GEANY_LOCALEDIR, NULL);
2177 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(GEANY_LIBDIR, "geany", NULL);
2179 #endif
2182 return resdirs[type];
2186 void utils_start_new_geany_instance(const gchar *doc_path)
2188 const gchar *const *argv;
2189 const gchar *command = is_osx_bundle() ? "open" : "geany";
2190 gchar *exec_path = g_find_program_in_path(command);
2192 if (exec_path)
2194 GError *err = NULL;
2196 if (is_osx_bundle())
2198 const gchar *const osx_argv[] = {exec_path, "-n", "-a", "Geany", doc_path, NULL};
2199 argv = osx_argv;
2201 else
2203 const gchar *const unix_argv[] = {exec_path, "-i", doc_path, NULL};
2204 argv = unix_argv;
2207 if (!utils_spawn_async(NULL, (gchar**) argv, NULL, 0, NULL, NULL, NULL, &err))
2209 g_printerr("Unable to open new window: %s", err->message);
2210 g_error_free(err);
2212 g_free(exec_path);
2214 else
2215 g_printerr("Unable to find 'geany'");