Adjust test case tags file for fixed Python parser (class arglist assignment)
[geany-mirror.git] / src / utils.c
bloba2c35262a1cf1194b757ba2e093bf40203ac8508
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 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
641 * Returns 0 if it can be written, otherwise it returns errno */
642 gint utils_is_file_writable(const gchar *locale_filename)
644 gchar *file;
645 gint ret;
647 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
648 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
649 /* get the file's directory to check for write permission if it doesn't yet exist */
650 file = g_path_get_dirname(locale_filename);
651 else
652 file = g_strdup(locale_filename);
654 #ifdef G_OS_WIN32
655 /* use _waccess on Windows, access() doesn't accept special characters */
656 ret = win32_check_write_permission(file);
657 #else
659 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
660 * errno only when access() explicitly returns an error */
661 if (access(file, R_OK | W_OK) != 0)
662 ret = errno;
663 else
664 ret = 0;
665 #endif
666 g_free(file);
667 return ret;
671 /* Replaces all occurrences of needle in haystack with replacement.
672 * Warning: *haystack must be a heap address; it may be freed and reassigned.
673 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
674 * than @a needle.
675 * All strings have to be NULL-terminated.
676 * See utils_string_replace_all() for details. */
677 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
679 GString *str;
681 g_return_if_fail(*haystack != NULL);
683 str = g_string_new(*haystack);
685 g_free(*haystack);
686 utils_string_replace_all(str, needle, replacement);
688 *haystack = g_string_free(str, FALSE);
692 gint utils_strpos(const gchar *haystack, const gchar *needle)
694 const gchar *sub;
696 if (! *needle)
697 return -1;
699 sub = strstr(haystack, needle);
700 if (! sub)
701 return -1;
703 return sub - haystack;
708 * Retrieves a formatted date/time string from strftime().
709 * This function should be preferred to directly calling strftime() since this function
710 * works on UTF-8 encoded strings.
712 * @param format The format string to pass to strftime(3). See the strftime(3)
713 * documentation for details, in UTF-8 encoding.
714 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
716 * @return A newly-allocated string, should be freed when no longer needed.
718 * @since 0.16
720 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
722 const struct tm *tm;
723 static gchar date[1024];
724 gchar *locale_format;
725 gsize len;
727 g_return_val_if_fail(format != NULL, NULL);
729 if (! g_utf8_validate(format, -1, NULL))
731 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
732 if (locale_format == NULL)
733 return NULL;
735 else
736 locale_format = g_strdup(format);
738 if (time_to_use != NULL)
739 tm = localtime(time_to_use);
740 else
742 time_t tp = time(NULL);
743 tm = localtime(&tp);
746 len = strftime(date, 1024, locale_format, tm);
747 g_free(locale_format);
748 if (len == 0)
749 return NULL;
751 if (! g_utf8_validate(date, len, NULL))
752 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
753 else
754 return g_strdup(date);
758 gchar *utils_get_initials(const gchar *name)
760 gint i = 1, j = 1;
761 gchar *initials = g_malloc0(5);
763 initials[0] = name[0];
764 while (name[i] != '\0' && j < 4)
766 if (name[i] == ' ' && name[i + 1] != ' ')
768 initials[j++] = name[i + 1];
770 i++;
772 return initials;
777 * Wraps g_key_file_get_integer() to add a default value argument.
779 * @param config A GKeyFile object.
780 * @param section The group name to look in for the key.
781 * @param key The key to find.
782 * @param default_value The default value which will be returned when @a section or @a key
783 * don't exist.
785 * @return The value associated with @a key as an integer, or the given default value if the value
786 * could not be retrieved.
788 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
789 const gint default_value)
791 gint tmp;
792 GError *error = NULL;
794 g_return_val_if_fail(config, default_value);
796 tmp = g_key_file_get_integer(config, section, key, &error);
797 if (error)
799 g_error_free(error);
800 return default_value;
802 return tmp;
807 * Wraps g_key_file_get_boolean() to add a default value argument.
809 * @param config A GKeyFile object.
810 * @param section The group name to look in for the key.
811 * @param key The key to find.
812 * @param default_value The default value which will be returned when @c section or @c key
813 * don't exist.
815 * @return The value associated with @a key as a boolean, or the given default value if the value
816 * could not be retrieved.
818 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
819 const gboolean default_value)
821 gboolean tmp;
822 GError *error = NULL;
824 g_return_val_if_fail(config, default_value);
826 tmp = g_key_file_get_boolean(config, section, key, &error);
827 if (error)
829 g_error_free(error);
830 return default_value;
832 return tmp;
837 * Wraps g_key_file_get_string() to add a default value argument.
839 * @param config A GKeyFile object.
840 * @param section The group name to look in for the key.
841 * @param key The key to find.
842 * @param default_value The default value which will be returned when @a section or @a key
843 * don't exist.
845 * @return A newly allocated string, either the value for @a key or a copy of the given
846 * default value if it could not be retrieved.
848 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
849 const gchar *default_value)
851 gchar *tmp;
853 g_return_val_if_fail(config, g_strdup(default_value));
855 tmp = g_key_file_get_string(config, section, key, NULL);
856 if (!tmp)
858 return g_strdup(default_value);
860 return tmp;
864 gchar *utils_get_hex_from_color(GdkColor *color)
866 gchar *buffer = g_malloc0(9);
868 g_return_val_if_fail(color != NULL, NULL);
870 g_snprintf(buffer, 8, "#%02X%02X%02X",
871 (guint) (utils_scale_round(color->red / 256, 255)),
872 (guint) (utils_scale_round(color->green / 256, 255)),
873 (guint) (utils_scale_round(color->blue / 256, 255)));
875 return buffer;
879 guint utils_invert_color(guint color)
881 guint r, g, b;
883 r = 0xffffff - color;
884 g = 0xffffff - (color >> 8);
885 b = 0xffffff - (color >> 16);
887 return (r | (g << 8) | (b << 16));
891 /* Get directory from current file in the notebook.
892 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
893 * Returned string is in UTF-8 encoding */
894 gchar *utils_get_current_file_dir_utf8(void)
896 GeanyDocument *doc = document_get_current();
898 if (doc != NULL)
900 /* get current filename */
901 const gchar *cur_fname = doc->file_name;
903 if (cur_fname != NULL)
905 /* get folder part from current filename */
906 return g_path_get_dirname(cur_fname); /* returns "." if no path */
910 return NULL; /* no file open */
914 /* very simple convenience function */
915 void utils_beep(void)
917 if (prefs.beep_on_errors)
918 gdk_beep();
922 /* taken from busybox, thanks */
923 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
924 gulong display_unit)
926 /* The code will adjust for additional (appended) units. */
927 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
928 static const gchar fmt[] = "%Lu %c%c";
929 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
931 guint64 val;
932 gint frac;
933 const gchar *u;
934 const gchar *f;
936 u = zero_and_units;
937 f = fmt;
938 frac = 0;
940 val = size * block_size;
941 if (val == 0)
942 return g_strdup(u);
944 if (display_unit)
946 val += display_unit/2; /* Deal with rounding. */
947 val /= display_unit; /* Don't combine with the line above!!! */
949 else
951 ++u;
952 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
954 f = fmt_tenths;
955 ++u;
956 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
957 val /= 1024;
959 if (frac >= 10)
960 { /* We need to round up here. */
961 ++val;
962 frac = 0;
966 /* If f==fmt then 'frac' and 'u' are ignored. */
967 return g_strdup_printf(f, val, frac, *u, 'b');
971 static guint utils_get_value_of_hex(const gchar ch)
973 if (ch >= '0' && ch <= '9')
974 return ch - '0';
975 else if (ch >= 'A' && ch <= 'F')
976 return ch - 'A' + 10;
977 else if (ch >= 'a' && ch <= 'f')
978 return ch - 'a' + 10;
979 else
980 return 0;
984 /* utils_strtod() converts a string containing a hex colour ("0x00ff00") into an integer.
985 * Basically, it is the same as strtod() would do, but it does not understand hex colour values,
986 * before ANSI-C99. With with_route set, it takes strings of the format "#00ff00".
987 * Returns -1 on failure. */
988 gint utils_strtod(const gchar *source, gchar **end, gboolean with_route)
990 guint red, green, blue, offset = 0;
992 g_return_val_if_fail(source != NULL, -1);
994 if (with_route && (strlen(source) != 7 || source[0] != '#'))
995 return -1;
996 else if (! with_route && (strlen(source) != 8 || source[0] != '0' ||
997 (source[1] != 'x' && source[1] != 'X')))
999 return -1;
1002 /* offset is set to 1 when the string starts with 0x, otherwise it starts with #
1003 * and we don't need to increase the index */
1004 if (! with_route)
1005 offset = 1;
1007 red = utils_get_value_of_hex(
1008 source[1 + offset]) * 16 + utils_get_value_of_hex(source[2 + offset]);
1009 green = utils_get_value_of_hex(
1010 source[3 + offset]) * 16 + utils_get_value_of_hex(source[4 + offset]);
1011 blue = utils_get_value_of_hex(
1012 source[5 + offset]) * 16 + utils_get_value_of_hex(source[6 + offset]);
1014 return (red | (green << 8) | (blue << 16));
1018 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
1019 gchar *utils_get_current_time_string(void)
1021 const time_t tp = time(NULL);
1022 const struct tm *tmval = localtime(&tp);
1023 gchar *result = g_malloc0(9);
1025 strftime(result, 9, "%H:%M:%S", tmval);
1026 result[8] = '\0';
1027 return result;
1031 GIOChannel *utils_set_up_io_channel(
1032 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1034 GIOChannel *ioc;
1035 /*const gchar *encoding;*/
1037 #ifdef G_OS_WIN32
1038 ioc = g_io_channel_win32_new_fd(fd);
1039 #else
1040 ioc = g_io_channel_unix_new(fd);
1041 #endif
1043 if (nblock)
1044 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1046 g_io_channel_set_encoding(ioc, NULL, NULL);
1048 if (! g_get_charset(&encoding))
1049 { // hope this works reliably
1050 GError *error = NULL;
1051 g_io_channel_set_encoding(ioc, encoding, &error);
1052 if (error)
1054 geany_debug("%s: %s", G_STRFUNC, error->message);
1055 g_error_free(error);
1056 return ioc;
1060 /* "auto-close" ;-) */
1061 g_io_channel_set_close_on_unref(ioc, TRUE);
1063 g_io_add_watch(ioc, cond, func, data);
1064 g_io_channel_unref(ioc);
1066 return ioc;
1070 gchar **utils_read_file_in_array(const gchar *filename)
1072 gchar **result = NULL;
1073 gchar *data;
1075 g_return_val_if_fail(filename != NULL, NULL);
1077 g_file_get_contents(filename, &data, NULL, NULL);
1079 if (data != NULL)
1081 result = g_strsplit_set(data, "\r\n", -1);
1082 g_free(data);
1085 return result;
1089 /* Contributed by Stefan Oltmanns, thanks.
1090 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1091 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1092 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1094 gsize i, j, len;
1095 guint unicodechar;
1097 g_return_val_if_fail(string != NULL, FALSE);
1099 j = 0;
1100 len = strlen(string);
1101 for (i = 0; i < len; i++)
1103 if (string[i]=='\\')
1105 if (i++ >= strlen(string))
1107 return FALSE;
1109 switch (string[i])
1111 case '\\':
1112 if (keep_backslash)
1113 string[j++] = '\\';
1114 string[j] = '\\';
1115 break;
1116 case 'n':
1117 string[j] = '\n';
1118 break;
1119 case 'r':
1120 string[j] = '\r';
1121 break;
1122 case 't':
1123 string[j] = '\t';
1124 break;
1125 #if 0
1126 case 'x': /* Warning: May produce illegal utf-8 string! */
1127 i += 2;
1128 if (i >= strlen(string))
1130 return FALSE;
1132 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1133 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1134 else return FALSE;
1135 string[j] <<= 4;
1136 if (isdigit(string[i])) string[j] |= string[i] - 48;
1137 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1138 else return FALSE;
1139 break;
1140 #endif
1141 case 'u':
1143 i += 2;
1144 if (i >= strlen(string))
1146 return FALSE;
1148 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1149 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1150 else return FALSE;
1151 unicodechar <<= 4;
1152 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1153 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1154 else return FALSE;
1155 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1156 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1158 i += 2;
1159 unicodechar <<= 8;
1160 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1161 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1162 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1163 else unicodechar |= tolower(string[i])-87;
1165 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1166 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1168 i += 2;
1169 unicodechar <<= 8;
1170 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1171 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1172 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1173 else unicodechar |= tolower(string[i])-87;
1175 if (unicodechar < 0x80)
1177 string[j] = unicodechar;
1179 else if (unicodechar < 0x800)
1181 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1182 j++;
1183 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1185 else if (unicodechar < 0x10000)
1187 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1188 j++;
1189 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1190 j++;
1191 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1193 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1195 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1196 j++;
1197 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1198 j++;
1199 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1200 j++;
1201 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1203 else
1205 return FALSE;
1207 break;
1209 default:
1210 /* unnecessary escapes are allowed */
1211 if (keep_backslash)
1212 string[j++] = '\\';
1213 string[j] = string[i];
1216 else
1218 string[j] = string[i];
1220 j++;
1222 while (j < i)
1224 string[j] = 0;
1225 j++;
1227 return TRUE;
1231 /* Wraps a string in place, replacing a space with a newline character.
1232 * wrapstart is the minimum position to start wrapping or -1 for default */
1233 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1235 gchar *pos, *linestart;
1236 gboolean ret = FALSE;
1238 if (wrapstart < 0)
1239 wrapstart = 80;
1241 for (pos = linestart = string; *pos != '\0'; pos++)
1243 if (pos - linestart >= wrapstart && *pos == ' ')
1245 *pos = '\n';
1246 linestart = pos;
1247 ret = TRUE;
1250 return ret;
1255 * Converts the given UTF-8 encoded string into locale encoding.
1256 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1258 * @param utf8_text UTF-8 encoded text.
1260 * @return The converted string in locale encoding, or a copy of the input string if conversion
1261 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1263 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1265 #ifdef G_OS_WIN32
1266 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1267 * which would result in wrongly converted strings */
1268 return g_strdup(utf8_text);
1269 #else
1270 gchar *locale_text;
1272 if (! utf8_text)
1273 return NULL;
1274 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1275 if (locale_text == NULL)
1276 locale_text = g_strdup(utf8_text);
1277 return locale_text;
1278 #endif
1283 * Converts the given string (in locale encoding) into UTF-8 encoding.
1284 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1286 * @param locale_text Text in locale encoding.
1288 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1289 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1291 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1293 #ifdef G_OS_WIN32
1294 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1295 * which would result in wrongly converted strings */
1296 return g_strdup(locale_text);
1297 #else
1298 gchar *utf8_text;
1300 if (! locale_text)
1301 return NULL;
1302 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1303 if (utf8_text == NULL)
1304 utf8_text = g_strdup(locale_text);
1305 return utf8_text;
1306 #endif
1310 /* Pass pointers to free after arg_count.
1311 * The last argument must be NULL as an extra check that arg_count is correct. */
1312 void utils_free_pointers(gsize arg_count, ...)
1314 va_list a;
1315 gsize i;
1316 gpointer ptr;
1318 va_start(a, arg_count);
1319 for (i = 0; i < arg_count; i++)
1321 ptr = va_arg(a, gpointer);
1322 g_free(ptr);
1324 ptr = va_arg(a, gpointer);
1325 if (ptr)
1326 g_warning("Wrong arg_count!");
1327 va_end(a);
1331 /* currently unused */
1332 #if 0
1333 /* Creates a string array deep copy of a series of non-NULL strings.
1334 * The first argument is nothing special.
1335 * The list must be ended with NULL.
1336 * If first is NULL, NULL is returned. */
1337 gchar **utils_strv_new(const gchar *first, ...)
1339 gsize strvlen, i;
1340 va_list args;
1341 gchar *str;
1342 gchar **strv;
1344 g_return_val_if_fail(first != NULL, NULL);
1346 strvlen = 1; /* for first argument */
1348 /* count other arguments */
1349 va_start(args, first);
1350 for (; va_arg(args, gchar*) != NULL; strvlen++);
1351 va_end(args);
1353 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1354 strv[0] = g_strdup(first);
1356 va_start(args, first);
1357 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1359 strv[i] = g_strdup(str);
1361 va_end(args);
1363 strv[i] = NULL;
1364 return strv;
1366 #endif
1370 * Creates a directory if it doesn't already exist.
1371 * Creates intermediate parent directories as needed, too.
1372 * The permissions of the created directory are set 0700.
1374 * @param path The path of the directory to create, in locale encoding.
1375 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1377 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1378 * failed operation is returned.
1380 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1382 gint mode = 0700;
1383 gint result;
1385 if (path == NULL || strlen(path) == 0)
1386 return EFAULT;
1388 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1389 if (result != 0)
1390 return errno;
1391 return 0;
1396 * Gets a list of files from the specified directory.
1397 * Locale encoding is expected for @a path and used for the file list. The list and the data
1398 * in the list should be freed after use, e.g.:
1399 * @code
1400 * g_slist_foreach(list, (GFunc) g_free, NULL);
1401 * g_slist_free(list); @endcode
1403 * @note If you don't need a list you should use the foreach_dir() macro instead -
1404 * it's more efficient.
1406 * @param path The path of the directory to scan, in locale encoding.
1407 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1408 * will use more memory.
1409 * @param sort Whether to sort alphabetically (UTF-8 safe).
1410 * @param error The location for storing a possible error, or @c NULL.
1412 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1413 * freed when no longer needed.
1414 * @see utils_get_file_list().
1416 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1418 GSList *list = NULL;
1419 GDir *dir;
1420 const gchar *filename;
1422 if (error)
1423 *error = NULL;
1424 g_return_val_if_fail(path != NULL, NULL);
1426 dir = g_dir_open(path, 0, error);
1427 if (dir == NULL)
1428 return NULL;
1430 foreach_dir(filename, dir)
1432 list = g_slist_prepend(list, full_path ?
1433 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1435 g_dir_close(dir);
1436 /* sorting last is quicker than on insertion */
1437 if (sort)
1438 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1439 return list;
1444 * Gets a sorted list of files from the specified directory.
1445 * Locale encoding is expected for @a path and used for the file list. The list and the data
1446 * in the list should be freed after use, e.g.:
1447 * @code
1448 * g_slist_foreach(list, (GFunc) g_free, NULL);
1449 * g_slist_free(list); @endcode
1451 * @param path The path of the directory to scan, in locale encoding.
1452 * @param length The location to store the number of non-@c NULL data items in the list,
1453 * unless @c NULL.
1454 * @param error The location for storing a possible error, or @c NULL.
1456 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1457 * freed when no longer needed.
1458 * @see utils_get_file_list_full().
1460 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1462 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1464 if (length)
1465 *length = g_slist_length(list);
1466 return list;
1470 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1471 gboolean utils_str_has_upper(const gchar *str)
1473 gunichar c;
1475 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1476 return FALSE;
1478 while (*str != '\0')
1480 c = g_utf8_get_char(str);
1481 /* check only letters and stop once the first non-capital was found */
1482 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1483 return TRUE;
1484 /* FIXME don't write a const string */
1485 str = g_utf8_next_char(str);
1487 return FALSE;
1491 /* end can be -1 for haystack->len.
1492 * returns: position of found text or -1. */
1493 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1495 gint pos;
1497 g_return_val_if_fail(haystack != NULL, -1);
1498 if (haystack->len == 0)
1499 return -1;
1501 g_return_val_if_fail(start >= 0, -1);
1502 if (start >= (gint)haystack->len)
1503 return -1;
1505 g_return_val_if_fail(!EMPTY(needle), -1);
1507 if (end < 0)
1508 end = haystack->len;
1510 pos = utils_strpos(haystack->str + start, needle);
1511 if (pos == -1)
1512 return -1;
1514 pos += start;
1515 if (pos >= end)
1516 return -1;
1517 return pos;
1521 /* Replaces @len characters from offset @a pos.
1522 * len can be -1 to replace the remainder of @a str.
1523 * returns: pos + strlen(replace). */
1524 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1526 g_string_erase(str, pos, len);
1527 if (replace)
1529 g_string_insert(str, pos, replace);
1530 pos += strlen(replace);
1532 return pos;
1537 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1538 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1539 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1541 * @param haystack The input string to operate on. This string is modified in place.
1542 * @param needle The string which should be replaced.
1543 * @param replace The replacement for @a needle.
1545 * @return Number of replacements made.
1547 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1549 guint count = 0;
1550 gint pos = 0;
1551 gsize needle_length = strlen(needle);
1553 while (1)
1555 pos = utils_string_find(haystack, pos, -1, needle);
1557 if (pos == -1)
1558 break;
1560 pos = utils_string_replace(haystack, pos, needle_length, replace);
1561 count++;
1563 return count;
1568 * Replaces only the first occurrence of @a needle in @a haystack
1569 * with @a replace.
1570 * For details, see utils_string_replace_all().
1572 * @param haystack The input string to operate on. This string is modified in place.
1573 * @param needle The string which should be replaced.
1574 * @param replace The replacement for @a needle.
1576 * @return Number of replacements made.
1578 * @since 0.16
1580 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1582 gint pos = utils_string_find(haystack, 0, -1, needle);
1584 if (pos == -1)
1585 return 0;
1587 utils_string_replace(haystack, pos, strlen(needle), replace);
1588 return 1;
1592 /* Similar to g_regex_replace but allows matching a subgroup.
1593 * match_num: which match to replace, 0 for whole match.
1594 * literal: FALSE to interpret escape sequences in @a replace.
1595 * returns: number of replacements.
1596 * bug: replaced text can affect matching of ^ or \b */
1597 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1598 guint match_num, const gchar *replace, gboolean literal)
1600 GMatchInfo *minfo;
1601 guint ret = 0;
1602 gint start = 0;
1604 g_assert(literal); /* escapes not implemented yet */
1605 g_return_val_if_fail(replace, 0);
1607 /* ensure haystack->str is not null */
1608 if (haystack->len == 0)
1609 return 0;
1611 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1612 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1614 gint end, len;
1616 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1617 len = end - start;
1618 utils_string_replace(haystack, start, len, replace);
1619 ret++;
1621 /* skip past whole match */
1622 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1623 start = end - len + strlen(replace);
1624 g_match_info_free(minfo);
1626 g_match_info_free(minfo);
1627 return ret;
1631 /* Get project or default startup directory (if set), or NULL. */
1632 const gchar *utils_get_default_dir_utf8(void)
1634 if (app->project && !EMPTY(app->project->base_path))
1636 return app->project->base_path;
1639 if (!EMPTY(prefs.default_open_path))
1641 return prefs.default_open_path;
1643 return NULL;
1648 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1649 * systems. On Win32 platforms, it uses the Windows API.
1651 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1652 * @param argv The child's argument vector.
1653 * @param env The child's environment, or @a NULL to inherit parent's.
1654 * @param flags Flags from GSpawnFlags.
1655 * @param child_setup A function to run in the child just before exec().
1656 * @param user_data The user data for child_setup.
1657 * @param std_out The return location for child output.
1658 * @param std_err The return location for child error messages.
1659 * @param exit_status The child exit status, as returned by waitpid().
1660 * @param error The return location for error or @a NULL.
1662 * @return @c TRUE on success, @c FALSE if an error was set.
1664 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1665 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1666 gchar **std_err, gint *exit_status, GError **error)
1668 gboolean result;
1670 if (argv == NULL)
1672 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1673 return FALSE;
1676 if (std_out)
1677 *std_out = NULL;
1679 if (std_err)
1680 *std_err = NULL;
1682 #ifdef G_OS_WIN32
1683 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1684 #else
1685 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1686 #endif
1688 return result;
1693 * Wraps g_spawn_async() and internally calls this function on Unix-like
1694 * systems. On Win32 platforms, it uses the Windows API.
1696 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1697 * @param argv The child's argument vector.
1698 * @param env The child's environment, or @a NULL to inherit parent's.
1699 * @param flags Flags from GSpawnFlags.
1700 * @param child_setup A function to run in the child just before exec().
1701 * @param user_data The user data for child_setup.
1702 * @param child_pid The return location for child process ID, or NULL.
1703 * @param error The return location for error or @a NULL.
1705 * @return @c TRUE on success, @c FALSE if an error was set.
1707 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1708 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1709 GError **error)
1711 gboolean result;
1713 if (argv == NULL)
1715 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1716 return FALSE;
1719 #ifdef G_OS_WIN32
1720 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1721 #else
1722 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1723 #endif
1724 return result;
1728 /* Retrieves the path for the given URI.
1729 * It returns:
1730 * - the path which was determined by g_filename_from_uri() or GIO
1731 * - NULL if the URI is non-local and gvfs-fuse is not installed
1732 * - a new copy of 'uri' if it is not an URI. */
1733 gchar *utils_get_path_from_uri(const gchar *uri)
1735 gchar *locale_filename;
1737 g_return_val_if_fail(uri != NULL, NULL);
1739 if (! utils_is_uri(uri))
1740 return g_strdup(uri);
1742 /* this will work only for 'file://' URIs */
1743 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1744 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1745 if (locale_filename == NULL)
1747 GFile *file = g_file_new_for_uri(uri);
1748 locale_filename = g_file_get_path(file);
1749 g_object_unref(file);
1750 if (locale_filename == NULL)
1752 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1753 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1757 return locale_filename;
1761 gboolean utils_is_uri(const gchar *uri)
1763 g_return_val_if_fail(uri != NULL, FALSE);
1765 return (strstr(uri, "://") != NULL);
1769 /* path should be in locale encoding */
1770 gboolean utils_is_remote_path(const gchar *path)
1772 g_return_val_if_fail(path != NULL, FALSE);
1774 /* if path is an URI and it doesn't start "file://", we take it as remote */
1775 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1776 return TRUE;
1778 #ifndef G_OS_WIN32
1780 static gchar *fuse_path = NULL;
1781 static gsize len = 0;
1783 if (G_UNLIKELY(fuse_path == NULL))
1785 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1786 len = strlen(fuse_path);
1788 /* Comparing the file path against a hardcoded path is not the most elegant solution
1789 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1790 * proper GFile objects for Fuse paths, but it only does in future GVFS
1791 * versions (gvfs 1.1.1). */
1792 return (strncmp(path, fuse_path, len) == 0);
1794 #endif
1796 return FALSE;
1800 /* Remove all relative and untidy elements from the path of @a filename.
1801 * @param filename must be a valid absolute path.
1802 * @see tm_get_real_path() - also resolves links. */
1803 void utils_tidy_path(gchar *filename)
1805 GString *str = g_string_new(filename);
1806 const gchar *c, *needle;
1807 gchar *tmp;
1808 gssize pos;
1809 gboolean preserve_double_backslash = FALSE;
1811 g_return_if_fail(g_path_is_absolute(filename));
1813 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1814 preserve_double_backslash = TRUE;
1816 #ifdef G_OS_WIN32
1817 /* using MSYS we can get Unix-style separators */
1818 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1819 #endif
1820 /* replace "/./" and "//" */
1821 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1822 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1824 if (preserve_double_backslash)
1825 g_string_prepend(str, "\\");
1827 /* replace "/../" */
1828 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1829 while (1)
1831 c = strstr(str->str, needle);
1832 if (c == NULL)
1833 break;
1834 else
1836 pos = c - str->str;
1837 if (pos <= 3)
1838 break; /* bad path */
1840 /* replace "/../" */
1841 g_string_erase(str, pos, strlen(needle));
1842 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1844 tmp = g_strndup(str->str, pos); /* path up to "/../" */
1845 c = g_strrstr(tmp, G_DIR_SEPARATOR_S);
1846 g_return_if_fail(c);
1848 pos = c - tmp; /* position of previous "/" */
1849 g_string_erase(str, pos, strlen(c));
1850 g_free(tmp);
1853 g_return_if_fail(strlen(str->str) <= strlen(filename));
1854 strcpy(filename, str->str);
1855 g_string_free(str, TRUE);
1860 * Removes characters from a string, in place.
1862 * @param string String to search.
1863 * @param chars Characters to remove.
1865 * @return @a string - return value is only useful when nesting function calls, e.g.:
1866 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1868 * @see @c g_strdelimit.
1870 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1872 const gchar *r;
1873 gchar *w = string;
1875 g_return_val_if_fail(string, NULL);
1876 if (G_UNLIKELY(EMPTY(chars)))
1877 return string;
1879 foreach_str(r, string)
1881 if (!strchr(chars, *r))
1882 *w++ = *r;
1884 *w = 0x0;
1885 return string;
1889 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1890 GSList *utils_get_config_files(const gchar *subdir)
1892 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1893 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1894 GSList *syslist, *node;
1896 if (!list)
1898 utils_mkdir(path, FALSE);
1900 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1901 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1902 /* merge lists */
1903 list = g_slist_concat(list, syslist);
1905 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1906 /* remove duplicates (next to each other after sorting) */
1907 foreach_slist(node, list)
1909 if (node->next && utils_str_equal(node->next->data, node->data))
1911 GSList *old = node->next;
1913 g_free(old->data);
1914 node->next = old->next;
1915 g_slist_free1(old);
1918 g_free(path);
1919 return list;
1923 /* Suffix can be NULL or a string which should be appended to the Help URL like
1924 * an anchor link, e.g. "#some_anchor". */
1925 gchar *utils_get_help_url(const gchar *suffix)
1927 gint skip;
1928 gchar *uri;
1930 #ifdef G_OS_WIN32
1931 skip = 8;
1932 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1933 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1934 #else
1935 skip = 7;
1936 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1937 #endif
1939 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1940 { /* fall back to online documentation if it is not found on the hard disk */
1941 g_free(uri);
1942 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1945 if (suffix != NULL)
1947 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1950 return uri;
1954 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1956 const gchar **p;
1958 for (p = haystack; *p != NULL; ++p)
1960 if (utils_str_equal(*p, needle))
1961 return TRUE;
1963 return FALSE;
1968 * Copies the current environment into a new array.
1969 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1970 * All further arguments are key, value pairs of variables which should be added to
1971 * the environment.
1973 * The argument list must be @c NULL-terminated.
1975 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1976 * @param first_varname Name of the first variable to copy into the new array.
1977 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1979 * @return The new environment array.
1981 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1983 gchar **result;
1984 gchar **p;
1985 gchar **env;
1986 va_list args;
1987 const gchar *key, *value;
1988 guint n, o;
1990 /* get all the environ variables */
1991 env = g_listenv();
1993 /* count the additional variables */
1994 va_start(args, first_varname);
1995 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1996 va_end(args);
1997 /* the passed arguments should be even (key, value pairs) */
1998 g_return_val_if_fail(o % 2 == 0, NULL);
2000 o /= 2;
2002 /* create an array large enough to hold the new environment */
2003 n = g_strv_length(env);
2004 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
2005 result = g_new(gchar *, n + o + 1);
2007 /* copy the environment */
2008 for (n = 0, p = env; *p != NULL; ++p)
2010 /* copy the variable */
2011 value = g_getenv(*p);
2012 if (G_LIKELY(value != NULL))
2014 /* skip excluded variables */
2015 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
2016 continue;
2018 result[n++] = g_strconcat(*p, "=", value, NULL);
2021 g_strfreev(env);
2023 /* now add additional variables */
2024 va_start(args, first_varname);
2025 key = first_varname;
2026 value = va_arg(args, gchar*);
2027 while (key != NULL)
2029 result[n++] = g_strconcat(key, "=", value, NULL);
2031 key = va_arg(args, gchar*);
2032 if (key == NULL)
2033 break;
2034 value = va_arg(args, gchar*);
2036 va_end(args);
2038 result[n] = NULL;
2040 return result;
2044 /* Joins @a first and @a second into a new string vector, freeing the originals.
2045 * The original contents are reused. */
2046 gchar **utils_strv_join(gchar **first, gchar **second)
2048 gchar **strv;
2049 gchar **rptr, **wptr;
2051 if (!first)
2052 return second;
2053 if (!second)
2054 return first;
2056 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2057 wptr = strv;
2059 foreach_strv(rptr, first)
2060 *wptr++ = *rptr;
2061 foreach_strv(rptr, second)
2062 *wptr++ = *rptr;
2064 g_free(first);
2065 g_free(second);
2066 return strv;
2070 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2071 * obviously g_date_set_parse() uses some magic.
2072 * The returned GDate object must be freed. */
2073 GDate *utils_parse_date(const gchar *input)
2075 GDate *date = g_date_new();
2077 g_date_set_parse(date, input);
2079 if (g_date_valid(date))
2080 return date;
2082 g_date_free(date);
2083 return NULL;
2087 gchar *utils_parse_and_format_build_date(const gchar *input)
2089 gchar date_buf[255];
2090 GDate *date = utils_parse_date(input);
2092 if (date != NULL)
2094 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2095 g_date_free(date);
2096 return g_strdup(date_buf);
2099 return g_strdup(input);