g_printerr handler: Don't add newline
[geany-mirror.git] / src / utils.c
blob4f0bd9db13aa2c64d3f203501a33c24b51fe4b67
1 /*
2 * utils.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005 The Geany contributors
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * General utility functions, non-GTK related.
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include "utils.h"
31 #include "app.h"
32 #include "dialogs.h"
33 #include "document.h"
34 #include "prefs.h"
35 #include "prefix.h"
36 #include "sciwrappers.h"
37 #include "spawn.h"
38 #include "support.h"
39 #include "tm_source_file.h" // for tm_get_real_path()
40 #include "templates.h"
41 #include "ui_utils.h"
42 #include "win32.h"
43 #include "osx.h"
45 #include <stdlib.h>
46 #include <ctype.h>
47 #include <math.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <stdarg.h>
53 #ifdef HAVE_SYS_STAT_H
54 # include <sys/stat.h>
55 #endif
56 #ifdef HAVE_SYS_TYPES_H
57 # include <sys/types.h>
58 #endif
60 #include <glib/gstdio.h>
61 #include <gio/gio.h>
64 /**
65 * Tries to open the given URI in a browser.
66 * On Windows, the system's default browser is opened.
67 * On non-Windows systems, the browser command set in the preferences dialog is used. In case
68 * that fails or it is unset, the user is asked to correct or fill it.
70 * @param uri The URI to open in the web browser.
72 * @since 0.16
73 **/
74 GEANY_API_SYMBOL
75 void utils_open_browser(const gchar *uri)
77 #ifdef G_OS_WIN32
78 g_return_if_fail(uri != NULL);
79 win32_open_browser(uri);
80 #else
81 gchar *argv[2] = { (gchar *) uri, NULL };
83 g_return_if_fail(uri != NULL);
85 while (!spawn_async(NULL, tool_prefs.browser_cmd, argv, NULL, NULL, NULL))
87 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
88 _("Failed to spawn the configured browser command. "
89 "Please correct it or enter another one."),
90 tool_prefs.browser_cmd);
92 if (new_cmd == NULL) /* user canceled */
93 break;
95 SETPTR(tool_prefs.browser_cmd, new_cmd);
97 #endif
101 /* taken from anjuta, to determine the EOL mode of the file */
102 gint utils_get_line_endings(const gchar* buffer, gsize size)
104 gsize i;
105 guint cr, lf, crlf, max_mode;
106 gint mode;
108 cr = lf = crlf = 0;
110 for (i = 0; i < size ; i++)
112 if (buffer[i] == 0x0a)
114 /* LF */
115 lf++;
117 else if (buffer[i] == 0x0d)
119 if (i >= (size - 1))
121 /* Last char, CR */
122 cr++;
124 else
126 if (buffer[i + 1] != 0x0a)
128 /* CR */
129 cr++;
131 else
133 /* CRLF */
134 crlf++;
136 i++;
141 /* Vote for the maximum */
142 mode = SC_EOL_LF;
143 max_mode = lf;
144 if (crlf > max_mode)
146 mode = SC_EOL_CRLF;
147 max_mode = crlf;
149 if (cr > max_mode)
151 mode = SC_EOL_CR;
152 max_mode = cr;
155 return mode;
159 gboolean utils_isbrace(gchar c, gboolean include_angles)
161 switch (c)
163 case '<':
164 case '>':
165 return include_angles;
167 case '(':
168 case ')':
169 case '{':
170 case '}':
171 case '[':
172 case ']': return TRUE;
173 default: return FALSE;
178 gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
180 switch (c)
182 case '<':
183 return include_angles;
185 case '(':
186 case '{':
187 case '[': return TRUE;
188 default: return FALSE;
194 * Writes @a text into a file named @a filename.
195 * If the file doesn't exist, it will be created.
196 * If it already exists, it will be overwritten.
198 * @warning You should use @c g_file_set_contents() instead if you don't need
199 * file permissions and other metadata to be preserved, as that always handles
200 * disk exhaustion safely.
202 * @param filename The filename of the file to write, in locale encoding.
203 * @param text The text to write into the file.
205 * @return 0 if the file was successfully written, otherwise the @c errno of the
206 * failed operation is returned.
208 GEANY_API_SYMBOL
209 gint utils_write_file(const gchar *filename, const gchar *text)
211 g_return_val_if_fail(filename != NULL, ENOENT);
212 g_return_val_if_fail(text != NULL, EINVAL);
214 if (file_prefs.use_safe_file_saving)
216 GError *error = NULL;
217 if (! g_file_set_contents(filename, text, -1, &error))
219 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
220 g_error_free(error);
221 return EIO;
224 else
226 FILE *fp;
227 gsize bytes_written, len;
228 gboolean fail = FALSE;
230 if (filename == NULL)
231 return ENOENT;
233 len = strlen(text);
234 errno = 0;
235 fp = g_fopen(filename, "w");
236 if (fp == NULL)
237 fail = TRUE;
238 else
240 bytes_written = fwrite(text, sizeof(gchar), len, fp);
242 if (len != bytes_written)
244 fail = TRUE;
245 geany_debug(
246 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
247 bytes_written, len, filename);
249 if (fclose(fp) != 0)
250 fail = TRUE;
252 if (fail)
254 geany_debug("utils_write_file(): could not write to file %s (%s)",
255 filename, g_strerror(errno));
256 return FALLBACK(errno, EIO);
259 return 0;
263 /** Searches backward through @a size bytes looking for a '<'.
264 * @param sel .
265 * @param size .
266 * @return @nullable The tag name (newly allocated) or @c NULL if no opening tag was found.
268 GEANY_API_SYMBOL
269 gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
271 const gchar *cur, *begin;
272 gsize len;
274 cur = utils_find_open_xml_tag_pos(sel, size);
275 if (cur == NULL)
276 return NULL;
278 cur++; /* skip the bracket */
279 begin = cur;
280 while (strchr(":_-.", *cur) || isalnum(*cur))
281 cur++;
283 len = (gsize)(cur - begin);
284 return len ? g_strndup(begin, len) : NULL;
288 /** Searches backward through @a size bytes looking for a '<'.
289 * @param sel .
290 * @param size .
291 * @return @nullable pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
293 GEANY_API_SYMBOL
294 const gchar *utils_find_open_xml_tag_pos(const gchar sel[], gint size)
296 /* stolen from anjuta and modified */
297 const gchar *begin, *cur;
299 if (G_UNLIKELY(size < 3))
300 { /* Smallest tag is "<p>" which is 3 characters */
301 return NULL;
303 begin = &sel[0];
304 cur = &sel[size - 1];
306 /* Skip to the character before the closing brace */
307 while (cur > begin)
309 if (*cur == '>')
310 break;
311 --cur;
313 --cur;
314 /* skip whitespace */
315 while (cur > begin && isspace(*cur))
316 cur--;
317 if (*cur == '/')
318 return NULL; /* we found a short tag which doesn't need to be closed */
319 while (cur > begin)
321 if (*cur == '<')
322 break;
323 /* exit immediately if such non-valid XML/HTML is detected, e.g. "<script>if a >" */
324 else if (*cur == '>')
325 break;
326 --cur;
329 /* if the found tag is an opening, not a closing tag or empty <> */
330 if (*cur == '<' && *(cur + 1) != '/' && *(cur + 1) != '>')
331 return cur;
333 return NULL;
337 /* Returns true if tag_name is a self-closing tag */
338 gboolean utils_is_short_html_tag(const gchar *tag_name)
340 const gchar names[][20] = {
341 "area",
342 "base",
343 "basefont", /* < or not < */
344 "br",
345 "col",
346 "command",
347 "embed",
348 "frame",
349 "hr",
350 "img",
351 "input",
352 "keygen",
353 "link",
354 "meta",
355 "param",
356 "source",
357 "track",
358 "wbr"
361 if (tag_name)
363 if (bsearch(tag_name, names, G_N_ELEMENTS(names), 20,
364 (GCompareFunc)g_ascii_strcasecmp))
365 return TRUE;
367 return FALSE;
371 const gchar *utils_get_eol_name(gint eol_mode)
373 switch (eol_mode)
375 case SC_EOL_CRLF: return _("Windows (CRLF)"); break;
376 case SC_EOL_CR: return _("Classic Mac (CR)"); break;
377 default: return _("Unix (LF)"); break;
382 const gchar *utils_get_eol_short_name(gint eol_mode)
384 switch (eol_mode)
386 case SC_EOL_CRLF: return _("CRLF"); break;
387 case SC_EOL_CR: return _("CR"); break;
388 default: return _("LF"); break;
393 const gchar *utils_get_eol_char(gint eol_mode)
395 switch (eol_mode)
397 case SC_EOL_CRLF: return "\r\n"; break;
398 case SC_EOL_CR: return "\r"; break;
399 default: return "\n"; break;
404 /* Converts line endings to @a target_eol_mode. */
405 void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
407 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
409 /* first convert data to LF only */
410 utils_string_replace_all(string, "\r\n", "\n");
411 utils_string_replace_all(string, "\r", "\n");
413 if (target_eol_mode == SC_EOL_LF)
414 return;
416 /* now convert to desired line endings */
417 utils_string_replace_all(string, "\n", eol_str);
421 gboolean utils_atob(const gchar *str)
423 if (G_UNLIKELY(str == NULL))
424 return FALSE;
425 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
426 return TRUE;
427 return FALSE;
431 /* NULL-safe version of g_path_is_absolute(). */
432 gboolean utils_is_absolute_path(const gchar *path)
434 if (G_UNLIKELY(EMPTY(path)))
435 return FALSE;
437 return g_path_is_absolute(path);
441 /* Skips root if path is absolute, do nothing otherwise.
442 * This is a relative-safe version of g_path_skip_root().
444 const gchar *utils_path_skip_root(const gchar *path)
446 const gchar *path_relative;
448 path_relative = g_path_skip_root(path);
450 return (path_relative != NULL) ? path_relative : path;
454 /* Convert a fractional @a val in the range [0, 1] to a whole value in the range [0, @a factor].
455 * In particular, this is used for converting a @c GdkColor to the "#RRGGBB" format in a way that
456 * agrees with GTK+, so the "#RRGGBB" in the color picker is the same "#RRGGBB" that is inserted
457 * into the document. See https://github.com/geany/geany/issues/1527
459 gdouble utils_scale_round(gdouble val, gdouble factor)
461 val = floor(val * factor + 0.5);
462 val = MAX(val, 0);
463 val = MIN(val, factor);
465 return val;
469 /* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
470 * returns NULL on charset conversion failure */
471 static gchar *utf8_strdown(const gchar *str)
473 gchar *down;
475 if (g_utf8_validate(str, -1, NULL))
476 down = g_utf8_strdown(str, -1);
477 else
479 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
480 if (down)
481 SETPTR(down, g_utf8_strdown(down, -1));
484 return down;
489 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
490 * It converts both strings into lowercase using g_utf8_strdown() and then compare
491 * both strings using strcmp().
492 * This is not completely accurate regarding locale-specific case sorting rules
493 * but seems to be a good compromise between correctness and performance.
495 * The input strings should be in UTF-8 or locale encoding.
497 * @param s1 @nullable Pointer to first string or @c NULL.
498 * @param s2 @nullable Pointer to second string or @c NULL.
500 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
501 * to be less than, to match, or to be greater than @a s2.
503 * @since 0.16
505 GEANY_API_SYMBOL
506 gint utils_str_casecmp(const gchar *s1, const gchar *s2)
508 gchar *tmp1, *tmp2;
509 gint result;
511 g_return_val_if_fail(s1 != NULL, 1);
512 g_return_val_if_fail(s2 != NULL, -1);
514 /* ensure strings are UTF-8 and lowercase */
515 tmp1 = utf8_strdown(s1);
516 if (! tmp1)
517 return 1;
518 tmp2 = utf8_strdown(s2);
519 if (! tmp2)
521 g_free(tmp1);
522 return -1;
525 /* compare */
526 result = strcmp(tmp1, tmp2);
528 g_free(tmp1);
529 g_free(tmp2);
530 return result;
535 * Truncates the input string to a given length.
536 * Characters are removed from the middle of the string, so the start and the end of string
537 * won't change.
539 * @param string Input string.
540 * @param truncate_length The length in characters of the resulting string.
542 * @return A copy of @a string which is truncated to @a truncate_length characters,
543 * should be freed when no longer needed.
545 * @since 0.17
547 /* This following function is taken from Gedit. */
548 GEANY_API_SYMBOL
549 gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
551 GString *truncated;
552 guint length;
553 guint n_chars;
554 guint num_left_chars;
555 guint right_offset;
556 guint delimiter_length;
557 const gchar *delimiter = "\342\200\246";
559 g_return_val_if_fail(string != NULL, NULL);
561 length = strlen(string);
563 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
565 /* It doesn't make sense to truncate strings to less than the size of the delimiter plus 2
566 * characters (one on each side) */
567 delimiter_length = g_utf8_strlen(delimiter, -1);
568 if (truncate_length < (delimiter_length + 2))
569 return g_strdup(string);
571 n_chars = g_utf8_strlen(string, length);
573 /* Make sure the string is not already small enough. */
574 if (n_chars <= truncate_length)
575 return g_strdup (string);
577 /* Find the 'middle' where the truncation will occur. */
578 num_left_chars = (truncate_length - delimiter_length) / 2;
579 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
581 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
582 g_string_append(truncated, delimiter);
583 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
585 return g_string_free(truncated, FALSE);
590 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
591 * or if @a a and @a b refer to valid strings which are equal.
593 * @param a @nullable Pointer to first string or @c NULL.
594 * @param b @nullable Pointer to second string or @c NULL.
596 * @return @c TRUE if @a a equals @a b, else @c FALSE.
598 GEANY_API_SYMBOL
599 gboolean utils_str_equal(const gchar *a, const gchar *b)
601 /* (taken from libexo from os-cillation) */
602 if (a == NULL && b == NULL) return TRUE;
603 else if (a == NULL || b == NULL) return FALSE;
605 return strcmp(a, b) == 0;
610 * Removes the extension from @a filename and return the result in a newly allocated string.
612 * @param filename The filename to operate on.
614 * @return A newly-allocated string, should be freed when no longer needed.
616 GEANY_API_SYMBOL
617 gchar *utils_remove_ext_from_filename(const gchar *filename)
619 gchar *last_dot;
620 gchar *result;
621 gsize len;
623 g_return_val_if_fail(filename != NULL, NULL);
625 last_dot = strrchr(filename, '.');
626 if (! last_dot)
627 return g_strdup(filename);
629 len = (gsize) (last_dot - filename);
630 result = g_malloc(len + 1);
631 memcpy(result, filename, len);
632 result[len] = 0;
634 return result;
638 gchar utils_brace_opposite(gchar ch)
640 switch (ch)
642 case '(': return ')';
643 case ')': return '(';
644 case '[': return ']';
645 case ']': return '[';
646 case '{': return '}';
647 case '}': return '{';
648 case '<': return '>';
649 case '>': return '<';
650 default: return '\0';
655 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
656 * Returns 0 if it can be written, otherwise it returns errno */
657 gint utils_is_file_writable(const gchar *locale_filename)
659 gchar *file;
660 gint ret;
662 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
663 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
664 /* get the file's directory to check for write permission if it doesn't yet exist */
665 file = g_path_get_dirname(locale_filename);
666 else
667 file = g_strdup(locale_filename);
669 #ifdef G_OS_WIN32
670 /* use _waccess on Windows, access() doesn't accept special characters */
671 ret = win32_check_write_permission(file);
672 #else
674 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
675 * errno only when access() explicitly returns an error */
676 if (access(file, R_OK | W_OK) != 0)
677 ret = errno;
678 else
679 ret = 0;
680 #endif
681 g_free(file);
682 return ret;
686 /* Replaces all occurrences of needle in haystack with replacement.
687 * Warning: *haystack must be a heap address; it may be freed and reassigned.
688 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
689 * than @a needle.
690 * All strings have to be NULL-terminated.
691 * See utils_string_replace_all() for details. */
692 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
694 GString *str;
696 g_return_if_fail(*haystack != NULL);
698 str = g_string_new(*haystack);
700 g_free(*haystack);
701 utils_string_replace_all(str, needle, replacement);
703 *haystack = g_string_free(str, FALSE);
707 gint utils_strpos(const gchar *haystack, const gchar *needle)
709 const gchar *sub;
711 if (! *needle)
712 return -1;
714 sub = strstr(haystack, needle);
715 if (! sub)
716 return -1;
718 return sub - haystack;
723 * Retrieves a formatted date/time string from strftime().
724 * This function should be preferred to directly calling strftime() since this function
725 * works on UTF-8 encoded strings.
727 * @param format The format string to pass to strftime(3). See the strftime(3)
728 * documentation for details, in UTF-8 encoding.
729 * @param time_to_use @nullable The date/time to use, in time_t format or @c NULL to use the current time.
731 * @return A newly-allocated string, should be freed when no longer needed.
733 * @since 0.16
735 GEANY_API_SYMBOL
736 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
738 const struct tm *tm;
739 static gchar date[1024];
740 gchar *locale_format;
741 gsize len;
743 g_return_val_if_fail(format != NULL, NULL);
745 if (! g_utf8_validate(format, -1, NULL))
747 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
748 if (locale_format == NULL)
749 return NULL;
751 else
752 locale_format = g_strdup(format);
754 if (time_to_use != NULL)
755 tm = localtime(time_to_use);
756 else
758 time_t tp = time(NULL);
759 tm = localtime(&tp);
762 len = strftime(date, 1024, locale_format, tm);
763 g_free(locale_format);
764 if (len == 0)
765 return NULL;
767 if (! g_utf8_validate(date, len, NULL))
768 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
769 else
770 return g_strdup(date);
774 gchar *utils_get_initials(const gchar *name)
776 gint i = 1, j = 1;
777 gchar *initials = g_malloc0(5);
779 initials[0] = name[0];
780 while (name[i] != '\0' && j < 4)
782 if (name[i] == ' ' && name[i + 1] != ' ')
784 initials[j++] = name[i + 1];
786 i++;
788 return initials;
793 * Wraps g_key_file_get_integer() to add a default value argument.
795 * @param config A GKeyFile object.
796 * @param section The group name to look in for the key.
797 * @param key The key to find.
798 * @param default_value The default value which will be returned when @a section or @a key
799 * don't exist.
801 * @return The value associated with @a key as an integer, or the given default value if the value
802 * could not be retrieved.
804 GEANY_API_SYMBOL
805 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
806 const gint default_value)
808 gint tmp;
809 GError *error = NULL;
811 g_return_val_if_fail(config, default_value);
813 tmp = g_key_file_get_integer(config, section, key, &error);
814 if (error)
816 g_error_free(error);
817 return default_value;
819 return tmp;
824 * Wraps g_key_file_get_boolean() to add a default value argument.
826 * @param config A GKeyFile object.
827 * @param section The group name to look in for the key.
828 * @param key The key to find.
829 * @param default_value The default value which will be returned when @c section or @c key
830 * don't exist.
832 * @return The value associated with @a key as a boolean, or the given default value if the value
833 * could not be retrieved.
835 GEANY_API_SYMBOL
836 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
837 const gboolean default_value)
839 gboolean tmp;
840 GError *error = NULL;
842 g_return_val_if_fail(config, default_value);
844 tmp = g_key_file_get_boolean(config, section, key, &error);
845 if (error)
847 g_error_free(error);
848 return default_value;
850 return tmp;
855 * Wraps g_key_file_get_string() to add a default value argument.
857 * @param config A GKeyFile object.
858 * @param section The group name to look in for the key.
859 * @param key The key to find.
860 * @param default_value The default value which will be returned when @a section or @a key
861 * don't exist.
863 * @return A newly allocated string, either the value for @a key or a copy of the given
864 * default value if it could not be retrieved.
866 GEANY_API_SYMBOL
867 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
868 const gchar *default_value)
870 gchar *tmp;
872 g_return_val_if_fail(config, g_strdup(default_value));
874 tmp = g_key_file_get_string(config, section, key, NULL);
875 if (!tmp)
877 return g_strdup(default_value);
879 return tmp;
883 gchar *utils_get_hex_from_color(GdkColor *color)
885 g_return_val_if_fail(color != NULL, NULL);
887 return g_strdup_printf("#%02X%02X%02X",
888 (guint) (utils_scale_round(color->red / 65535.0, 255)),
889 (guint) (utils_scale_round(color->green / 65535.0, 255)),
890 (guint) (utils_scale_round(color->blue / 65535.0, 255)));
894 /* Get directory from current file in the notebook.
895 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
896 * Returned string is in UTF-8 encoding */
897 gchar *utils_get_current_file_dir_utf8(void)
899 GeanyDocument *doc = document_get_current();
901 if (doc != NULL)
903 /* get current filename */
904 const gchar *cur_fname = doc->file_name;
906 if (cur_fname != NULL)
908 /* get folder part from current filename */
909 return g_path_get_dirname(cur_fname); /* returns "." if no path */
913 return NULL; /* no file open */
917 /* very simple convenience function */
918 void utils_beep(void)
920 if (prefs.beep_on_errors)
921 gdk_beep();
925 /* taken from busybox, thanks */
926 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
927 gulong display_unit)
929 /* The code will adjust for additional (appended) units. */
930 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
931 static const gchar fmt[] = "%Lu %c%c";
932 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
934 guint64 val;
935 gint frac;
936 const gchar *u;
937 const gchar *f;
939 u = zero_and_units;
940 f = fmt;
941 frac = 0;
943 val = size * block_size;
944 if (val == 0)
945 return g_strdup(u);
947 if (display_unit)
949 val += display_unit/2; /* Deal with rounding. */
950 val /= display_unit; /* Don't combine with the line above!!! */
952 else
954 ++u;
955 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
957 f = fmt_tenths;
958 ++u;
959 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
960 val /= 1024;
962 if (frac >= 10)
963 { /* We need to round up here. */
964 ++val;
965 frac = 0;
969 /* If f==fmt then 'frac' and 'u' are ignored. */
970 return g_strdup_printf(f, val, frac, *u, 'b');
974 /* converts a color representation using gdk_color_parse(), with additional
975 * support of the "0x" prefix as a synonym for "#" */
976 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
978 gchar buf[64] = {0};
980 g_return_val_if_fail(spec != NULL, -1);
982 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
984 /* convert to # format for GDK to understand it */
985 buf[0] = '#';
986 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
987 spec = buf;
990 return gdk_color_parse(spec, color);
994 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
995 * returns a 24 bits BGR color, or -1 on failure */
996 gint utils_color_to_bgr(const GdkColor *c)
998 g_return_val_if_fail(c != NULL, -1);
999 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
1003 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
1004 * utils_color_to_bgr() */
1005 gint utils_parse_color_to_bgr(const gchar *spec)
1007 GdkColor color;
1008 if (utils_parse_color(spec, &color))
1009 return utils_color_to_bgr(&color);
1010 else
1011 return -1;
1015 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
1016 gchar *utils_get_current_time_string(void)
1018 const time_t tp = time(NULL);
1019 const struct tm *tmval = localtime(&tp);
1020 gchar *result = g_malloc0(9);
1022 strftime(result, 9, "%H:%M:%S", tmval);
1023 result[8] = '\0';
1024 return result;
1028 GIOChannel *utils_set_up_io_channel(
1029 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1031 GIOChannel *ioc;
1032 /*const gchar *encoding;*/
1034 #ifdef G_OS_WIN32
1035 ioc = g_io_channel_win32_new_fd(fd);
1036 #else
1037 ioc = g_io_channel_unix_new(fd);
1038 #endif
1040 if (nblock)
1041 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1043 g_io_channel_set_encoding(ioc, NULL, NULL);
1045 if (! g_get_charset(&encoding))
1046 { // hope this works reliably
1047 GError *error = NULL;
1048 g_io_channel_set_encoding(ioc, encoding, &error);
1049 if (error)
1051 geany_debug("%s: %s", G_STRFUNC, error->message);
1052 g_error_free(error);
1053 return ioc;
1057 /* "auto-close" ;-) */
1058 g_io_channel_set_close_on_unref(ioc, TRUE);
1060 g_io_add_watch(ioc, cond, func, data);
1061 g_io_channel_unref(ioc);
1063 return ioc;
1067 /* Contributed by Stefan Oltmanns, thanks.
1068 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1069 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1070 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1072 gsize i, j, len;
1073 guint unicodechar;
1075 g_return_val_if_fail(string != NULL, FALSE);
1077 j = 0;
1078 len = strlen(string);
1079 for (i = 0; i < len; i++)
1081 if (string[i]=='\\')
1083 if (i++ >= strlen(string))
1085 return FALSE;
1087 switch (string[i])
1089 case '\\':
1090 if (keep_backslash)
1091 string[j++] = '\\';
1092 string[j] = '\\';
1093 break;
1094 case 'n':
1095 string[j] = '\n';
1096 break;
1097 case 'r':
1098 string[j] = '\r';
1099 break;
1100 case 't':
1101 string[j] = '\t';
1102 break;
1103 #if 0
1104 case 'x': /* Warning: May produce illegal utf-8 string! */
1105 i += 2;
1106 if (i >= strlen(string))
1108 return FALSE;
1110 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1111 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1112 else return FALSE;
1113 string[j] <<= 4;
1114 if (isdigit(string[i])) string[j] |= string[i] - 48;
1115 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1116 else return FALSE;
1117 break;
1118 #endif
1119 case 'u':
1121 i += 2;
1122 if (i >= strlen(string))
1124 return FALSE;
1126 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1127 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1128 else return FALSE;
1129 unicodechar <<= 4;
1130 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1131 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1132 else return FALSE;
1133 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1134 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1136 i += 2;
1137 unicodechar <<= 8;
1138 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1139 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1140 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1141 else unicodechar |= tolower(string[i])-87;
1143 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1144 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1146 i += 2;
1147 unicodechar <<= 8;
1148 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1149 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1150 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1151 else unicodechar |= tolower(string[i])-87;
1153 if (unicodechar < 0x80)
1155 string[j] = unicodechar;
1157 else if (unicodechar < 0x800)
1159 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1160 j++;
1161 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1163 else if (unicodechar < 0x10000)
1165 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1166 j++;
1167 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1168 j++;
1169 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1171 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1173 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1174 j++;
1175 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1176 j++;
1177 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1178 j++;
1179 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1181 else
1183 return FALSE;
1185 break;
1187 default:
1188 /* unnecessary escapes are allowed */
1189 if (keep_backslash)
1190 string[j++] = '\\';
1191 string[j] = string[i];
1194 else
1196 string[j] = string[i];
1198 j++;
1200 while (j < i)
1202 string[j] = 0;
1203 j++;
1205 return TRUE;
1209 /* Wraps a string in place, replacing a space with a newline character.
1210 * wrapstart is the minimum position to start wrapping or -1 for default */
1211 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1213 gchar *pos, *linestart;
1214 gboolean ret = FALSE;
1216 if (wrapstart < 0)
1217 wrapstart = 80;
1219 for (pos = linestart = string; *pos != '\0'; pos++)
1221 if (pos - linestart >= wrapstart && *pos == ' ')
1223 *pos = '\n';
1224 linestart = pos;
1225 ret = TRUE;
1228 return ret;
1233 * Converts the given UTF-8 encoded string into locale encoding.
1234 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1236 * @param utf8_text UTF-8 encoded text.
1238 * @return The converted string in locale encoding, or a copy of the input string if conversion
1239 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1241 GEANY_API_SYMBOL
1242 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1244 #ifdef G_OS_WIN32
1245 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1246 * which would result in wrongly converted strings */
1247 return g_strdup(utf8_text);
1248 #else
1249 gchar *locale_text;
1251 if (! utf8_text)
1252 return NULL;
1253 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1254 if (locale_text == NULL)
1255 locale_text = g_strdup(utf8_text);
1256 return locale_text;
1257 #endif
1262 * Converts the given string (in locale encoding) into UTF-8 encoding.
1263 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1265 * @param locale_text Text in locale encoding.
1267 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1268 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1270 GEANY_API_SYMBOL
1271 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1273 #ifdef G_OS_WIN32
1274 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1275 * which would result in wrongly converted strings */
1276 return g_strdup(locale_text);
1277 #else
1278 gchar *utf8_text;
1280 if (! locale_text)
1281 return NULL;
1282 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1283 if (utf8_text == NULL)
1284 utf8_text = g_strdup(locale_text);
1285 return utf8_text;
1286 #endif
1290 /* Pass pointers to free after arg_count.
1291 * The last argument must be NULL as an extra check that arg_count is correct. */
1292 void utils_free_pointers(gsize arg_count, ...)
1294 va_list a;
1295 gsize i;
1296 gpointer ptr;
1298 va_start(a, arg_count);
1299 for (i = 0; i < arg_count; i++)
1301 ptr = va_arg(a, gpointer);
1302 g_free(ptr);
1304 ptr = va_arg(a, gpointer);
1305 if (ptr)
1306 g_warning("Wrong arg_count!");
1307 va_end(a);
1311 /* Creates a string array deep copy of a series of non-NULL strings.
1312 * The first argument is nothing special and must not be NULL.
1313 * The list must be terminated with NULL. */
1314 GEANY_EXPORT_SYMBOL
1315 gchar **utils_strv_new(const gchar *first, ...)
1317 gsize strvlen, i;
1318 va_list args;
1319 gchar *str;
1320 gchar **strv;
1322 g_return_val_if_fail(first != NULL, NULL);
1324 strvlen = 1; /* for first argument */
1326 /* count other arguments */
1327 va_start(args, first);
1328 for (; va_arg(args, gchar*) != NULL; strvlen++);
1329 va_end(args);
1331 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1332 strv[0] = g_strdup(first);
1334 va_start(args, first);
1335 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1337 strv[i] = g_strdup(str);
1339 va_end(args);
1341 strv[i] = NULL;
1342 return strv;
1347 * Creates a directory if it doesn't already exist.
1348 * Creates intermediate parent directories as needed, too.
1349 * The permissions of the created directory are set 0700.
1351 * @param path The path of the directory to create, in locale encoding.
1352 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1354 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1355 * failed operation is returned.
1357 GEANY_API_SYMBOL
1358 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1360 gint mode = 0700;
1361 gint result;
1363 if (path == NULL || strlen(path) == 0)
1364 return EFAULT;
1366 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1367 if (result != 0)
1368 return errno;
1369 return 0;
1374 * Gets a list of files from the specified directory.
1375 * Locale encoding is expected for @a path and used for the file list. The list and the data
1376 * in the list should be freed after use, e.g.:
1377 * @code
1378 * g_slist_foreach(list, (GFunc) g_free, NULL);
1379 * g_slist_free(list); @endcode
1381 * @note If you don't need a list you should use the foreach_dir() macro instead -
1382 * it's more efficient.
1384 * @param path The path of the directory to scan, in locale encoding.
1385 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1386 * will use more memory.
1387 * @param sort Whether to sort alphabetically (UTF-8 safe).
1388 * @param error The location for storing a possible error, or @c NULL.
1390 * @return @elementtype{filename} @transfer{full} @nullable A newly allocated list or @c NULL if
1391 * no files were found. The list and its data should be freed when no longer needed.
1392 * @see utils_get_file_list().
1394 GEANY_API_SYMBOL
1395 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1397 GSList *list = NULL;
1398 GDir *dir;
1399 const gchar *filename;
1401 if (error)
1402 *error = NULL;
1403 g_return_val_if_fail(path != NULL, NULL);
1405 dir = g_dir_open(path, 0, error);
1406 if (dir == NULL)
1407 return NULL;
1409 foreach_dir(filename, dir)
1411 list = g_slist_prepend(list, full_path ?
1412 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1414 g_dir_close(dir);
1415 /* sorting last is quicker than on insertion */
1416 if (sort)
1417 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1418 return list;
1423 * Gets a sorted list of files from the specified directory.
1424 * Locale encoding is expected for @a path and used for the file list. The list and the data
1425 * in the list should be freed after use, e.g.:
1426 * @code
1427 * g_slist_foreach(list, (GFunc) g_free, NULL);
1428 * g_slist_free(list); @endcode
1430 * @param path The path of the directory to scan, in locale encoding.
1431 * @param length The location to store the number of non-@c NULL data items in the list,
1432 * unless @c NULL.
1433 * @param error The location for storing a possible error, or @c NULL.
1435 * @return @elementtype{filename} @transfer{full} @nullable A newly allocated list or @c NULL
1436 * if no files were found. The list and its data should be freed when no longer needed.
1437 * @see utils_get_file_list_full().
1439 GEANY_API_SYMBOL
1440 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1442 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1444 if (length)
1445 *length = g_slist_length(list);
1446 return list;
1450 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1451 gboolean utils_str_has_upper(const gchar *str)
1453 gunichar c;
1455 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1456 return FALSE;
1458 while (*str != '\0')
1460 c = g_utf8_get_char(str);
1461 /* check only letters and stop once the first non-capital was found */
1462 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1463 return TRUE;
1464 /* FIXME don't write a const string */
1465 str = g_utf8_next_char(str);
1467 return FALSE;
1471 /* end can be -1 for haystack->len.
1472 * returns: position of found text or -1. */
1473 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1475 gint pos;
1477 g_return_val_if_fail(haystack != NULL, -1);
1478 if (haystack->len == 0)
1479 return -1;
1481 g_return_val_if_fail(start >= 0, -1);
1482 if (start >= (gint)haystack->len)
1483 return -1;
1485 g_return_val_if_fail(!EMPTY(needle), -1);
1487 if (end < 0)
1488 end = haystack->len;
1490 pos = utils_strpos(haystack->str + start, needle);
1491 if (pos == -1)
1492 return -1;
1494 pos += start;
1495 if (pos >= end)
1496 return -1;
1497 return pos;
1501 /* Replaces @len characters from offset @a pos.
1502 * len can be -1 to replace the remainder of @a str.
1503 * returns: pos + strlen(replace). */
1504 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1506 g_string_erase(str, pos, len);
1507 if (replace)
1509 g_string_insert(str, pos, replace);
1510 pos += strlen(replace);
1512 return pos;
1517 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1518 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1519 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1521 * @param haystack The input string to operate on. This string is modified in place.
1522 * @param needle The string which should be replaced.
1523 * @param replace The replacement for @a needle.
1525 * @return Number of replacements made.
1527 GEANY_API_SYMBOL
1528 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1530 guint count = 0;
1531 gint pos = 0;
1532 gsize needle_length = strlen(needle);
1534 while (1)
1536 pos = utils_string_find(haystack, pos, -1, needle);
1538 if (pos == -1)
1539 break;
1541 pos = utils_string_replace(haystack, pos, needle_length, replace);
1542 count++;
1544 return count;
1549 * Replaces only the first occurrence of @a needle in @a haystack
1550 * with @a replace.
1551 * For details, see utils_string_replace_all().
1553 * @param haystack The input string to operate on. This string is modified in place.
1554 * @param needle The string which should be replaced.
1555 * @param replace The replacement for @a needle.
1557 * @return Number of replacements made.
1559 * @since 0.16
1561 GEANY_API_SYMBOL
1562 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1564 gint pos = utils_string_find(haystack, 0, -1, needle);
1566 if (pos == -1)
1567 return 0;
1569 utils_string_replace(haystack, pos, strlen(needle), replace);
1570 return 1;
1574 /* Similar to g_regex_replace but allows matching a subgroup.
1575 * match_num: which match to replace, 0 for whole match.
1576 * literal: FALSE to interpret escape sequences in @a replace.
1577 * returns: number of replacements.
1578 * bug: replaced text can affect matching of ^ or \b */
1579 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1580 guint match_num, const gchar *replace, gboolean literal)
1582 GMatchInfo *minfo;
1583 guint ret = 0;
1584 gint start = 0;
1586 g_assert(literal); /* escapes not implemented yet */
1587 g_return_val_if_fail(replace, 0);
1589 /* ensure haystack->str is not null */
1590 if (haystack->len == 0)
1591 return 0;
1593 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1594 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1596 gint end, len;
1598 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1599 len = end - start;
1600 utils_string_replace(haystack, start, len, replace);
1601 ret++;
1603 /* skip past whole match */
1604 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1605 start = end - len + strlen(replace);
1606 g_match_info_free(minfo);
1608 g_match_info_free(minfo);
1609 return ret;
1613 /* Get project or default startup directory (if set), or NULL. */
1614 const gchar *utils_get_default_dir_utf8(void)
1616 if (app->project && !EMPTY(app->project->base_path))
1618 return app->project->base_path;
1621 if (!EMPTY(prefs.default_open_path))
1623 return prefs.default_open_path;
1625 return NULL;
1630 * Wraps @c spawn_sync(), which see.
1632 * @param dir @nullable The child's current working directory, or @c NULL to inherit parent's.
1633 * @param argv The child's argument vector.
1634 * @param env @nullable The child's environment, or @c NULL to inherit parent's.
1635 * @param flags Ignored.
1636 * @param child_setup @girskip Ignored.
1637 * @param user_data @girskip Ignored.
1638 * @param std_out @out @optional The return location for child output, or @c NULL.
1639 * @param std_err @out @optional The return location for child error messages, or @c NULL.
1640 * @param exit_status @out @optional The child exit status, as returned by waitpid(), or @c NULL.
1641 * @param error The return location for error or @c NULL.
1643 * @return @c TRUE on success, @c FALSE if an error was set.
1645 GEANY_API_SYMBOL
1646 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1647 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1648 gchar **std_err, gint *exit_status, GError **error)
1650 GString *output = std_out ? g_string_new(NULL) : NULL;
1651 GString *errors = std_err ? g_string_new(NULL) : NULL;
1652 gboolean result = spawn_sync(dir, NULL, argv, env, NULL, output, errors, exit_status, error);
1654 if (std_out)
1655 *std_out = g_string_free(output, !result);
1657 if (std_err)
1658 *std_err = g_string_free(errors, !result);
1660 return result;
1665 * Wraps @c spawn_async(), which see.
1667 * @param dir @nullable The child's current working directory, or @c NULL to inherit parent's.
1668 * @param argv The child's argument vector.
1669 * @param env @nullable The child's environment, or @c NULL to inherit parent's.
1670 * @param flags Ignored.
1671 * @param child_setup @girskip Ignored.
1672 * @param user_data Ignored.
1673 * @param child_pid @out @nullable The return location for child process ID, or @c NULL.
1674 * @param error The return location for error or @c NULL.
1676 * @return @c TRUE on success, @c FALSE if an error was set.
1678 GEANY_API_SYMBOL
1679 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1680 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1681 GError **error)
1683 return spawn_async(dir, NULL, argv, env, child_pid, error);
1687 /* Returns "file:///" on Windows, "file://" everywhere else */
1688 const gchar *utils_get_uri_file_prefix(void)
1690 #ifdef G_OS_WIN32
1691 return "file:///";
1692 #else
1693 return "file://";
1694 #endif
1698 /* Retrieves the path for the given URI.
1699 * It returns:
1700 * - the path which was determined by g_filename_from_uri() or GIO
1701 * - NULL if the URI is non-local and gvfs-fuse is not installed
1702 * - a new copy of 'uri' if it is not an URI. */
1703 gchar *utils_get_path_from_uri(const gchar *uri)
1705 gchar *locale_filename;
1707 g_return_val_if_fail(uri != NULL, NULL);
1709 if (! utils_is_uri(uri))
1710 return g_strdup(uri);
1712 /* this will work only for 'file://' URIs */
1713 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1714 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1715 if (locale_filename == NULL)
1717 GFile *file = g_file_new_for_uri(uri);
1718 locale_filename = g_file_get_path(file);
1719 g_object_unref(file);
1720 if (locale_filename == NULL)
1722 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1723 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1727 return locale_filename;
1731 gboolean utils_is_uri(const gchar *uri)
1733 g_return_val_if_fail(uri != NULL, FALSE);
1735 return (strstr(uri, "://") != NULL);
1739 /* path should be in locale encoding */
1740 gboolean utils_is_remote_path(const gchar *path)
1742 g_return_val_if_fail(path != NULL, FALSE);
1744 /* if path is an URI and it doesn't start "file://", we take it as remote */
1745 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1746 return TRUE;
1748 #ifndef G_OS_WIN32
1750 static gchar *fuse_path = NULL;
1751 static gsize len = 0;
1753 if (G_UNLIKELY(fuse_path == NULL))
1755 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1756 len = strlen(fuse_path);
1758 /* Comparing the file path against a hardcoded path is not the most elegant solution
1759 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1760 * proper GFile objects for Fuse paths, but it only does in future GVFS
1761 * versions (gvfs 1.1.1). */
1762 return (strncmp(path, fuse_path, len) == 0);
1764 #endif
1766 return FALSE;
1770 /* Remove all relative and untidy elements from the path of @a filename.
1771 * @param filename must be a valid absolute path.
1772 * @see utils_get_real_path() - also resolves links. */
1773 void utils_tidy_path(gchar *filename)
1775 GString *str;
1776 const gchar *needle;
1777 gboolean preserve_double_backslash = FALSE;
1779 g_return_if_fail(g_path_is_absolute(filename));
1781 str = g_string_new(filename);
1783 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1784 preserve_double_backslash = TRUE;
1786 #ifdef G_OS_WIN32
1787 /* using MSYS we can get Unix-style separators */
1788 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1789 #endif
1790 /* replace "/./" and "//" */
1791 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1792 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1794 if (preserve_double_backslash)
1795 g_string_prepend(str, "\\");
1797 /* replace "/../" */
1798 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1799 while (1)
1801 const gchar *c = strstr(str->str, needle);
1802 if (c == NULL)
1803 break;
1804 else
1806 gssize pos, sub_len;
1808 pos = c - str->str;
1809 if (pos <= 3)
1810 break; /* bad path */
1812 /* replace "/../" */
1813 g_string_erase(str, pos, strlen(needle));
1814 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1816 /* search for last "/" before found "/../" */
1817 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1818 sub_len = pos - (c - str->str);
1819 if (! c)
1820 break; /* bad path */
1822 pos = c - str->str; /* position of previous "/" */
1823 g_string_erase(str, pos, sub_len);
1826 if (str->len <= strlen(filename))
1827 memcpy(filename, str->str, str->len + 1);
1828 else
1829 g_warn_if_reached();
1830 g_string_free(str, TRUE);
1835 * Removes characters from a string, in place.
1837 * @param string String to search.
1838 * @param chars Characters to remove.
1840 * @return @a string - return value is only useful when nesting function calls, e.g.:
1841 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1843 * @see @c g_strdelimit.
1845 GEANY_API_SYMBOL
1846 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1848 const gchar *r;
1849 gchar *w = string;
1851 g_return_val_if_fail(string, NULL);
1852 if (G_UNLIKELY(EMPTY(chars)))
1853 return string;
1855 foreach_str(r, string)
1857 if (!strchr(chars, *r))
1858 *w++ = *r;
1860 *w = 0x0;
1861 return string;
1865 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1866 GSList *utils_get_config_files(const gchar *subdir)
1868 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1869 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1870 GSList *syslist, *node;
1872 if (!list)
1874 utils_mkdir(path, FALSE);
1876 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1877 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1878 /* merge lists */
1879 list = g_slist_concat(list, syslist);
1881 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1882 /* remove duplicates (next to each other after sorting) */
1883 foreach_slist(node, list)
1885 if (node->next && utils_str_equal(node->next->data, node->data))
1887 GSList *old = node->next;
1889 g_free(old->data);
1890 node->next = old->next;
1891 g_slist_free1(old);
1894 g_free(path);
1895 return list;
1899 /* Suffix can be NULL or a string which should be appended to the Help URL like
1900 * an anchor link, e.g. "#some_anchor". */
1901 gchar *utils_get_help_url(const gchar *suffix)
1903 gchar *uri;
1904 const gchar *uri_file_prefix = utils_get_uri_file_prefix();
1905 gint skip = strlen(uri_file_prefix);
1907 uri = g_strconcat(uri_file_prefix, app->docdir, "/index.html", NULL);
1908 #ifdef G_OS_WIN32
1909 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1910 #endif
1912 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1913 { /* fall back to online documentation if it is not found on the hard disk */
1914 g_free(uri);
1915 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1918 if (suffix != NULL)
1920 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1923 return uri;
1927 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1929 const gchar **p;
1931 for (p = haystack; *p != NULL; ++p)
1933 if (utils_str_equal(*p, needle))
1934 return TRUE;
1936 return FALSE;
1941 * Copies the current environment into a new array.
1942 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1943 * All further arguments are key, value pairs of variables which should be added to
1944 * the environment.
1946 * The argument list must be @c NULL-terminated.
1948 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1949 * @param first_varname Name of the first variable to copy into the new array.
1950 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1952 * @return @transfer{full} The new environment array. Use @c g_strfreev() to free it.
1954 GEANY_API_SYMBOL
1955 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1957 gchar **result;
1958 gchar **p;
1959 gchar **env;
1960 va_list args;
1961 const gchar *key, *value;
1962 guint n, o;
1964 /* count the additional variables */
1965 va_start(args, first_varname);
1966 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1967 va_end(args);
1968 /* the passed arguments should be even (key, value pairs) */
1969 g_return_val_if_fail(o % 2 == 0, NULL);
1971 o /= 2;
1973 /* get all the environ variables */
1974 env = g_listenv();
1976 /* create an array large enough to hold the new environment */
1977 n = g_strv_length(env);
1978 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1979 result = g_new(gchar *, n + o + 1);
1981 /* copy the environment */
1982 for (n = 0, p = env; *p != NULL; ++p)
1984 /* copy the variable */
1985 value = g_getenv(*p);
1986 if (G_LIKELY(value != NULL))
1988 /* skip excluded variables */
1989 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1990 continue;
1992 result[n++] = g_strconcat(*p, "=", value, NULL);
1995 g_strfreev(env);
1997 /* now add additional variables */
1998 va_start(args, first_varname);
1999 key = first_varname;
2000 value = va_arg(args, gchar*);
2001 while (key != NULL)
2003 result[n++] = g_strconcat(key, "=", value, NULL);
2005 key = va_arg(args, gchar*);
2006 if (key == NULL)
2007 break;
2008 value = va_arg(args, gchar*);
2010 va_end(args);
2012 result[n] = NULL;
2014 return result;
2018 /* Joins @a first and @a second into a new string vector, freeing the originals.
2019 * The original contents are reused. */
2020 gchar **utils_strv_join(gchar **first, gchar **second)
2022 gchar **strv;
2023 gchar **rptr, **wptr;
2025 if (!first)
2026 return second;
2027 if (!second)
2028 return first;
2030 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2031 wptr = strv;
2033 foreach_strv(rptr, first)
2034 *wptr++ = *rptr;
2035 foreach_strv(rptr, second)
2036 *wptr++ = *rptr;
2038 g_free(first);
2039 g_free(second);
2040 return strv;
2043 /* * Returns the common prefix in a list of strings.
2045 * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv).
2047 * @param strv The list of strings to process.
2048 * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL.
2050 * @return The common prefix that is part of all strings (maybe empty), or NULL if an empty list
2051 * was passed in.
2053 GEANY_EXPORT_SYMBOL
2054 gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len)
2056 gsize num;
2058 if (strv_len == 0)
2059 return NULL;
2061 num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len;
2063 for (gsize i = 0; strv[0][i]; i++)
2065 for (gsize j = 1; j < num; j++)
2067 if (strv[j][i] != strv[0][i])
2069 /* return prefix on first mismatch */
2070 return g_strndup(strv[0], i);
2075 return g_strdup(strv[0]);
2079 /* * Returns the longest common substring in a list of strings.
2081 * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv).
2083 * @param strv The list of strings to process.
2084 * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL.
2086 * @return The common prefix that is part of all strings.
2088 GEANY_EXPORT_SYMBOL
2089 gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len, const gchar *delim)
2091 gchar *first, *_sub, *sub;
2092 gsize num;
2093 gsize n_chars;
2094 gsize len;
2095 gsize max = 0;
2096 char *lcs;
2097 gsize found;
2099 if (strv_len == 0)
2100 return NULL;
2102 num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len;
2104 first = strv[0];
2105 len = strlen(first);
2107 /* sub is the working area where substrings from first are copied to */
2108 sub = g_malloc(len+1);
2109 lcs = g_strdup("");
2110 foreach_str(_sub, first)
2112 gsize chars_left = len - (_sub - first);
2113 /* No point in continuing if the remainder is too short */
2114 if (max > chars_left)
2115 break;
2116 /* If delimiters are given, we only need to compare substrings which start and
2117 * end with one of them, so skip any non-delim chars at front ... */
2118 if (NZV(delim) && (strchr(delim, _sub[0]) == NULL))
2119 continue;
2120 for (n_chars = 1; n_chars <= chars_left; n_chars++)
2122 if (NZV(delim))
2123 { /* ... and advance to the next delim char at the end, if any */
2124 if (!_sub[n_chars] || strchr(delim, _sub[n_chars]) == NULL)
2125 continue;
2126 n_chars += 1;
2128 g_strlcpy(sub, _sub, n_chars+1);
2129 found = 1;
2130 for (gsize i = 1; i < num; i++)
2132 if (strstr(strv[i], sub) == NULL)
2133 break;
2134 found++;
2136 if (found == num && n_chars > max)
2138 max = n_chars;
2139 SETPTR(lcs, g_strdup(sub));
2143 g_free(sub);
2145 return lcs;
2149 /** Transform file names in a list to be shorter.
2151 * This function takes a list of file names (probably with absolute paths), and
2152 * transforms the paths such that they are short but still unique. This is intended
2153 * for dialogs which present the file list to the user, where the base name may result
2154 * in duplicates (showing the full path might be inappropriate).
2156 * The algorthm strips the common prefix (e-g. the user's home directory) and
2157 * replaces the longest common substring with an ellipsis ("...").
2159 * @param file_names @array{length=file_names_len} The list of strings to process.
2160 * @param file_names_len The number of strings contained in @a file_names. Can be -1 if it's
2161 * terminated by @c NULL.
2162 * @return @transfer{full} A newly-allocated array of transformed paths strings, terminated by
2163 @c NULL. Use @c g_strfreev() to free it.
2165 * @since 1.34 (API 239)
2167 GEANY_API_SYMBOL
2168 gchar **utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len)
2170 gsize num;
2171 gsize i;
2172 gchar *prefix, *lcs, *end;
2173 gchar **names;
2174 gsize prefix_len = 0, lcs_len = 0;
2176 if (file_names_len == 0)
2177 return g_new0(gchar *, 1);
2179 g_return_val_if_fail(file_names != NULL, NULL);
2181 num = (file_names_len == -1) ? g_strv_length(file_names) : (gsize) file_names_len;
2182 /* Always include a terminating NULL, enables easy freeing with g_strfreev()
2183 * We just copy the pointers so we can advance them here. But don't
2184 * forget to duplicate the strings before returning.
2186 names = g_new(gchar *, num + 1);
2187 memcpy(names, file_names, num * sizeof(gchar *));
2188 /* Always include a terminating NULL, enables easy freeing with g_strfreev() */
2189 names[num] = NULL;
2191 /* First: determine the common prefix, that will be stripped.
2192 /* We only want to strip full path components, including the trailing slash.
2193 * Except if the component is just "/".
2195 prefix = utils_strv_find_common_prefix(names, num);
2196 end = strrchr(prefix, G_DIR_SEPARATOR);
2197 if (end && end > prefix)
2199 prefix_len = end - prefix + 1; /* prefix_len includes the trailing slash */
2200 for (i = 0; i < num; i++)
2201 names[i] += prefix_len;
2204 /* Second: determine the longest common substring (lcs), that will be ellipsized. Again,
2205 * we look only for full path compnents so that we ellipsize between separators. This implies
2206 * that the file name cannot be ellipsized which is desirable anyway.
2208 lcs = utils_strv_find_lcs(names, num, G_DIR_SEPARATOR_S"/");
2209 if (lcs)
2211 lcs_len = strlen(lcs);
2212 /* Don't bother for tiny common parts (which are often just "." or "/"). Beware
2213 * that lcs includes the enclosing dir separators so the part must be at least 5 chars
2214 * to be eligible for ellipsizing.
2216 if (lcs_len < 7)
2217 lcs_len = 0;
2220 /* Last: build the shortened list of unique file names */
2221 for (i = 0; i < num; i++)
2223 if (lcs_len == 0)
2224 { /* no lcs, copy without prefix */
2225 names[i] = g_strdup(names[i]);
2227 else
2229 const gchar *lcs_start = strstr(names[i], lcs);
2230 const gchar *lcs_end = lcs_start + lcs_len;
2231 /* Dir seperators are included in lcs but shouldn't be elipsized. */
2232 names[i] = g_strdup_printf("%.*s...%s", (int)(lcs_start - names[i] + 1), names[i], lcs_end - 1);
2236 g_free(lcs);
2237 g_free(prefix);
2239 return names;
2243 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2244 * obviously g_date_set_parse() uses some magic.
2245 * The returned GDate object must be freed. */
2246 GDate *utils_parse_date(const gchar *input)
2248 GDate *date = g_date_new();
2250 g_date_set_parse(date, input);
2252 if (g_date_valid(date))
2253 return date;
2255 g_date_free(date);
2256 return NULL;
2260 gchar *utils_parse_and_format_build_date(const gchar *input)
2262 gchar date_buf[255];
2263 GDate *date = utils_parse_date(input);
2265 if (date != NULL)
2267 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2268 g_date_free(date);
2269 return g_strdup(date_buf);
2272 return g_strdup(input);
2276 gchar *utils_get_user_config_dir(void)
2278 #ifdef G_OS_WIN32
2279 return win32_get_user_config_dir();
2280 #else
2281 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
2282 #endif
2286 static gboolean is_osx_bundle(void)
2288 #ifdef MAC_INTEGRATION
2289 gchar *bundle_id = gtkosx_application_get_bundle_id();
2290 if (bundle_id)
2292 g_free(bundle_id);
2293 return TRUE;
2295 #endif
2296 return FALSE;
2300 const gchar *utils_resource_dir(GeanyResourceDirType type)
2302 static const gchar *resdirs[RESOURCE_DIR_COUNT] = {NULL};
2304 if (!resdirs[RESOURCE_DIR_DATA])
2306 #ifdef G_OS_WIN32
2307 gchar *prefix = win32_get_installation_dir();
2309 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "data", NULL);
2310 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2311 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2312 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2313 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2314 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
2315 g_free(prefix);
2316 #else
2317 if (is_osx_bundle())
2319 # ifdef MAC_INTEGRATION
2320 gchar *prefix = gtkosx_application_get_resource_path();
2322 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "share", "geany", NULL);
2323 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2324 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2325 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2326 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2327 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
2328 g_free(prefix);
2329 # endif
2331 else
2333 resdirs[RESOURCE_DIR_DATA] = g_build_filename(GEANY_DATADIR, "geany", NULL);
2334 resdirs[RESOURCE_DIR_ICON] = g_build_filename(GEANY_DATADIR, "icons", NULL);
2335 resdirs[RESOURCE_DIR_DOC] = g_build_filename(GEANY_DOCDIR, "html", NULL);
2336 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(GEANY_LOCALEDIR, NULL);
2337 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(GEANY_LIBDIR, "geany", NULL);
2338 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(GEANY_LIBEXECDIR, "geany", NULL);
2340 #endif
2343 return resdirs[type];
2347 void utils_start_new_geany_instance(const gchar *doc_path)
2349 const gchar *command = is_osx_bundle() ? "open" : "geany";
2350 gchar *exec_path = g_find_program_in_path(command);
2352 if (exec_path)
2354 GError *err = NULL;
2355 const gchar *argv[6]; // max args + 1
2356 gint argc = 0;
2358 argv[argc++] = exec_path;
2359 if (is_osx_bundle())
2361 argv[argc++] = "-n";
2362 argv[argc++] = "-a";
2363 argv[argc++] = "Geany";
2364 argv[argc++] = doc_path;
2366 else
2368 argv[argc++] = "-i";
2369 argv[argc++] = doc_path;
2371 argv[argc] = NULL;
2373 if (!utils_spawn_async(NULL, (gchar**) argv, NULL, 0, NULL, NULL, NULL, &err))
2375 g_printerr("Unable to open new window: %s\n", err->message);
2376 g_error_free(err);
2378 g_free(exec_path);
2380 else
2381 g_printerr("Unable to find 'geany'\n");
2386 * Get a link-dereferenced, absolute version of a file name.
2388 * This is similar to the POSIX `realpath` function when passed a
2389 * @c NULL argument.
2391 * @warning This function suffers the same problems as the POSIX
2392 * function `realpath()`, namely that it's impossible to determine
2393 * a suitable size for the returned buffer, and so it's limited to a
2394 * maximum of `PATH_MAX`.
2396 * @param file_name The file name to get the real path of.
2398 * @return A newly-allocated string containing the real path which
2399 * should be freed with `g_free()` when no longer needed, or @c NULL
2400 * if the real path cannot be obtained.
2402 * @since 1.32 (API 235)
2404 GEANY_API_SYMBOL
2405 gchar *utils_get_real_path(const gchar *file_name)
2407 return tm_get_real_path(file_name);