Remove comment noise from `doc/plugins.dox` and make it a build depend
[geany-mirror.git] / src / utils.c
blobcd196cce693901a92bb9c91df2e47417ae65e5b6
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 g_return_val_if_fail(color != NULL, NULL);
868 return g_strdup_printf("#%02X%02X%02X",
869 (guint) (utils_scale_round(color->red / 256, 255)),
870 (guint) (utils_scale_round(color->green / 256, 255)),
871 (guint) (utils_scale_round(color->blue / 256, 255)));
875 /* Get directory from current file in the notebook.
876 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
877 * Returned string is in UTF-8 encoding */
878 gchar *utils_get_current_file_dir_utf8(void)
880 GeanyDocument *doc = document_get_current();
882 if (doc != NULL)
884 /* get current filename */
885 const gchar *cur_fname = doc->file_name;
887 if (cur_fname != NULL)
889 /* get folder part from current filename */
890 return g_path_get_dirname(cur_fname); /* returns "." if no path */
894 return NULL; /* no file open */
898 /* very simple convenience function */
899 void utils_beep(void)
901 if (prefs.beep_on_errors)
902 gdk_beep();
906 /* taken from busybox, thanks */
907 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
908 gulong display_unit)
910 /* The code will adjust for additional (appended) units. */
911 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
912 static const gchar fmt[] = "%Lu %c%c";
913 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
915 guint64 val;
916 gint frac;
917 const gchar *u;
918 const gchar *f;
920 u = zero_and_units;
921 f = fmt;
922 frac = 0;
924 val = size * block_size;
925 if (val == 0)
926 return g_strdup(u);
928 if (display_unit)
930 val += display_unit/2; /* Deal with rounding. */
931 val /= display_unit; /* Don't combine with the line above!!! */
933 else
935 ++u;
936 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
938 f = fmt_tenths;
939 ++u;
940 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
941 val /= 1024;
943 if (frac >= 10)
944 { /* We need to round up here. */
945 ++val;
946 frac = 0;
950 /* If f==fmt then 'frac' and 'u' are ignored. */
951 return g_strdup_printf(f, val, frac, *u, 'b');
955 /* converts a color representation using gdk_color_parse(), with additional
956 * support of the "0x" prefix as a synonym for "#" */
957 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
959 gchar buf[64] = {0};
961 g_return_val_if_fail(spec != NULL, -1);
963 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
965 /* convert to # format for GDK to understand it */
966 buf[0] = '#';
967 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
968 spec = buf;
971 return gdk_color_parse(spec, color);
975 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
976 * returns a 24 bits BGR color, or -1 on failure */
977 gint utils_color_to_bgr(const GdkColor *c)
979 g_return_val_if_fail(c != NULL, -1);
980 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
984 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
985 * utils_color_to_bgr() */
986 gint utils_parse_color_to_bgr(const gchar *spec)
988 GdkColor color;
989 if (utils_parse_color(spec, &color))
990 return utils_color_to_bgr(&color);
991 else
992 return -1;
996 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
997 gchar *utils_get_current_time_string(void)
999 const time_t tp = time(NULL);
1000 const struct tm *tmval = localtime(&tp);
1001 gchar *result = g_malloc0(9);
1003 strftime(result, 9, "%H:%M:%S", tmval);
1004 result[8] = '\0';
1005 return result;
1009 GIOChannel *utils_set_up_io_channel(
1010 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1012 GIOChannel *ioc;
1013 /*const gchar *encoding;*/
1015 #ifdef G_OS_WIN32
1016 ioc = g_io_channel_win32_new_fd(fd);
1017 #else
1018 ioc = g_io_channel_unix_new(fd);
1019 #endif
1021 if (nblock)
1022 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1024 g_io_channel_set_encoding(ioc, NULL, NULL);
1026 if (! g_get_charset(&encoding))
1027 { // hope this works reliably
1028 GError *error = NULL;
1029 g_io_channel_set_encoding(ioc, encoding, &error);
1030 if (error)
1032 geany_debug("%s: %s", G_STRFUNC, error->message);
1033 g_error_free(error);
1034 return ioc;
1038 /* "auto-close" ;-) */
1039 g_io_channel_set_close_on_unref(ioc, TRUE);
1041 g_io_add_watch(ioc, cond, func, data);
1042 g_io_channel_unref(ioc);
1044 return ioc;
1048 gchar **utils_read_file_in_array(const gchar *filename)
1050 gchar **result = NULL;
1051 gchar *data;
1053 g_return_val_if_fail(filename != NULL, NULL);
1055 g_file_get_contents(filename, &data, NULL, NULL);
1057 if (data != NULL)
1059 result = g_strsplit_set(data, "\r\n", -1);
1060 g_free(data);
1063 return result;
1067 /* Contributed by Stefan Oltmanns, thanks.
1068 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1069 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1070 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1072 gsize i, j, len;
1073 guint unicodechar;
1075 g_return_val_if_fail(string != NULL, FALSE);
1077 j = 0;
1078 len = strlen(string);
1079 for (i = 0; i < len; i++)
1081 if (string[i]=='\\')
1083 if (i++ >= strlen(string))
1085 return FALSE;
1087 switch (string[i])
1089 case '\\':
1090 if (keep_backslash)
1091 string[j++] = '\\';
1092 string[j] = '\\';
1093 break;
1094 case 'n':
1095 string[j] = '\n';
1096 break;
1097 case 'r':
1098 string[j] = '\r';
1099 break;
1100 case 't':
1101 string[j] = '\t';
1102 break;
1103 #if 0
1104 case 'x': /* Warning: May produce illegal utf-8 string! */
1105 i += 2;
1106 if (i >= strlen(string))
1108 return FALSE;
1110 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1111 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1112 else return FALSE;
1113 string[j] <<= 4;
1114 if (isdigit(string[i])) string[j] |= string[i] - 48;
1115 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1116 else return FALSE;
1117 break;
1118 #endif
1119 case 'u':
1121 i += 2;
1122 if (i >= strlen(string))
1124 return FALSE;
1126 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1127 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1128 else return FALSE;
1129 unicodechar <<= 4;
1130 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1131 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1132 else return FALSE;
1133 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1134 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1136 i += 2;
1137 unicodechar <<= 8;
1138 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1139 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1140 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1141 else unicodechar |= tolower(string[i])-87;
1143 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1144 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1146 i += 2;
1147 unicodechar <<= 8;
1148 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1149 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1150 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1151 else unicodechar |= tolower(string[i])-87;
1153 if (unicodechar < 0x80)
1155 string[j] = unicodechar;
1157 else if (unicodechar < 0x800)
1159 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1160 j++;
1161 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1163 else if (unicodechar < 0x10000)
1165 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1166 j++;
1167 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1168 j++;
1169 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1171 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1173 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1174 j++;
1175 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1176 j++;
1177 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1178 j++;
1179 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1181 else
1183 return FALSE;
1185 break;
1187 default:
1188 /* unnecessary escapes are allowed */
1189 if (keep_backslash)
1190 string[j++] = '\\';
1191 string[j] = string[i];
1194 else
1196 string[j] = string[i];
1198 j++;
1200 while (j < i)
1202 string[j] = 0;
1203 j++;
1205 return TRUE;
1209 /* Wraps a string in place, replacing a space with a newline character.
1210 * wrapstart is the minimum position to start wrapping or -1 for default */
1211 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1213 gchar *pos, *linestart;
1214 gboolean ret = FALSE;
1216 if (wrapstart < 0)
1217 wrapstart = 80;
1219 for (pos = linestart = string; *pos != '\0'; pos++)
1221 if (pos - linestart >= wrapstart && *pos == ' ')
1223 *pos = '\n';
1224 linestart = pos;
1225 ret = TRUE;
1228 return ret;
1233 * Converts the given UTF-8 encoded string into locale encoding.
1234 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1236 * @param utf8_text UTF-8 encoded text.
1238 * @return The converted string in locale encoding, or a copy of the input string if conversion
1239 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1241 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1243 #ifdef G_OS_WIN32
1244 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1245 * which would result in wrongly converted strings */
1246 return g_strdup(utf8_text);
1247 #else
1248 gchar *locale_text;
1250 if (! utf8_text)
1251 return NULL;
1252 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1253 if (locale_text == NULL)
1254 locale_text = g_strdup(utf8_text);
1255 return locale_text;
1256 #endif
1261 * Converts the given string (in locale encoding) into UTF-8 encoding.
1262 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1264 * @param locale_text Text in locale encoding.
1266 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1267 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1269 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1271 #ifdef G_OS_WIN32
1272 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1273 * which would result in wrongly converted strings */
1274 return g_strdup(locale_text);
1275 #else
1276 gchar *utf8_text;
1278 if (! locale_text)
1279 return NULL;
1280 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1281 if (utf8_text == NULL)
1282 utf8_text = g_strdup(locale_text);
1283 return utf8_text;
1284 #endif
1288 /* Pass pointers to free after arg_count.
1289 * The last argument must be NULL as an extra check that arg_count is correct. */
1290 void utils_free_pointers(gsize arg_count, ...)
1292 va_list a;
1293 gsize i;
1294 gpointer ptr;
1296 va_start(a, arg_count);
1297 for (i = 0; i < arg_count; i++)
1299 ptr = va_arg(a, gpointer);
1300 g_free(ptr);
1302 ptr = va_arg(a, gpointer);
1303 if (ptr)
1304 g_warning("Wrong arg_count!");
1305 va_end(a);
1309 /* currently unused */
1310 #if 0
1311 /* Creates a string array deep copy of a series of non-NULL strings.
1312 * The first argument is nothing special.
1313 * The list must be ended with NULL.
1314 * If first is NULL, NULL is returned. */
1315 gchar **utils_strv_new(const gchar *first, ...)
1317 gsize strvlen, i;
1318 va_list args;
1319 gchar *str;
1320 gchar **strv;
1322 g_return_val_if_fail(first != NULL, NULL);
1324 strvlen = 1; /* for first argument */
1326 /* count other arguments */
1327 va_start(args, first);
1328 for (; va_arg(args, gchar*) != NULL; strvlen++);
1329 va_end(args);
1331 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1332 strv[0] = g_strdup(first);
1334 va_start(args, first);
1335 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1337 strv[i] = g_strdup(str);
1339 va_end(args);
1341 strv[i] = NULL;
1342 return strv;
1344 #endif
1348 * Creates a directory if it doesn't already exist.
1349 * Creates intermediate parent directories as needed, too.
1350 * The permissions of the created directory are set 0700.
1352 * @param path The path of the directory to create, in locale encoding.
1353 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1355 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1356 * failed operation is returned.
1358 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1360 gint mode = 0700;
1361 gint result;
1363 if (path == NULL || strlen(path) == 0)
1364 return EFAULT;
1366 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1367 if (result != 0)
1368 return errno;
1369 return 0;
1374 * Gets a list of files from the specified directory.
1375 * Locale encoding is expected for @a path and used for the file list. The list and the data
1376 * in the list should be freed after use, e.g.:
1377 * @code
1378 * g_slist_foreach(list, (GFunc) g_free, NULL);
1379 * g_slist_free(list); @endcode
1381 * @note If you don't need a list you should use the foreach_dir() macro instead -
1382 * it's more efficient.
1384 * @param path The path of the directory to scan, in locale encoding.
1385 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1386 * will use more memory.
1387 * @param sort Whether to sort alphabetically (UTF-8 safe).
1388 * @param error The location for storing a possible error, or @c NULL.
1390 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1391 * freed when no longer needed.
1392 * @see utils_get_file_list().
1394 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1396 GSList *list = NULL;
1397 GDir *dir;
1398 const gchar *filename;
1400 if (error)
1401 *error = NULL;
1402 g_return_val_if_fail(path != NULL, NULL);
1404 dir = g_dir_open(path, 0, error);
1405 if (dir == NULL)
1406 return NULL;
1408 foreach_dir(filename, dir)
1410 list = g_slist_prepend(list, full_path ?
1411 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1413 g_dir_close(dir);
1414 /* sorting last is quicker than on insertion */
1415 if (sort)
1416 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1417 return list;
1422 * Gets a sorted list of files from the specified directory.
1423 * Locale encoding is expected for @a path and used for the file list. The list and the data
1424 * in the list should be freed after use, e.g.:
1425 * @code
1426 * g_slist_foreach(list, (GFunc) g_free, NULL);
1427 * g_slist_free(list); @endcode
1429 * @param path The path of the directory to scan, in locale encoding.
1430 * @param length The location to store the number of non-@c NULL data items in the list,
1431 * unless @c NULL.
1432 * @param error The location for storing a possible error, or @c NULL.
1434 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1435 * freed when no longer needed.
1436 * @see utils_get_file_list_full().
1438 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1440 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1442 if (length)
1443 *length = g_slist_length(list);
1444 return list;
1448 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1449 gboolean utils_str_has_upper(const gchar *str)
1451 gunichar c;
1453 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1454 return FALSE;
1456 while (*str != '\0')
1458 c = g_utf8_get_char(str);
1459 /* check only letters and stop once the first non-capital was found */
1460 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1461 return TRUE;
1462 /* FIXME don't write a const string */
1463 str = g_utf8_next_char(str);
1465 return FALSE;
1469 /* end can be -1 for haystack->len.
1470 * returns: position of found text or -1. */
1471 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1473 gint pos;
1475 g_return_val_if_fail(haystack != NULL, -1);
1476 if (haystack->len == 0)
1477 return -1;
1479 g_return_val_if_fail(start >= 0, -1);
1480 if (start >= (gint)haystack->len)
1481 return -1;
1483 g_return_val_if_fail(!EMPTY(needle), -1);
1485 if (end < 0)
1486 end = haystack->len;
1488 pos = utils_strpos(haystack->str + start, needle);
1489 if (pos == -1)
1490 return -1;
1492 pos += start;
1493 if (pos >= end)
1494 return -1;
1495 return pos;
1499 /* Replaces @len characters from offset @a pos.
1500 * len can be -1 to replace the remainder of @a str.
1501 * returns: pos + strlen(replace). */
1502 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1504 g_string_erase(str, pos, len);
1505 if (replace)
1507 g_string_insert(str, pos, replace);
1508 pos += strlen(replace);
1510 return pos;
1515 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1516 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1517 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1519 * @param haystack The input string to operate on. This string is modified in place.
1520 * @param needle The string which should be replaced.
1521 * @param replace The replacement for @a needle.
1523 * @return Number of replacements made.
1525 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1527 guint count = 0;
1528 gint pos = 0;
1529 gsize needle_length = strlen(needle);
1531 while (1)
1533 pos = utils_string_find(haystack, pos, -1, needle);
1535 if (pos == -1)
1536 break;
1538 pos = utils_string_replace(haystack, pos, needle_length, replace);
1539 count++;
1541 return count;
1546 * Replaces only the first occurrence of @a needle in @a haystack
1547 * with @a replace.
1548 * For details, see utils_string_replace_all().
1550 * @param haystack The input string to operate on. This string is modified in place.
1551 * @param needle The string which should be replaced.
1552 * @param replace The replacement for @a needle.
1554 * @return Number of replacements made.
1556 * @since 0.16
1558 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1560 gint pos = utils_string_find(haystack, 0, -1, needle);
1562 if (pos == -1)
1563 return 0;
1565 utils_string_replace(haystack, pos, strlen(needle), replace);
1566 return 1;
1570 /* Similar to g_regex_replace but allows matching a subgroup.
1571 * match_num: which match to replace, 0 for whole match.
1572 * literal: FALSE to interpret escape sequences in @a replace.
1573 * returns: number of replacements.
1574 * bug: replaced text can affect matching of ^ or \b */
1575 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1576 guint match_num, const gchar *replace, gboolean literal)
1578 GMatchInfo *minfo;
1579 guint ret = 0;
1580 gint start = 0;
1582 g_assert(literal); /* escapes not implemented yet */
1583 g_return_val_if_fail(replace, 0);
1585 /* ensure haystack->str is not null */
1586 if (haystack->len == 0)
1587 return 0;
1589 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1590 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1592 gint end, len;
1594 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1595 len = end - start;
1596 utils_string_replace(haystack, start, len, replace);
1597 ret++;
1599 /* skip past whole match */
1600 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1601 start = end - len + strlen(replace);
1602 g_match_info_free(minfo);
1604 g_match_info_free(minfo);
1605 return ret;
1609 /* Get project or default startup directory (if set), or NULL. */
1610 const gchar *utils_get_default_dir_utf8(void)
1612 if (app->project && !EMPTY(app->project->base_path))
1614 return app->project->base_path;
1617 if (!EMPTY(prefs.default_open_path))
1619 return prefs.default_open_path;
1621 return NULL;
1626 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1627 * systems. On Win32 platforms, it uses the Windows API.
1629 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1630 * @param argv The child's argument vector.
1631 * @param env The child's environment, or @a NULL to inherit parent's.
1632 * @param flags Flags from GSpawnFlags.
1633 * @param child_setup A function to run in the child just before exec().
1634 * @param user_data The user data for child_setup.
1635 * @param std_out The return location for child output.
1636 * @param std_err The return location for child error messages.
1637 * @param exit_status The child exit status, as returned by waitpid().
1638 * @param error The return location for error or @a NULL.
1640 * @return @c TRUE on success, @c FALSE if an error was set.
1642 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1643 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1644 gchar **std_err, gint *exit_status, GError **error)
1646 gboolean result;
1648 if (argv == NULL)
1650 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1651 return FALSE;
1654 if (std_out)
1655 *std_out = NULL;
1657 if (std_err)
1658 *std_err = NULL;
1660 #ifdef G_OS_WIN32
1661 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1662 #else
1663 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1664 #endif
1666 return result;
1671 * Wraps g_spawn_async() and internally calls this function on Unix-like
1672 * systems. On Win32 platforms, it uses the Windows API.
1674 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1675 * @param argv The child's argument vector.
1676 * @param env The child's environment, or @a NULL to inherit parent's.
1677 * @param flags Flags from GSpawnFlags.
1678 * @param child_setup A function to run in the child just before exec().
1679 * @param user_data The user data for child_setup.
1680 * @param child_pid The return location for child process ID, or NULL.
1681 * @param error The return location for error or @a NULL.
1683 * @return @c TRUE on success, @c FALSE if an error was set.
1685 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1686 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1687 GError **error)
1689 gboolean result;
1691 if (argv == NULL)
1693 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1694 return FALSE;
1697 #ifdef G_OS_WIN32
1698 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1699 #else
1700 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1701 #endif
1702 return result;
1706 /* Retrieves the path for the given URI.
1707 * It returns:
1708 * - the path which was determined by g_filename_from_uri() or GIO
1709 * - NULL if the URI is non-local and gvfs-fuse is not installed
1710 * - a new copy of 'uri' if it is not an URI. */
1711 gchar *utils_get_path_from_uri(const gchar *uri)
1713 gchar *locale_filename;
1715 g_return_val_if_fail(uri != NULL, NULL);
1717 if (! utils_is_uri(uri))
1718 return g_strdup(uri);
1720 /* this will work only for 'file://' URIs */
1721 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1722 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1723 if (locale_filename == NULL)
1725 GFile *file = g_file_new_for_uri(uri);
1726 locale_filename = g_file_get_path(file);
1727 g_object_unref(file);
1728 if (locale_filename == NULL)
1730 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1731 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1735 return locale_filename;
1739 gboolean utils_is_uri(const gchar *uri)
1741 g_return_val_if_fail(uri != NULL, FALSE);
1743 return (strstr(uri, "://") != NULL);
1747 /* path should be in locale encoding */
1748 gboolean utils_is_remote_path(const gchar *path)
1750 g_return_val_if_fail(path != NULL, FALSE);
1752 /* if path is an URI and it doesn't start "file://", we take it as remote */
1753 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1754 return TRUE;
1756 #ifndef G_OS_WIN32
1758 static gchar *fuse_path = NULL;
1759 static gsize len = 0;
1761 if (G_UNLIKELY(fuse_path == NULL))
1763 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1764 len = strlen(fuse_path);
1766 /* Comparing the file path against a hardcoded path is not the most elegant solution
1767 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1768 * proper GFile objects for Fuse paths, but it only does in future GVFS
1769 * versions (gvfs 1.1.1). */
1770 return (strncmp(path, fuse_path, len) == 0);
1772 #endif
1774 return FALSE;
1778 /* Remove all relative and untidy elements from the path of @a filename.
1779 * @param filename must be a valid absolute path.
1780 * @see tm_get_real_path() - also resolves links. */
1781 void utils_tidy_path(gchar *filename)
1783 GString *str;
1784 const gchar *needle;
1785 gboolean preserve_double_backslash = FALSE;
1787 g_return_if_fail(g_path_is_absolute(filename));
1789 str = g_string_new(filename);
1791 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1792 preserve_double_backslash = TRUE;
1794 #ifdef G_OS_WIN32
1795 /* using MSYS we can get Unix-style separators */
1796 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1797 #endif
1798 /* replace "/./" and "//" */
1799 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1800 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1802 if (preserve_double_backslash)
1803 g_string_prepend(str, "\\");
1805 /* replace "/../" */
1806 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1807 while (1)
1809 const gchar *c = strstr(str->str, needle);
1810 if (c == NULL)
1811 break;
1812 else
1814 gssize pos, sub_len;
1816 pos = c - str->str;
1817 if (pos <= 3)
1818 break; /* bad path */
1820 /* replace "/../" */
1821 g_string_erase(str, pos, strlen(needle));
1822 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1824 /* search for last "/" before found "/../" */
1825 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1826 sub_len = pos - (c - str->str);
1827 if (! c)
1828 break; /* bad path */
1830 pos = c - str->str; /* position of previous "/" */
1831 g_string_erase(str, pos, sub_len);
1834 if (str->len <= strlen(filename))
1835 memcpy(filename, str->str, str->len + 1);
1836 else
1837 g_warn_if_reached();
1838 g_string_free(str, TRUE);
1843 * Removes characters from a string, in place.
1845 * @param string String to search.
1846 * @param chars Characters to remove.
1848 * @return @a string - return value is only useful when nesting function calls, e.g.:
1849 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1851 * @see @c g_strdelimit.
1853 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1855 const gchar *r;
1856 gchar *w = string;
1858 g_return_val_if_fail(string, NULL);
1859 if (G_UNLIKELY(EMPTY(chars)))
1860 return string;
1862 foreach_str(r, string)
1864 if (!strchr(chars, *r))
1865 *w++ = *r;
1867 *w = 0x0;
1868 return string;
1872 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1873 GSList *utils_get_config_files(const gchar *subdir)
1875 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1876 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1877 GSList *syslist, *node;
1879 if (!list)
1881 utils_mkdir(path, FALSE);
1883 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1884 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1885 /* merge lists */
1886 list = g_slist_concat(list, syslist);
1888 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1889 /* remove duplicates (next to each other after sorting) */
1890 foreach_slist(node, list)
1892 if (node->next && utils_str_equal(node->next->data, node->data))
1894 GSList *old = node->next;
1896 g_free(old->data);
1897 node->next = old->next;
1898 g_slist_free1(old);
1901 g_free(path);
1902 return list;
1906 /* Suffix can be NULL or a string which should be appended to the Help URL like
1907 * an anchor link, e.g. "#some_anchor". */
1908 gchar *utils_get_help_url(const gchar *suffix)
1910 gint skip;
1911 gchar *uri;
1913 #ifdef G_OS_WIN32
1914 skip = 8;
1915 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1916 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1917 #else
1918 skip = 7;
1919 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1920 #endif
1922 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1923 { /* fall back to online documentation if it is not found on the hard disk */
1924 g_free(uri);
1925 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1928 if (suffix != NULL)
1930 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1933 return uri;
1937 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1939 const gchar **p;
1941 for (p = haystack; *p != NULL; ++p)
1943 if (utils_str_equal(*p, needle))
1944 return TRUE;
1946 return FALSE;
1951 * Copies the current environment into a new array.
1952 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1953 * All further arguments are key, value pairs of variables which should be added to
1954 * the environment.
1956 * The argument list must be @c NULL-terminated.
1958 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1959 * @param first_varname Name of the first variable to copy into the new array.
1960 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1962 * @return The new environment array.
1964 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1966 gchar **result;
1967 gchar **p;
1968 gchar **env;
1969 va_list args;
1970 const gchar *key, *value;
1971 guint n, o;
1973 /* get all the environ variables */
1974 env = g_listenv();
1976 /* count the additional variables */
1977 va_start(args, first_varname);
1978 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1979 va_end(args);
1980 /* the passed arguments should be even (key, value pairs) */
1981 g_return_val_if_fail(o % 2 == 0, NULL);
1983 o /= 2;
1985 /* create an array large enough to hold the new environment */
1986 n = g_strv_length(env);
1987 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1988 result = g_new(gchar *, n + o + 1);
1990 /* copy the environment */
1991 for (n = 0, p = env; *p != NULL; ++p)
1993 /* copy the variable */
1994 value = g_getenv(*p);
1995 if (G_LIKELY(value != NULL))
1997 /* skip excluded variables */
1998 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1999 continue;
2001 result[n++] = g_strconcat(*p, "=", value, NULL);
2004 g_strfreev(env);
2006 /* now add additional variables */
2007 va_start(args, first_varname);
2008 key = first_varname;
2009 value = va_arg(args, gchar*);
2010 while (key != NULL)
2012 result[n++] = g_strconcat(key, "=", value, NULL);
2014 key = va_arg(args, gchar*);
2015 if (key == NULL)
2016 break;
2017 value = va_arg(args, gchar*);
2019 va_end(args);
2021 result[n] = NULL;
2023 return result;
2027 /* Joins @a first and @a second into a new string vector, freeing the originals.
2028 * The original contents are reused. */
2029 gchar **utils_strv_join(gchar **first, gchar **second)
2031 gchar **strv;
2032 gchar **rptr, **wptr;
2034 if (!first)
2035 return second;
2036 if (!second)
2037 return first;
2039 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2040 wptr = strv;
2042 foreach_strv(rptr, first)
2043 *wptr++ = *rptr;
2044 foreach_strv(rptr, second)
2045 *wptr++ = *rptr;
2047 g_free(first);
2048 g_free(second);
2049 return strv;
2053 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2054 * obviously g_date_set_parse() uses some magic.
2055 * The returned GDate object must be freed. */
2056 GDate *utils_parse_date(const gchar *input)
2058 GDate *date = g_date_new();
2060 g_date_set_parse(date, input);
2062 if (g_date_valid(date))
2063 return date;
2065 g_date_free(date);
2066 return NULL;
2070 gchar *utils_parse_and_format_build_date(const gchar *input)
2072 gchar date_buf[255];
2073 GDate *date = utils_parse_date(input);
2075 if (date != NULL)
2077 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2078 g_date_free(date);
2079 return g_strdup(date_buf);
2082 return g_strdup(input);