Add Clojure filetype
[geany-mirror.git] / src / utils.c
blob1686e8cb7293ae14bbcca0272636ee3cb2e20e83
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 #include "geany.h"
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <math.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdarg.h>
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
38 #endif
39 #ifdef HAVE_SYS_TYPES_H
40 # include <sys/types.h>
41 #endif
43 #include <glib/gstdio.h>
45 #include <gio/gio.h>
47 #include "prefs.h"
48 #include "support.h"
49 #include "document.h"
50 #include "filetypes.h"
51 #include "dialogs.h"
52 #include "win32.h"
53 #include "project.h"
54 #include "ui_utils.h"
55 #include "templates.h"
57 #include "utils.h"
60 /**
61 * Tries to open the given URI in a browser.
62 * On Windows, the system's default browser is opened.
63 * On non-Windows systems, the browser command set in the preferences dialog is used. In case
64 * that fails or it is unset, the user is asked to correct or fill it.
66 * @param uri The URI to open in the web browser.
68 * @since 0.16
69 **/
70 void utils_open_browser(const gchar *uri)
72 #ifdef G_OS_WIN32
73 g_return_if_fail(uri != NULL);
74 win32_open_browser(uri);
75 #else
76 gboolean again = TRUE;
78 g_return_if_fail(uri != NULL);
80 while (again)
82 gchar *cmdline = g_strconcat(tool_prefs.browser_cmd, " \"", uri, "\"", NULL);
84 if (g_spawn_command_line_async(cmdline, NULL))
85 again = FALSE;
86 else
88 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
89 _("Failed to spawn the configured browser command. "
90 "Please correct it or enter another one."),
91 tool_prefs.browser_cmd);
93 if (new_cmd == NULL) /* user canceled */
94 again = FALSE;
95 else
96 SETPTR(tool_prefs.browser_cmd, new_cmd);
98 g_free(cmdline);
100 #endif
104 /* taken from anjuta, to determine the EOL mode of the file */
105 gint utils_get_line_endings(const gchar* buffer, gsize size)
107 gsize i;
108 guint cr, lf, crlf, max_mode;
109 gint mode;
111 cr = lf = crlf = 0;
113 for (i = 0; i < size ; i++)
115 if (buffer[i] == 0x0a)
117 /* LF */
118 lf++;
120 else if (buffer[i] == 0x0d)
122 if (i >= (size - 1))
124 /* Last char, CR */
125 cr++;
127 else
129 if (buffer[i + 1] != 0x0a)
131 /* CR */
132 cr++;
134 else
136 /* CRLF */
137 crlf++;
139 i++;
144 /* Vote for the maximum */
145 mode = SC_EOL_LF;
146 max_mode = lf;
147 if (crlf > max_mode)
149 mode = SC_EOL_CRLF;
150 max_mode = crlf;
152 if (cr > max_mode)
154 mode = SC_EOL_CR;
155 max_mode = cr;
158 return mode;
162 gboolean utils_isbrace(gchar c, gboolean include_angles)
164 switch (c)
166 case '<':
167 case '>':
168 return include_angles;
170 case '(':
171 case ')':
172 case '{':
173 case '}':
174 case '[':
175 case ']': return TRUE;
176 default: return FALSE;
181 gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
183 switch (c)
185 case '<':
186 return include_angles;
188 case '(':
189 case '{':
190 case '[': return TRUE;
191 default: return FALSE;
197 * Writes @a text into a file named @a filename.
198 * If the file doesn't exist, it will be created.
199 * If it already exists, it will be overwritten.
201 * @warning You should use @c g_file_set_contents() instead if you don't need
202 * file permissions and other metadata to be preserved, as that always handles
203 * disk exhaustion safely.
205 * @param filename The filename of the file to write, in locale encoding.
206 * @param text The text to write into the file.
208 * @return 0 if the file was successfully written, otherwise the @c errno of the
209 * failed operation is returned.
211 gint utils_write_file(const gchar *filename, const gchar *text)
213 g_return_val_if_fail(filename != NULL, ENOENT);
214 g_return_val_if_fail(text != NULL, EINVAL);
216 if (file_prefs.use_safe_file_saving)
218 GError *error = NULL;
219 if (! g_file_set_contents(filename, text, -1, &error))
221 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
222 g_error_free(error);
223 return EIO;
226 else
228 FILE *fp;
229 gsize bytes_written, len;
230 gboolean fail = FALSE;
232 if (filename == NULL)
233 return ENOENT;
235 len = strlen(text);
236 errno = 0;
237 fp = g_fopen(filename, "w");
238 if (fp == NULL)
239 fail = TRUE;
240 else
242 bytes_written = fwrite(text, sizeof(gchar), len, fp);
244 if (len != bytes_written)
246 fail = TRUE;
247 geany_debug(
248 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
249 bytes_written, len, filename);
251 if (fclose(fp) != 0)
252 fail = TRUE;
254 if (fail)
256 geany_debug("utils_write_file(): could not write to file %s (%s)",
257 filename, g_strerror(errno));
258 return FALLBACK(errno, EIO);
261 return 0;
265 /** Searches backward through @a size bytes looking for a '<'.
266 * @param sel .
267 * @param size .
268 * @return The tag name (newly allocated) or @c NULL if no opening tag was found.
270 gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
272 const gchar *cur, *begin;
273 gsize len;
275 cur = utils_find_open_xml_tag_pos(sel, size);
276 if (cur == NULL)
277 return NULL;
279 cur++; /* skip the bracket */
280 begin = cur;
281 while (strchr(":_-.", *cur) || isalnum(*cur))
282 cur++;
284 len = (gsize)(cur - begin);
285 return len ? g_strndup(begin, len) : NULL;
289 /** Searches backward through @a size bytes looking for a '<'.
290 * @param sel .
291 * @param size .
292 * @return pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
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 _("Win (CRLF)"); break;
376 case SC_EOL_CR: return _("Mac (CR)"); break;
377 default: return _("Unix (LF)"); break;
382 const gchar *utils_get_eol_char(gint eol_mode)
384 switch (eol_mode)
386 case SC_EOL_CRLF: return "\r\n"; break;
387 case SC_EOL_CR: return "\r"; break;
388 default: return "\n"; break;
393 /* Converts line endings to @a target_eol_mode. */
394 void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
396 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
398 /* first convert data to LF only */
399 utils_string_replace_all(string, "\r\n", "\n");
400 utils_string_replace_all(string, "\r", "\n");
402 if (target_eol_mode == SC_EOL_LF)
403 return;
405 /* now convert to desired line endings */
406 utils_string_replace_all(string, "\n", eol_str);
410 gboolean utils_atob(const gchar *str)
412 if (G_UNLIKELY(str == NULL))
413 return FALSE;
414 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
415 return TRUE;
416 return FALSE;
420 /* NULL-safe version of g_path_is_absolute(). */
421 gboolean utils_is_absolute_path(const gchar *path)
423 if (G_UNLIKELY(EMPTY(path)))
424 return FALSE;
426 return g_path_is_absolute(path);
430 /* Skips root if path is absolute, do nothing otherwise.
431 * This is a relative-safe version of g_path_skip_root().
433 const gchar *utils_path_skip_root(const gchar *path)
435 const gchar *path_relative;
437 path_relative = g_path_skip_root(path);
439 return (path_relative != NULL) ? path_relative : path;
443 gdouble utils_scale_round(gdouble val, gdouble factor)
445 /*val = floor(val * factor + 0.5);*/
446 val = floor(val);
447 val = MAX(val, 0);
448 val = MIN(val, factor);
450 return val;
454 /* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
455 * returns NULL on charset conversion failure */
456 static gchar *utf8_strdown(const gchar *str)
458 gchar *down;
460 if (g_utf8_validate(str, -1, NULL))
461 down = g_utf8_strdown(str, -1);
462 else
464 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
465 if (down)
466 SETPTR(down, g_utf8_strdown(down, -1));
469 return down;
474 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
475 * It converts both strings into lowercase using g_utf8_strdown() and then compare
476 * both strings using strcmp().
477 * This is not completely accurate regarding locale-specific case sorting rules
478 * but seems to be a good compromise between correctness and performance.
480 * The input strings should be in UTF-8 or locale encoding.
482 * @param s1 Pointer to first string or @c NULL.
483 * @param s2 Pointer to second string or @c NULL.
485 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
486 * to be less than, to match, or to be greater than @a s2.
488 * @since 0.16
490 gint utils_str_casecmp(const gchar *s1, const gchar *s2)
492 gchar *tmp1, *tmp2;
493 gint result;
495 g_return_val_if_fail(s1 != NULL, 1);
496 g_return_val_if_fail(s2 != NULL, -1);
498 /* ensure strings are UTF-8 and lowercase */
499 tmp1 = utf8_strdown(s1);
500 if (! tmp1)
501 return 1;
502 tmp2 = utf8_strdown(s2);
503 if (! tmp2)
505 g_free(tmp1);
506 return -1;
509 /* compare */
510 result = strcmp(tmp1, tmp2);
512 g_free(tmp1);
513 g_free(tmp2);
514 return result;
519 * Truncates the input string to a given length.
520 * Characters are removed from the middle of the string, so the start and the end of string
521 * won't change.
523 * @param string Input string.
524 * @param truncate_length The length in characters of the resulting string.
526 * @return A copy of @a string which is truncated to @a truncate_length characters,
527 * should be freed when no longer needed.
529 * @since 0.17
531 /* This following function is taken from Gedit. */
532 gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
534 GString *truncated;
535 guint length;
536 guint n_chars;
537 guint num_left_chars;
538 guint right_offset;
539 guint delimiter_length;
540 const gchar *delimiter = "\342\200\246";
542 g_return_val_if_fail(string != NULL, NULL);
544 length = strlen(string);
546 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
548 /* It doesnt make sense to truncate strings to less than the size of the delimiter plus 2
549 * characters (one on each side) */
550 delimiter_length = g_utf8_strlen(delimiter, -1);
551 if (truncate_length < (delimiter_length + 2))
552 return g_strdup(string);
554 n_chars = g_utf8_strlen(string, length);
556 /* Make sure the string is not already small enough. */
557 if (n_chars <= truncate_length)
558 return g_strdup (string);
560 /* Find the 'middle' where the truncation will occur. */
561 num_left_chars = (truncate_length - delimiter_length) / 2;
562 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
564 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
565 g_string_append(truncated, delimiter);
566 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
568 return g_string_free(truncated, FALSE);
573 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
574 * or if @a a and @a b refer to valid strings which are equal.
576 * @param a Pointer to first string or @c NULL.
577 * @param b Pointer to second string or @c NULL.
579 * @return @c TRUE if @a a equals @a b, else @c FALSE.
581 gboolean utils_str_equal(const gchar *a, const gchar *b)
583 /* (taken from libexo from os-cillation) */
584 if (a == NULL && b == NULL) return TRUE;
585 else if (a == NULL || b == NULL) return FALSE;
587 while (*a == *b++)
588 if (*a++ == '\0')
589 return TRUE;
591 return FALSE;
596 * Removes the extension from @a filename and return the result in a newly allocated string.
598 * @param filename The filename to operate on.
600 * @return A newly-allocated string, should be freed when no longer needed.
602 gchar *utils_remove_ext_from_filename(const gchar *filename)
604 gchar *last_dot;
605 gchar *result;
606 gsize len;
608 g_return_val_if_fail(filename != NULL, NULL);
610 last_dot = strrchr(filename, '.');
611 if (! last_dot)
612 return g_strdup(filename);
614 len = (gsize) (last_dot - filename);
615 result = g_malloc(len + 1);
616 memcpy(result, filename, len);
617 result[len] = 0;
619 return result;
623 gchar utils_brace_opposite(gchar ch)
625 switch (ch)
627 case '(': return ')';
628 case ')': return '(';
629 case '[': return ']';
630 case ']': return '[';
631 case '{': return '}';
632 case '}': return '{';
633 case '<': return '>';
634 case '>': return '<';
635 default: return '\0';
640 gchar *utils_get_hostname(void)
642 #ifdef G_OS_WIN32
643 return win32_get_hostname();
644 #elif defined(HAVE_GETHOSTNAME)
645 gchar hostname[100];
646 if (gethostname(hostname, sizeof(hostname)) == 0)
647 return g_strdup(hostname);
648 #endif
649 return g_strdup("localhost");
653 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
654 * Returns 0 if it can be written, otherwise it returns errno */
655 gint utils_is_file_writable(const gchar *locale_filename)
657 gchar *file;
658 gint ret;
660 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
661 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
662 /* get the file's directory to check for write permission if it doesn't yet exist */
663 file = g_path_get_dirname(locale_filename);
664 else
665 file = g_strdup(locale_filename);
667 #ifdef G_OS_WIN32
668 /* use _waccess on Windows, access() doesn't accept special characters */
669 ret = win32_check_write_permission(file);
670 #else
672 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
673 * errno only when access() explicitly returns an error */
674 if (access(file, R_OK | W_OK) != 0)
675 ret = errno;
676 else
677 ret = 0;
678 #endif
679 g_free(file);
680 return ret;
684 /* Replaces all occurrences of needle in haystack with replacement.
685 * Warning: *haystack must be a heap address; it may be freed and reassigned.
686 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
687 * than @a needle.
688 * All strings have to be NULL-terminated.
689 * See utils_string_replace_all() for details. */
690 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
692 GString *str;
694 g_return_if_fail(*haystack != NULL);
696 str = g_string_new(*haystack);
698 g_free(*haystack);
699 utils_string_replace_all(str, needle, replacement);
701 *haystack = g_string_free(str, FALSE);
705 gint utils_strpos(const gchar *haystack, const gchar *needle)
707 const gchar *sub;
709 if (! *needle)
710 return -1;
712 sub = strstr(haystack, needle);
713 if (! sub)
714 return -1;
716 return sub - haystack;
721 * Retrieves a formatted date/time string from strftime().
722 * This function should be preferred to directly calling strftime() since this function
723 * works on UTF-8 encoded strings.
725 * @param format The format string to pass to strftime(3). See the strftime(3)
726 * documentation for details, in UTF-8 encoding.
727 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
729 * @return A newly-allocated string, should be freed when no longer needed.
731 * @since 0.16
733 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
735 const struct tm *tm;
736 static gchar date[1024];
737 gchar *locale_format;
738 gsize len;
740 g_return_val_if_fail(format != NULL, NULL);
742 if (! g_utf8_validate(format, -1, NULL))
744 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
745 if (locale_format == NULL)
746 return NULL;
748 else
749 locale_format = g_strdup(format);
751 if (time_to_use != NULL)
752 tm = localtime(time_to_use);
753 else
755 time_t tp = time(NULL);
756 tm = localtime(&tp);
759 len = strftime(date, 1024, locale_format, tm);
760 g_free(locale_format);
761 if (len == 0)
762 return NULL;
764 if (! g_utf8_validate(date, len, NULL))
765 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
766 else
767 return g_strdup(date);
771 gchar *utils_get_initials(const gchar *name)
773 gint i = 1, j = 1;
774 gchar *initials = g_malloc0(5);
776 initials[0] = name[0];
777 while (name[i] != '\0' && j < 4)
779 if (name[i] == ' ' && name[i + 1] != ' ')
781 initials[j++] = name[i + 1];
783 i++;
785 return initials;
790 * Wraps g_key_file_get_integer() to add a default value argument.
792 * @param config A GKeyFile object.
793 * @param section The group name to look in for the key.
794 * @param key The key to find.
795 * @param default_value The default value which will be returned when @a section or @a key
796 * don't exist.
798 * @return The value associated with @a key as an integer, or the given default value if the value
799 * could not be retrieved.
801 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
802 const gint default_value)
804 gint tmp;
805 GError *error = NULL;
807 g_return_val_if_fail(config, default_value);
809 tmp = g_key_file_get_integer(config, section, key, &error);
810 if (error)
812 g_error_free(error);
813 return default_value;
815 return tmp;
820 * Wraps g_key_file_get_boolean() to add a default value argument.
822 * @param config A GKeyFile object.
823 * @param section The group name to look in for the key.
824 * @param key The key to find.
825 * @param default_value The default value which will be returned when @c section or @c key
826 * don't exist.
828 * @return The value associated with @a key as a boolean, or the given default value if the value
829 * could not be retrieved.
831 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
832 const gboolean default_value)
834 gboolean tmp;
835 GError *error = NULL;
837 g_return_val_if_fail(config, default_value);
839 tmp = g_key_file_get_boolean(config, section, key, &error);
840 if (error)
842 g_error_free(error);
843 return default_value;
845 return tmp;
850 * Wraps g_key_file_get_string() to add a default value argument.
852 * @param config A GKeyFile object.
853 * @param section The group name to look in for the key.
854 * @param key The key to find.
855 * @param default_value The default value which will be returned when @a section or @a key
856 * don't exist.
858 * @return A newly allocated string, either the value for @a key or a copy of the given
859 * default value if it could not be retrieved.
861 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
862 const gchar *default_value)
864 gchar *tmp;
866 g_return_val_if_fail(config, g_strdup(default_value));
868 tmp = g_key_file_get_string(config, section, key, NULL);
869 if (!tmp)
871 return g_strdup(default_value);
873 return tmp;
877 gchar *utils_get_hex_from_color(GdkColor *color)
879 gchar *buffer = g_malloc0(9);
881 g_return_val_if_fail(color != NULL, NULL);
883 g_snprintf(buffer, 8, "#%02X%02X%02X",
884 (guint) (utils_scale_round(color->red / 256, 255)),
885 (guint) (utils_scale_round(color->green / 256, 255)),
886 (guint) (utils_scale_round(color->blue / 256, 255)));
888 return buffer;
892 guint utils_invert_color(guint color)
894 guint r, g, b;
896 r = 0xffffff - color;
897 g = 0xffffff - (color >> 8);
898 b = 0xffffff - (color >> 16);
900 return (r | (g << 8) | (b << 16));
904 /* Get directory from current file in the notebook.
905 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
906 * Returned string is in UTF-8 encoding */
907 gchar *utils_get_current_file_dir_utf8(void)
909 GeanyDocument *doc = document_get_current();
911 if (doc != NULL)
913 /* get current filename */
914 const gchar *cur_fname = doc->file_name;
916 if (cur_fname != NULL)
918 /* get folder part from current filename */
919 return g_path_get_dirname(cur_fname); /* returns "." if no path */
923 return NULL; /* no file open */
927 /* very simple convenience function */
928 void utils_beep(void)
930 if (prefs.beep_on_errors)
931 gdk_beep();
935 /* taken from busybox, thanks */
936 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
937 gulong display_unit)
939 /* The code will adjust for additional (appended) units. */
940 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
941 static const gchar fmt[] = "%Lu %c%c";
942 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
944 guint64 val;
945 gint frac;
946 const gchar *u;
947 const gchar *f;
949 u = zero_and_units;
950 f = fmt;
951 frac = 0;
953 val = size * block_size;
954 if (val == 0)
955 return g_strdup(u);
957 if (display_unit)
959 val += display_unit/2; /* Deal with rounding. */
960 val /= display_unit; /* Don't combine with the line above!!! */
962 else
964 ++u;
965 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
967 f = fmt_tenths;
968 ++u;
969 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
970 val /= 1024;
972 if (frac >= 10)
973 { /* We need to round up here. */
974 ++val;
975 frac = 0;
979 /* If f==fmt then 'frac' and 'u' are ignored. */
980 return g_strdup_printf(f, val, frac, *u, 'b');
984 static guint utils_get_value_of_hex(const gchar ch)
986 if (ch >= '0' && ch <= '9')
987 return ch - '0';
988 else if (ch >= 'A' && ch <= 'F')
989 return ch - 'A' + 10;
990 else if (ch >= 'a' && ch <= 'f')
991 return ch - 'a' + 10;
992 else
993 return 0;
997 /* utils_strtod() converts a string containing a hex colour ("0x00ff00") into an integer.
998 * Basically, it is the same as strtod() would do, but it does not understand hex colour values,
999 * before ANSI-C99. With with_route set, it takes strings of the format "#00ff00".
1000 * Returns -1 on failure. */
1001 gint utils_strtod(const gchar *source, gchar **end, gboolean with_route)
1003 guint red, green, blue, offset = 0;
1005 g_return_val_if_fail(source != NULL, -1);
1007 if (with_route && (strlen(source) != 7 || source[0] != '#'))
1008 return -1;
1009 else if (! with_route && (strlen(source) != 8 || source[0] != '0' ||
1010 (source[1] != 'x' && source[1] != 'X')))
1012 return -1;
1015 /* offset is set to 1 when the string starts with 0x, otherwise it starts with #
1016 * and we don't need to increase the index */
1017 if (! with_route)
1018 offset = 1;
1020 red = utils_get_value_of_hex(
1021 source[1 + offset]) * 16 + utils_get_value_of_hex(source[2 + offset]);
1022 green = utils_get_value_of_hex(
1023 source[3 + offset]) * 16 + utils_get_value_of_hex(source[4 + offset]);
1024 blue = utils_get_value_of_hex(
1025 source[5 + offset]) * 16 + utils_get_value_of_hex(source[6 + offset]);
1027 return (red | (green << 8) | (blue << 16));
1031 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
1032 gchar *utils_get_current_time_string(void)
1034 const time_t tp = time(NULL);
1035 const struct tm *tmval = localtime(&tp);
1036 gchar *result = g_malloc0(9);
1038 strftime(result, 9, "%H:%M:%S", tmval);
1039 result[8] = '\0';
1040 return result;
1044 GIOChannel *utils_set_up_io_channel(
1045 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1047 GIOChannel *ioc;
1048 /*const gchar *encoding;*/
1050 #ifdef G_OS_WIN32
1051 ioc = g_io_channel_win32_new_fd(fd);
1052 #else
1053 ioc = g_io_channel_unix_new(fd);
1054 #endif
1056 if (nblock)
1057 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1059 g_io_channel_set_encoding(ioc, NULL, NULL);
1061 if (! g_get_charset(&encoding))
1062 { // hope this works reliably
1063 GError *error = NULL;
1064 g_io_channel_set_encoding(ioc, encoding, &error);
1065 if (error)
1067 geany_debug("%s: %s", G_STRFUNC, error->message);
1068 g_error_free(error);
1069 return ioc;
1073 /* "auto-close" ;-) */
1074 g_io_channel_set_close_on_unref(ioc, TRUE);
1076 g_io_add_watch(ioc, cond, func, data);
1077 g_io_channel_unref(ioc);
1079 return ioc;
1083 gchar **utils_read_file_in_array(const gchar *filename)
1085 gchar **result = NULL;
1086 gchar *data;
1088 g_return_val_if_fail(filename != NULL, NULL);
1090 g_file_get_contents(filename, &data, NULL, NULL);
1092 if (data != NULL)
1094 result = g_strsplit_set(data, "\r\n", -1);
1095 g_free(data);
1098 return result;
1102 /* Contributed by Stefan Oltmanns, thanks.
1103 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1104 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1105 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1107 gsize i, j, len;
1108 guint unicodechar;
1110 g_return_val_if_fail(string != NULL, FALSE);
1112 j = 0;
1113 len = strlen(string);
1114 for (i = 0; i < len; i++)
1116 if (string[i]=='\\')
1118 if (i++ >= strlen(string))
1120 return FALSE;
1122 switch (string[i])
1124 case '\\':
1125 if (keep_backslash)
1126 string[j++] = '\\';
1127 string[j] = '\\';
1128 break;
1129 case 'n':
1130 string[j] = '\n';
1131 break;
1132 case 'r':
1133 string[j] = '\r';
1134 break;
1135 case 't':
1136 string[j] = '\t';
1137 break;
1138 #if 0
1139 case 'x': /* Warning: May produce illegal utf-8 string! */
1140 i += 2;
1141 if (i >= strlen(string))
1143 return FALSE;
1145 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1146 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1147 else return FALSE;
1148 string[j] <<= 4;
1149 if (isdigit(string[i])) string[j] |= string[i] - 48;
1150 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1151 else return FALSE;
1152 break;
1153 #endif
1154 case 'u':
1156 i += 2;
1157 if (i >= strlen(string))
1159 return FALSE;
1161 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1162 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1163 else return FALSE;
1164 unicodechar <<= 4;
1165 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1166 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1167 else return FALSE;
1168 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1169 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1171 i += 2;
1172 unicodechar <<= 8;
1173 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1174 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1175 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1176 else unicodechar |= tolower(string[i])-87;
1178 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1179 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1181 i += 2;
1182 unicodechar <<= 8;
1183 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1184 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1185 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1186 else unicodechar |= tolower(string[i])-87;
1188 if (unicodechar < 0x80)
1190 string[j] = unicodechar;
1192 else if (unicodechar < 0x800)
1194 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1195 j++;
1196 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1198 else if (unicodechar < 0x10000)
1200 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1201 j++;
1202 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1203 j++;
1204 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1206 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1208 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1209 j++;
1210 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1211 j++;
1212 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1213 j++;
1214 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1216 else
1218 return FALSE;
1220 break;
1222 default:
1223 /* unnecessary escapes are allowed */
1224 if (keep_backslash)
1225 string[j++] = '\\';
1226 string[j] = string[i];
1229 else
1231 string[j] = string[i];
1233 j++;
1235 while (j < i)
1237 string[j] = 0;
1238 j++;
1240 return TRUE;
1244 /* Wraps a string in place, replacing a space with a newline character.
1245 * wrapstart is the minimum position to start wrapping or -1 for default */
1246 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1248 gchar *pos, *linestart;
1249 gboolean ret = FALSE;
1251 if (wrapstart < 0)
1252 wrapstart = 80;
1254 for (pos = linestart = string; *pos != '\0'; pos++)
1256 if (pos - linestart >= wrapstart && *pos == ' ')
1258 *pos = '\n';
1259 linestart = pos;
1260 ret = TRUE;
1263 return ret;
1268 * Converts the given UTF-8 encoded string into locale encoding.
1269 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1271 * @param utf8_text UTF-8 encoded text.
1273 * @return The converted string in locale encoding, or a copy of the input string if conversion
1274 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1276 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1278 #ifdef G_OS_WIN32
1279 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1280 * which would result in wrongly converted strings */
1281 return g_strdup(utf8_text);
1282 #else
1283 gchar *locale_text;
1285 if (! utf8_text)
1286 return NULL;
1287 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1288 if (locale_text == NULL)
1289 locale_text = g_strdup(utf8_text);
1290 return locale_text;
1291 #endif
1296 * Converts the given string (in locale encoding) into UTF-8 encoding.
1297 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1299 * @param locale_text Text in locale encoding.
1301 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1302 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1304 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1306 #ifdef G_OS_WIN32
1307 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1308 * which would result in wrongly converted strings */
1309 return g_strdup(locale_text);
1310 #else
1311 gchar *utf8_text;
1313 if (! locale_text)
1314 return NULL;
1315 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1316 if (utf8_text == NULL)
1317 utf8_text = g_strdup(locale_text);
1318 return utf8_text;
1319 #endif
1323 /* Pass pointers to free after arg_count.
1324 * The last argument must be NULL as an extra check that arg_count is correct. */
1325 void utils_free_pointers(gsize arg_count, ...)
1327 va_list a;
1328 gsize i;
1329 gpointer ptr;
1331 va_start(a, arg_count);
1332 for (i = 0; i < arg_count; i++)
1334 ptr = va_arg(a, gpointer);
1335 g_free(ptr);
1337 ptr = va_arg(a, gpointer);
1338 if (ptr)
1339 g_warning("Wrong arg_count!");
1340 va_end(a);
1344 /* currently unused */
1345 #if 0
1346 /* Creates a string array deep copy of a series of non-NULL strings.
1347 * The first argument is nothing special.
1348 * The list must be ended with NULL.
1349 * If first is NULL, NULL is returned. */
1350 gchar **utils_strv_new(const gchar *first, ...)
1352 gsize strvlen, i;
1353 va_list args;
1354 gchar *str;
1355 gchar **strv;
1357 g_return_val_if_fail(first != NULL, NULL);
1359 strvlen = 1; /* for first argument */
1361 /* count other arguments */
1362 va_start(args, first);
1363 for (; va_arg(args, gchar*) != NULL; strvlen++);
1364 va_end(args);
1366 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1367 strv[0] = g_strdup(first);
1369 va_start(args, first);
1370 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1372 strv[i] = g_strdup(str);
1374 va_end(args);
1376 strv[i] = NULL;
1377 return strv;
1379 #endif
1383 * Creates a directory if it doesn't already exist.
1384 * Creates intermediate parent directories as needed, too.
1385 * The permissions of the created directory are set 0700.
1387 * @param path The path of the directory to create, in locale encoding.
1388 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1390 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1391 * failed operation is returned.
1393 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1395 gint mode = 0700;
1396 gint result;
1398 if (path == NULL || strlen(path) == 0)
1399 return EFAULT;
1401 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1402 if (result != 0)
1403 return errno;
1404 return 0;
1409 * Gets a list of files from the specified directory.
1410 * Locale encoding is expected for @a path and used for the file list. The list and the data
1411 * in the list should be freed after use, e.g.:
1412 * @code
1413 * g_slist_foreach(list, (GFunc) g_free, NULL);
1414 * g_slist_free(list); @endcode
1416 * @note If you don't need a list you should use the foreach_dir() macro instead -
1417 * it's more efficient.
1419 * @param path The path of the directory to scan, in locale encoding.
1420 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1421 * will use more memory.
1422 * @param sort Whether to sort alphabetically (UTF-8 safe).
1423 * @param error The location for storing a possible error, or @c NULL.
1425 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1426 * freed when no longer needed.
1427 * @see utils_get_file_list().
1429 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1431 GSList *list = NULL;
1432 GDir *dir;
1433 const gchar *filename;
1435 if (error)
1436 *error = NULL;
1437 g_return_val_if_fail(path != NULL, NULL);
1439 dir = g_dir_open(path, 0, error);
1440 if (dir == NULL)
1441 return NULL;
1443 foreach_dir(filename, dir)
1445 list = g_slist_prepend(list, full_path ?
1446 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1448 g_dir_close(dir);
1449 /* sorting last is quicker than on insertion */
1450 if (sort)
1451 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1452 return list;
1457 * Gets a sorted list of files from the specified directory.
1458 * Locale encoding is expected for @a path and used for the file list. The list and the data
1459 * in the list should be freed after use, e.g.:
1460 * @code
1461 * g_slist_foreach(list, (GFunc) g_free, NULL);
1462 * g_slist_free(list); @endcode
1464 * @param path The path of the directory to scan, in locale encoding.
1465 * @param length The location to store the number of non-@c NULL data items in the list,
1466 * unless @c NULL.
1467 * @param error The location for storing a possible error, or @c NULL.
1469 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1470 * freed when no longer needed.
1471 * @see utils_get_file_list_full().
1473 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1475 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1477 if (length)
1478 *length = g_slist_length(list);
1479 return list;
1483 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1484 gboolean utils_str_has_upper(const gchar *str)
1486 gunichar c;
1488 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1489 return FALSE;
1491 while (*str != '\0')
1493 c = g_utf8_get_char(str);
1494 /* check only letters and stop once the first non-capital was found */
1495 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1496 return TRUE;
1497 /* FIXME don't write a const string */
1498 str = g_utf8_next_char(str);
1500 return FALSE;
1504 /* end can be -1 for haystack->len.
1505 * returns: position of found text or -1. */
1506 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1508 gint pos;
1510 g_return_val_if_fail(haystack != NULL, -1);
1511 if (haystack->len == 0)
1512 return -1;
1514 g_return_val_if_fail(start >= 0, -1);
1515 if (start >= (gint)haystack->len)
1516 return -1;
1518 g_return_val_if_fail(!EMPTY(needle), -1);
1520 if (end < 0)
1521 end = haystack->len;
1523 pos = utils_strpos(haystack->str + start, needle);
1524 if (pos == -1)
1525 return -1;
1527 pos += start;
1528 if (pos >= end)
1529 return -1;
1530 return pos;
1534 /* Replaces @len characters from offset @a pos.
1535 * len can be -1 to replace the remainder of @a str.
1536 * returns: pos + strlen(replace). */
1537 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1539 g_string_erase(str, pos, len);
1540 if (replace)
1542 g_string_insert(str, pos, replace);
1543 pos += strlen(replace);
1545 return pos;
1550 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1551 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1552 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1554 * @param haystack The input string to operate on. This string is modified in place.
1555 * @param needle The string which should be replaced.
1556 * @param replace The replacement for @a needle.
1558 * @return Number of replacements made.
1560 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1562 guint count = 0;
1563 gint pos = 0;
1564 gsize needle_length = strlen(needle);
1566 while (1)
1568 pos = utils_string_find(haystack, pos, -1, needle);
1570 if (pos == -1)
1571 break;
1573 pos = utils_string_replace(haystack, pos, needle_length, replace);
1574 count++;
1576 return count;
1581 * Replaces only the first occurrence of @a needle in @a haystack
1582 * with @a replace.
1583 * For details, see utils_string_replace_all().
1585 * @param haystack The input string to operate on. This string is modified in place.
1586 * @param needle The string which should be replaced.
1587 * @param replace The replacement for @a needle.
1589 * @return Number of replacements made.
1591 * @since 0.16
1593 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1595 gint pos = utils_string_find(haystack, 0, -1, needle);
1597 if (pos == -1)
1598 return 0;
1600 utils_string_replace(haystack, pos, strlen(needle), replace);
1601 return 1;
1605 /* Similar to g_regex_replace but allows matching a subgroup.
1606 * match_num: which match to replace, 0 for whole match.
1607 * literal: FALSE to interpret escape sequences in @a replace.
1608 * returns: number of replacements.
1609 * bug: replaced text can affect matching of ^ or \b */
1610 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1611 guint match_num, const gchar *replace, gboolean literal)
1613 GMatchInfo *minfo;
1614 guint ret = 0;
1615 gint start = 0;
1617 g_assert(literal); /* escapes not implemented yet */
1618 g_return_val_if_fail(replace, 0);
1620 /* ensure haystack->str is not null */
1621 if (haystack->len == 0)
1622 return 0;
1624 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1625 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1627 gint end, len;
1629 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1630 len = end - start;
1631 utils_string_replace(haystack, start, len, replace);
1632 ret++;
1634 /* skip past whole match */
1635 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1636 start = end - len + strlen(replace);
1637 g_match_info_free(minfo);
1639 g_match_info_free(minfo);
1640 return ret;
1644 /* Get project or default startup directory (if set), or NULL. */
1645 const gchar *utils_get_default_dir_utf8(void)
1647 if (app->project && !EMPTY(app->project->base_path))
1649 return app->project->base_path;
1652 if (!EMPTY(prefs.default_open_path))
1654 return prefs.default_open_path;
1656 return NULL;
1661 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1662 * systems. On Win32 platforms, it uses the Windows API.
1664 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1665 * @param argv The child's argument vector.
1666 * @param env The child's environment, or @a NULL to inherit parent's.
1667 * @param flags Flags from GSpawnFlags.
1668 * @param child_setup A function to run in the child just before exec().
1669 * @param user_data The user data for child_setup.
1670 * @param std_out The return location for child output.
1671 * @param std_err The return location for child error messages.
1672 * @param exit_status The child exit status, as returned by waitpid().
1673 * @param error The return location for error or @a NULL.
1675 * @return @c TRUE on success, @c FALSE if an error was set.
1677 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1678 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1679 gchar **std_err, gint *exit_status, GError **error)
1681 gboolean result;
1683 if (argv == NULL)
1685 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1686 return FALSE;
1689 if (std_out)
1690 *std_out = NULL;
1692 if (std_err)
1693 *std_err = NULL;
1695 #ifdef G_OS_WIN32
1696 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1697 #else
1698 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1699 #endif
1701 return result;
1706 * Wraps g_spawn_async() and internally calls this function on Unix-like
1707 * systems. On Win32 platforms, it uses the Windows API.
1709 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1710 * @param argv The child's argument vector.
1711 * @param env The child's environment, or @a NULL to inherit parent's.
1712 * @param flags Flags from GSpawnFlags.
1713 * @param child_setup A function to run in the child just before exec().
1714 * @param user_data The user data for child_setup.
1715 * @param child_pid The return location for child process ID, or NULL.
1716 * @param error The return location for error or @a NULL.
1718 * @return @c TRUE on success, @c FALSE if an error was set.
1720 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1721 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1722 GError **error)
1724 gboolean result;
1726 if (argv == NULL)
1728 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1729 return FALSE;
1732 #ifdef G_OS_WIN32
1733 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1734 #else
1735 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1736 #endif
1737 return result;
1741 /* Retrieves the path for the given URI.
1742 * It returns:
1743 * - the path which was determined by g_filename_from_uri() or GIO
1744 * - NULL if the URI is non-local and gvfs-fuse is not installed
1745 * - a new copy of 'uri' if it is not an URI. */
1746 gchar *utils_get_path_from_uri(const gchar *uri)
1748 gchar *locale_filename;
1750 g_return_val_if_fail(uri != NULL, NULL);
1752 if (! utils_is_uri(uri))
1753 return g_strdup(uri);
1755 /* this will work only for 'file://' URIs */
1756 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1757 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1758 if (locale_filename == NULL)
1760 GFile *file = g_file_new_for_uri(uri);
1761 locale_filename = g_file_get_path(file);
1762 g_object_unref(file);
1763 if (locale_filename == NULL)
1765 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1766 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1770 return locale_filename;
1774 gboolean utils_is_uri(const gchar *uri)
1776 g_return_val_if_fail(uri != NULL, FALSE);
1778 return (strstr(uri, "://") != NULL);
1782 /* path should be in locale encoding */
1783 gboolean utils_is_remote_path(const gchar *path)
1785 g_return_val_if_fail(path != NULL, FALSE);
1787 /* if path is an URI and it doesn't start "file://", we take it as remote */
1788 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1789 return TRUE;
1791 #ifndef G_OS_WIN32
1793 static gchar *fuse_path = NULL;
1794 static gsize len = 0;
1796 if (G_UNLIKELY(fuse_path == NULL))
1798 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1799 len = strlen(fuse_path);
1801 /* Comparing the file path against a hardcoded path is not the most elegant solution
1802 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1803 * proper GFile objects for Fuse paths, but it only does in future GVFS
1804 * versions (gvfs 1.1.1). */
1805 return (strncmp(path, fuse_path, len) == 0);
1807 #endif
1809 return FALSE;
1813 /* Remove all relative and untidy elements from the path of @a filename.
1814 * @param filename must be a valid absolute path.
1815 * @see tm_get_real_path() - also resolves links. */
1816 void utils_tidy_path(gchar *filename)
1818 GString *str = g_string_new(filename);
1819 const gchar *c, *needle;
1820 gchar *tmp;
1821 gssize pos;
1822 gboolean preserve_double_backslash = FALSE;
1824 g_return_if_fail(g_path_is_absolute(filename));
1826 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1827 preserve_double_backslash = TRUE;
1829 #ifdef G_OS_WIN32
1830 /* using MSYS we can get Unix-style separators */
1831 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1832 #endif
1833 /* replace "/./" and "//" */
1834 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1835 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1837 if (preserve_double_backslash)
1838 g_string_prepend(str, "\\");
1840 /* replace "/../" */
1841 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1842 while (1)
1844 c = strstr(str->str, needle);
1845 if (c == NULL)
1846 break;
1847 else
1849 pos = c - str->str;
1850 if (pos <= 3)
1851 break; /* bad path */
1853 /* replace "/../" */
1854 g_string_erase(str, pos, strlen(needle));
1855 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1857 tmp = g_strndup(str->str, pos); /* path up to "/../" */
1858 c = g_strrstr(tmp, G_DIR_SEPARATOR_S);
1859 g_return_if_fail(c);
1861 pos = c - tmp; /* position of previous "/" */
1862 g_string_erase(str, pos, strlen(c));
1863 g_free(tmp);
1866 g_return_if_fail(strlen(str->str) <= strlen(filename));
1867 strcpy(filename, str->str);
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 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1885 const gchar *r;
1886 gchar *w = string;
1888 g_return_val_if_fail(string, NULL);
1889 if (G_UNLIKELY(EMPTY(chars)))
1890 return string;
1892 foreach_str(r, string)
1894 if (!strchr(chars, *r))
1895 *w++ = *r;
1897 *w = 0x0;
1898 return string;
1902 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1903 GSList *utils_get_config_files(const gchar *subdir)
1905 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1906 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1907 GSList *syslist, *node;
1909 if (!list)
1911 utils_mkdir(path, FALSE);
1913 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1914 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1915 /* merge lists */
1916 list = g_slist_concat(list, syslist);
1918 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1919 /* remove duplicates (next to each other after sorting) */
1920 foreach_slist(node, list)
1922 if (node->next && utils_str_equal(node->next->data, node->data))
1924 GSList *old = node->next;
1926 g_free(old->data);
1927 node->next = old->next;
1928 g_slist_free1(old);
1931 g_free(path);
1932 return list;
1936 /* Suffix can be NULL or a string which should be appended to the Help URL like
1937 * an anchor link, e.g. "#some_anchor". */
1938 gchar *utils_get_help_url(const gchar *suffix)
1940 gint skip;
1941 gchar *uri;
1943 #ifdef G_OS_WIN32
1944 skip = 8;
1945 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1946 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1947 #else
1948 skip = 7;
1949 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1950 #endif
1952 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1953 { /* fall back to online documentation if it is not found on the hard disk */
1954 g_free(uri);
1955 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1958 if (suffix != NULL)
1960 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1963 return uri;
1967 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1969 const gchar **p;
1971 for (p = haystack; *p != NULL; ++p)
1973 if (utils_str_equal(*p, needle))
1974 return TRUE;
1976 return FALSE;
1981 * Copies the current environment into a new array.
1982 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1983 * All further arguments are key, value pairs of variables which should be added to
1984 * the environment.
1986 * The argument list must be @c NULL-terminated.
1988 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1989 * @param first_varname Name of the first variable to copy into the new array.
1990 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1992 * @return The new environment array.
1994 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1996 gchar **result;
1997 gchar **p;
1998 gchar **env;
1999 va_list args;
2000 const gchar *key, *value;
2001 guint n, o;
2003 /* get all the environ variables */
2004 env = g_listenv();
2006 /* count the additional variables */
2007 va_start(args, first_varname);
2008 for (o = 1; va_arg(args, gchar*) != NULL; o++);
2009 va_end(args);
2010 /* the passed arguments should be even (key, value pairs) */
2011 g_return_val_if_fail(o % 2 == 0, NULL);
2013 o /= 2;
2015 /* create an array large enough to hold the new environment */
2016 n = g_strv_length(env);
2017 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
2018 result = g_new(gchar *, n + o + 1);
2020 /* copy the environment */
2021 for (n = 0, p = env; *p != NULL; ++p)
2023 /* copy the variable */
2024 value = g_getenv(*p);
2025 if (G_LIKELY(value != NULL))
2027 /* skip excluded variables */
2028 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
2029 continue;
2031 result[n++] = g_strconcat(*p, "=", value, NULL);
2034 g_strfreev(env);
2036 /* now add additional variables */
2037 va_start(args, first_varname);
2038 key = first_varname;
2039 value = va_arg(args, gchar*);
2040 while (key != NULL)
2042 result[n++] = g_strconcat(key, "=", value, NULL);
2044 key = va_arg(args, gchar*);
2045 if (key == NULL)
2046 break;
2047 value = va_arg(args, gchar*);
2049 va_end(args);
2051 result[n] = NULL;
2053 return result;
2057 /* Joins @a first and @a second into a new string vector, freeing the originals.
2058 * The original contents are reused. */
2059 gchar **utils_strv_join(gchar **first, gchar **second)
2061 gchar **strv;
2062 gchar **rptr, **wptr;
2064 if (!first)
2065 return second;
2066 if (!second)
2067 return first;
2069 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2070 wptr = strv;
2072 foreach_strv(rptr, first)
2073 *wptr++ = *rptr;
2074 foreach_strv(rptr, second)
2075 *wptr++ = *rptr;
2077 g_free(first);
2078 g_free(second);
2079 return strv;
2083 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2084 * obviously g_date_set_parse() uses some magic.
2085 * The returned GDate object must be freed. */
2086 GDate *utils_parse_date(const gchar *input)
2088 GDate *date = g_date_new();
2090 g_date_set_parse(date, input);
2092 if (g_date_valid(date))
2093 return date;
2095 g_date_free(date);
2096 return NULL;
2100 gchar *utils_parse_and_format_build_date(const gchar *input)
2102 gchar date_buf[255];
2103 GDate *date = utils_parse_date(input);
2105 if (date != NULL)
2107 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2108 g_date_free(date);
2109 return g_strdup(date_buf);
2112 return g_strdup(input);