Merge branch 'scintilla-update-342'
[geany-mirror.git] / src / utils.c
blob4a0d2d15aa7e7a0748b87d4c0252327ed7675f04
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 return strcmp(a, b) == 0;
592 * Removes the extension from @a filename and return the result in a newly allocated string.
594 * @param filename The filename to operate on.
596 * @return A newly-allocated string, should be freed when no longer needed.
598 gchar *utils_remove_ext_from_filename(const gchar *filename)
600 gchar *last_dot;
601 gchar *result;
602 gsize len;
604 g_return_val_if_fail(filename != NULL, NULL);
606 last_dot = strrchr(filename, '.');
607 if (! last_dot)
608 return g_strdup(filename);
610 len = (gsize) (last_dot - filename);
611 result = g_malloc(len + 1);
612 memcpy(result, filename, len);
613 result[len] = 0;
615 return result;
619 gchar utils_brace_opposite(gchar ch)
621 switch (ch)
623 case '(': return ')';
624 case ')': return '(';
625 case '[': return ']';
626 case ']': return '[';
627 case '{': return '}';
628 case '}': return '{';
629 case '<': return '>';
630 case '>': return '<';
631 default: return '\0';
636 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
637 * Returns 0 if it can be written, otherwise it returns errno */
638 gint utils_is_file_writable(const gchar *locale_filename)
640 gchar *file;
641 gint ret;
643 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
644 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
645 /* get the file's directory to check for write permission if it doesn't yet exist */
646 file = g_path_get_dirname(locale_filename);
647 else
648 file = g_strdup(locale_filename);
650 #ifdef G_OS_WIN32
651 /* use _waccess on Windows, access() doesn't accept special characters */
652 ret = win32_check_write_permission(file);
653 #else
655 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
656 * errno only when access() explicitly returns an error */
657 if (access(file, R_OK | W_OK) != 0)
658 ret = errno;
659 else
660 ret = 0;
661 #endif
662 g_free(file);
663 return ret;
667 /* Replaces all occurrences of needle in haystack with replacement.
668 * Warning: *haystack must be a heap address; it may be freed and reassigned.
669 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
670 * than @a needle.
671 * All strings have to be NULL-terminated.
672 * See utils_string_replace_all() for details. */
673 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
675 GString *str;
677 g_return_if_fail(*haystack != NULL);
679 str = g_string_new(*haystack);
681 g_free(*haystack);
682 utils_string_replace_all(str, needle, replacement);
684 *haystack = g_string_free(str, FALSE);
688 gint utils_strpos(const gchar *haystack, const gchar *needle)
690 const gchar *sub;
692 if (! *needle)
693 return -1;
695 sub = strstr(haystack, needle);
696 if (! sub)
697 return -1;
699 return sub - haystack;
704 * Retrieves a formatted date/time string from strftime().
705 * This function should be preferred to directly calling strftime() since this function
706 * works on UTF-8 encoded strings.
708 * @param format The format string to pass to strftime(3). See the strftime(3)
709 * documentation for details, in UTF-8 encoding.
710 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
712 * @return A newly-allocated string, should be freed when no longer needed.
714 * @since 0.16
716 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
718 const struct tm *tm;
719 static gchar date[1024];
720 gchar *locale_format;
721 gsize len;
723 g_return_val_if_fail(format != NULL, NULL);
725 if (! g_utf8_validate(format, -1, NULL))
727 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
728 if (locale_format == NULL)
729 return NULL;
731 else
732 locale_format = g_strdup(format);
734 if (time_to_use != NULL)
735 tm = localtime(time_to_use);
736 else
738 time_t tp = time(NULL);
739 tm = localtime(&tp);
742 len = strftime(date, 1024, locale_format, tm);
743 g_free(locale_format);
744 if (len == 0)
745 return NULL;
747 if (! g_utf8_validate(date, len, NULL))
748 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
749 else
750 return g_strdup(date);
754 gchar *utils_get_initials(const gchar *name)
756 gint i = 1, j = 1;
757 gchar *initials = g_malloc0(5);
759 initials[0] = name[0];
760 while (name[i] != '\0' && j < 4)
762 if (name[i] == ' ' && name[i + 1] != ' ')
764 initials[j++] = name[i + 1];
766 i++;
768 return initials;
773 * Wraps g_key_file_get_integer() to add a default value argument.
775 * @param config A GKeyFile object.
776 * @param section The group name to look in for the key.
777 * @param key The key to find.
778 * @param default_value The default value which will be returned when @a section or @a key
779 * don't exist.
781 * @return The value associated with @a key as an integer, or the given default value if the value
782 * could not be retrieved.
784 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
785 const gint default_value)
787 gint tmp;
788 GError *error = NULL;
790 g_return_val_if_fail(config, default_value);
792 tmp = g_key_file_get_integer(config, section, key, &error);
793 if (error)
795 g_error_free(error);
796 return default_value;
798 return tmp;
803 * Wraps g_key_file_get_boolean() to add a default value argument.
805 * @param config A GKeyFile object.
806 * @param section The group name to look in for the key.
807 * @param key The key to find.
808 * @param default_value The default value which will be returned when @c section or @c key
809 * don't exist.
811 * @return The value associated with @a key as a boolean, or the given default value if the value
812 * could not be retrieved.
814 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
815 const gboolean default_value)
817 gboolean tmp;
818 GError *error = NULL;
820 g_return_val_if_fail(config, default_value);
822 tmp = g_key_file_get_boolean(config, section, key, &error);
823 if (error)
825 g_error_free(error);
826 return default_value;
828 return tmp;
833 * Wraps g_key_file_get_string() to add a default value argument.
835 * @param config A GKeyFile object.
836 * @param section The group name to look in for the key.
837 * @param key The key to find.
838 * @param default_value The default value which will be returned when @a section or @a key
839 * don't exist.
841 * @return A newly allocated string, either the value for @a key or a copy of the given
842 * default value if it could not be retrieved.
844 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
845 const gchar *default_value)
847 gchar *tmp;
849 g_return_val_if_fail(config, g_strdup(default_value));
851 tmp = g_key_file_get_string(config, section, key, NULL);
852 if (!tmp)
854 return g_strdup(default_value);
856 return tmp;
860 gchar *utils_get_hex_from_color(GdkColor *color)
862 g_return_val_if_fail(color != NULL, NULL);
864 return g_strdup_printf("#%02X%02X%02X",
865 (guint) (utils_scale_round(color->red / 256, 255)),
866 (guint) (utils_scale_round(color->green / 256, 255)),
867 (guint) (utils_scale_round(color->blue / 256, 255)));
871 /* Get directory from current file in the notebook.
872 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
873 * Returned string is in UTF-8 encoding */
874 gchar *utils_get_current_file_dir_utf8(void)
876 GeanyDocument *doc = document_get_current();
878 if (doc != NULL)
880 /* get current filename */
881 const gchar *cur_fname = doc->file_name;
883 if (cur_fname != NULL)
885 /* get folder part from current filename */
886 return g_path_get_dirname(cur_fname); /* returns "." if no path */
890 return NULL; /* no file open */
894 /* very simple convenience function */
895 void utils_beep(void)
897 if (prefs.beep_on_errors)
898 gdk_beep();
902 /* taken from busybox, thanks */
903 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
904 gulong display_unit)
906 /* The code will adjust for additional (appended) units. */
907 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
908 static const gchar fmt[] = "%Lu %c%c";
909 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
911 guint64 val;
912 gint frac;
913 const gchar *u;
914 const gchar *f;
916 u = zero_and_units;
917 f = fmt;
918 frac = 0;
920 val = size * block_size;
921 if (val == 0)
922 return g_strdup(u);
924 if (display_unit)
926 val += display_unit/2; /* Deal with rounding. */
927 val /= display_unit; /* Don't combine with the line above!!! */
929 else
931 ++u;
932 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
934 f = fmt_tenths;
935 ++u;
936 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
937 val /= 1024;
939 if (frac >= 10)
940 { /* We need to round up here. */
941 ++val;
942 frac = 0;
946 /* If f==fmt then 'frac' and 'u' are ignored. */
947 return g_strdup_printf(f, val, frac, *u, 'b');
951 /* converts a color representation using gdk_color_parse(), with additional
952 * support of the "0x" prefix as a synonym for "#" */
953 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
955 gchar buf[64] = {0};
957 g_return_val_if_fail(spec != NULL, -1);
959 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
961 /* convert to # format for GDK to understand it */
962 buf[0] = '#';
963 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
964 spec = buf;
967 return gdk_color_parse(spec, color);
971 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
972 * returns a 24 bits BGR color, or -1 on failure */
973 gint utils_color_to_bgr(const GdkColor *c)
975 g_return_val_if_fail(c != NULL, -1);
976 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
980 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
981 * utils_color_to_bgr() */
982 gint utils_parse_color_to_bgr(const gchar *spec)
984 GdkColor color;
985 if (utils_parse_color(spec, &color))
986 return utils_color_to_bgr(&color);
987 else
988 return -1;
992 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
993 gchar *utils_get_current_time_string(void)
995 const time_t tp = time(NULL);
996 const struct tm *tmval = localtime(&tp);
997 gchar *result = g_malloc0(9);
999 strftime(result, 9, "%H:%M:%S", tmval);
1000 result[8] = '\0';
1001 return result;
1005 GIOChannel *utils_set_up_io_channel(
1006 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1008 GIOChannel *ioc;
1009 /*const gchar *encoding;*/
1011 #ifdef G_OS_WIN32
1012 ioc = g_io_channel_win32_new_fd(fd);
1013 #else
1014 ioc = g_io_channel_unix_new(fd);
1015 #endif
1017 if (nblock)
1018 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1020 g_io_channel_set_encoding(ioc, NULL, NULL);
1022 if (! g_get_charset(&encoding))
1023 { // hope this works reliably
1024 GError *error = NULL;
1025 g_io_channel_set_encoding(ioc, encoding, &error);
1026 if (error)
1028 geany_debug("%s: %s", G_STRFUNC, error->message);
1029 g_error_free(error);
1030 return ioc;
1034 /* "auto-close" ;-) */
1035 g_io_channel_set_close_on_unref(ioc, TRUE);
1037 g_io_add_watch(ioc, cond, func, data);
1038 g_io_channel_unref(ioc);
1040 return ioc;
1044 gchar **utils_read_file_in_array(const gchar *filename)
1046 gchar **result = NULL;
1047 gchar *data;
1049 g_return_val_if_fail(filename != NULL, NULL);
1051 g_file_get_contents(filename, &data, NULL, NULL);
1053 if (data != NULL)
1055 result = g_strsplit_set(data, "\r\n", -1);
1056 g_free(data);
1059 return result;
1063 /* Contributed by Stefan Oltmanns, thanks.
1064 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1065 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1066 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1068 gsize i, j, len;
1069 guint unicodechar;
1071 g_return_val_if_fail(string != NULL, FALSE);
1073 j = 0;
1074 len = strlen(string);
1075 for (i = 0; i < len; i++)
1077 if (string[i]=='\\')
1079 if (i++ >= strlen(string))
1081 return FALSE;
1083 switch (string[i])
1085 case '\\':
1086 if (keep_backslash)
1087 string[j++] = '\\';
1088 string[j] = '\\';
1089 break;
1090 case 'n':
1091 string[j] = '\n';
1092 break;
1093 case 'r':
1094 string[j] = '\r';
1095 break;
1096 case 't':
1097 string[j] = '\t';
1098 break;
1099 #if 0
1100 case 'x': /* Warning: May produce illegal utf-8 string! */
1101 i += 2;
1102 if (i >= strlen(string))
1104 return FALSE;
1106 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1107 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1108 else return FALSE;
1109 string[j] <<= 4;
1110 if (isdigit(string[i])) string[j] |= string[i] - 48;
1111 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1112 else return FALSE;
1113 break;
1114 #endif
1115 case 'u':
1117 i += 2;
1118 if (i >= strlen(string))
1120 return FALSE;
1122 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1123 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1124 else return FALSE;
1125 unicodechar <<= 4;
1126 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1127 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1128 else return FALSE;
1129 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1130 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1132 i += 2;
1133 unicodechar <<= 8;
1134 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1135 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1136 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1137 else unicodechar |= tolower(string[i])-87;
1139 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1140 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1142 i += 2;
1143 unicodechar <<= 8;
1144 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1145 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1146 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1147 else unicodechar |= tolower(string[i])-87;
1149 if (unicodechar < 0x80)
1151 string[j] = unicodechar;
1153 else if (unicodechar < 0x800)
1155 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1156 j++;
1157 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1159 else if (unicodechar < 0x10000)
1161 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1162 j++;
1163 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1164 j++;
1165 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1167 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1169 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1170 j++;
1171 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1172 j++;
1173 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1174 j++;
1175 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1177 else
1179 return FALSE;
1181 break;
1183 default:
1184 /* unnecessary escapes are allowed */
1185 if (keep_backslash)
1186 string[j++] = '\\';
1187 string[j] = string[i];
1190 else
1192 string[j] = string[i];
1194 j++;
1196 while (j < i)
1198 string[j] = 0;
1199 j++;
1201 return TRUE;
1205 /* Wraps a string in place, replacing a space with a newline character.
1206 * wrapstart is the minimum position to start wrapping or -1 for default */
1207 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1209 gchar *pos, *linestart;
1210 gboolean ret = FALSE;
1212 if (wrapstart < 0)
1213 wrapstart = 80;
1215 for (pos = linestart = string; *pos != '\0'; pos++)
1217 if (pos - linestart >= wrapstart && *pos == ' ')
1219 *pos = '\n';
1220 linestart = pos;
1221 ret = TRUE;
1224 return ret;
1229 * Converts the given UTF-8 encoded string into locale encoding.
1230 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1232 * @param utf8_text UTF-8 encoded text.
1234 * @return The converted string in locale encoding, or a copy of the input string if conversion
1235 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1237 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1239 #ifdef G_OS_WIN32
1240 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1241 * which would result in wrongly converted strings */
1242 return g_strdup(utf8_text);
1243 #else
1244 gchar *locale_text;
1246 if (! utf8_text)
1247 return NULL;
1248 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1249 if (locale_text == NULL)
1250 locale_text = g_strdup(utf8_text);
1251 return locale_text;
1252 #endif
1257 * Converts the given string (in locale encoding) into UTF-8 encoding.
1258 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1260 * @param locale_text Text in locale encoding.
1262 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1263 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1265 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1267 #ifdef G_OS_WIN32
1268 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1269 * which would result in wrongly converted strings */
1270 return g_strdup(locale_text);
1271 #else
1272 gchar *utf8_text;
1274 if (! locale_text)
1275 return NULL;
1276 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1277 if (utf8_text == NULL)
1278 utf8_text = g_strdup(locale_text);
1279 return utf8_text;
1280 #endif
1284 /* Pass pointers to free after arg_count.
1285 * The last argument must be NULL as an extra check that arg_count is correct. */
1286 void utils_free_pointers(gsize arg_count, ...)
1288 va_list a;
1289 gsize i;
1290 gpointer ptr;
1292 va_start(a, arg_count);
1293 for (i = 0; i < arg_count; i++)
1295 ptr = va_arg(a, gpointer);
1296 g_free(ptr);
1298 ptr = va_arg(a, gpointer);
1299 if (ptr)
1300 g_warning("Wrong arg_count!");
1301 va_end(a);
1305 /* currently unused */
1306 #if 0
1307 /* Creates a string array deep copy of a series of non-NULL strings.
1308 * The first argument is nothing special.
1309 * The list must be ended with NULL.
1310 * If first is NULL, NULL is returned. */
1311 gchar **utils_strv_new(const gchar *first, ...)
1313 gsize strvlen, i;
1314 va_list args;
1315 gchar *str;
1316 gchar **strv;
1318 g_return_val_if_fail(first != NULL, NULL);
1320 strvlen = 1; /* for first argument */
1322 /* count other arguments */
1323 va_start(args, first);
1324 for (; va_arg(args, gchar*) != NULL; strvlen++);
1325 va_end(args);
1327 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1328 strv[0] = g_strdup(first);
1330 va_start(args, first);
1331 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1333 strv[i] = g_strdup(str);
1335 va_end(args);
1337 strv[i] = NULL;
1338 return strv;
1340 #endif
1344 * Creates a directory if it doesn't already exist.
1345 * Creates intermediate parent directories as needed, too.
1346 * The permissions of the created directory are set 0700.
1348 * @param path The path of the directory to create, in locale encoding.
1349 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1351 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1352 * failed operation is returned.
1354 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1356 gint mode = 0700;
1357 gint result;
1359 if (path == NULL || strlen(path) == 0)
1360 return EFAULT;
1362 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1363 if (result != 0)
1364 return errno;
1365 return 0;
1370 * Gets a list of files from the specified directory.
1371 * Locale encoding is expected for @a path and used for the file list. The list and the data
1372 * in the list should be freed after use, e.g.:
1373 * @code
1374 * g_slist_foreach(list, (GFunc) g_free, NULL);
1375 * g_slist_free(list); @endcode
1377 * @note If you don't need a list you should use the foreach_dir() macro instead -
1378 * it's more efficient.
1380 * @param path The path of the directory to scan, in locale encoding.
1381 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1382 * will use more memory.
1383 * @param sort Whether to sort alphabetically (UTF-8 safe).
1384 * @param error The location for storing a possible error, or @c NULL.
1386 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1387 * freed when no longer needed.
1388 * @see utils_get_file_list().
1390 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1392 GSList *list = NULL;
1393 GDir *dir;
1394 const gchar *filename;
1396 if (error)
1397 *error = NULL;
1398 g_return_val_if_fail(path != NULL, NULL);
1400 dir = g_dir_open(path, 0, error);
1401 if (dir == NULL)
1402 return NULL;
1404 foreach_dir(filename, dir)
1406 list = g_slist_prepend(list, full_path ?
1407 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1409 g_dir_close(dir);
1410 /* sorting last is quicker than on insertion */
1411 if (sort)
1412 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1413 return list;
1418 * Gets a sorted list of files from the specified directory.
1419 * Locale encoding is expected for @a path and used for the file list. The list and the data
1420 * in the list should be freed after use, e.g.:
1421 * @code
1422 * g_slist_foreach(list, (GFunc) g_free, NULL);
1423 * g_slist_free(list); @endcode
1425 * @param path The path of the directory to scan, in locale encoding.
1426 * @param length The location to store the number of non-@c NULL data items in the list,
1427 * unless @c NULL.
1428 * @param error The location for storing a possible error, or @c NULL.
1430 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1431 * freed when no longer needed.
1432 * @see utils_get_file_list_full().
1434 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1436 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1438 if (length)
1439 *length = g_slist_length(list);
1440 return list;
1444 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1445 gboolean utils_str_has_upper(const gchar *str)
1447 gunichar c;
1449 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1450 return FALSE;
1452 while (*str != '\0')
1454 c = g_utf8_get_char(str);
1455 /* check only letters and stop once the first non-capital was found */
1456 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1457 return TRUE;
1458 /* FIXME don't write a const string */
1459 str = g_utf8_next_char(str);
1461 return FALSE;
1465 /* end can be -1 for haystack->len.
1466 * returns: position of found text or -1. */
1467 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1469 gint pos;
1471 g_return_val_if_fail(haystack != NULL, -1);
1472 if (haystack->len == 0)
1473 return -1;
1475 g_return_val_if_fail(start >= 0, -1);
1476 if (start >= (gint)haystack->len)
1477 return -1;
1479 g_return_val_if_fail(!EMPTY(needle), -1);
1481 if (end < 0)
1482 end = haystack->len;
1484 pos = utils_strpos(haystack->str + start, needle);
1485 if (pos == -1)
1486 return -1;
1488 pos += start;
1489 if (pos >= end)
1490 return -1;
1491 return pos;
1495 /* Replaces @len characters from offset @a pos.
1496 * len can be -1 to replace the remainder of @a str.
1497 * returns: pos + strlen(replace). */
1498 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1500 g_string_erase(str, pos, len);
1501 if (replace)
1503 g_string_insert(str, pos, replace);
1504 pos += strlen(replace);
1506 return pos;
1511 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1512 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1513 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1515 * @param haystack The input string to operate on. This string is modified in place.
1516 * @param needle The string which should be replaced.
1517 * @param replace The replacement for @a needle.
1519 * @return Number of replacements made.
1521 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1523 guint count = 0;
1524 gint pos = 0;
1525 gsize needle_length = strlen(needle);
1527 while (1)
1529 pos = utils_string_find(haystack, pos, -1, needle);
1531 if (pos == -1)
1532 break;
1534 pos = utils_string_replace(haystack, pos, needle_length, replace);
1535 count++;
1537 return count;
1542 * Replaces only the first occurrence of @a needle in @a haystack
1543 * with @a replace.
1544 * For details, see utils_string_replace_all().
1546 * @param haystack The input string to operate on. This string is modified in place.
1547 * @param needle The string which should be replaced.
1548 * @param replace The replacement for @a needle.
1550 * @return Number of replacements made.
1552 * @since 0.16
1554 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1556 gint pos = utils_string_find(haystack, 0, -1, needle);
1558 if (pos == -1)
1559 return 0;
1561 utils_string_replace(haystack, pos, strlen(needle), replace);
1562 return 1;
1566 /* Similar to g_regex_replace but allows matching a subgroup.
1567 * match_num: which match to replace, 0 for whole match.
1568 * literal: FALSE to interpret escape sequences in @a replace.
1569 * returns: number of replacements.
1570 * bug: replaced text can affect matching of ^ or \b */
1571 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1572 guint match_num, const gchar *replace, gboolean literal)
1574 GMatchInfo *minfo;
1575 guint ret = 0;
1576 gint start = 0;
1578 g_assert(literal); /* escapes not implemented yet */
1579 g_return_val_if_fail(replace, 0);
1581 /* ensure haystack->str is not null */
1582 if (haystack->len == 0)
1583 return 0;
1585 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1586 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1588 gint end, len;
1590 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1591 len = end - start;
1592 utils_string_replace(haystack, start, len, replace);
1593 ret++;
1595 /* skip past whole match */
1596 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1597 start = end - len + strlen(replace);
1598 g_match_info_free(minfo);
1600 g_match_info_free(minfo);
1601 return ret;
1605 /* Get project or default startup directory (if set), or NULL. */
1606 const gchar *utils_get_default_dir_utf8(void)
1608 if (app->project && !EMPTY(app->project->base_path))
1610 return app->project->base_path;
1613 if (!EMPTY(prefs.default_open_path))
1615 return prefs.default_open_path;
1617 return NULL;
1622 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1623 * systems. On Win32 platforms, it uses the Windows API.
1625 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1626 * @param argv The child's argument vector.
1627 * @param env The child's environment, or @a NULL to inherit parent's.
1628 * @param flags Flags from GSpawnFlags.
1629 * @param child_setup A function to run in the child just before exec().
1630 * @param user_data The user data for child_setup.
1631 * @param std_out The return location for child output.
1632 * @param std_err The return location for child error messages.
1633 * @param exit_status The child exit status, as returned by waitpid().
1634 * @param error The return location for error or @a NULL.
1636 * @return @c TRUE on success, @c FALSE if an error was set.
1638 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1639 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1640 gchar **std_err, gint *exit_status, GError **error)
1642 gboolean result;
1644 if (argv == NULL)
1646 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1647 return FALSE;
1650 if (std_out)
1651 *std_out = NULL;
1653 if (std_err)
1654 *std_err = NULL;
1656 #ifdef G_OS_WIN32
1657 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1658 #else
1659 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1660 #endif
1662 return result;
1667 * Wraps g_spawn_async() and internally calls this function on Unix-like
1668 * systems. On Win32 platforms, it uses the Windows API.
1670 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1671 * @param argv The child's argument vector.
1672 * @param env The child's environment, or @a NULL to inherit parent's.
1673 * @param flags Flags from GSpawnFlags.
1674 * @param child_setup A function to run in the child just before exec().
1675 * @param user_data The user data for child_setup.
1676 * @param child_pid The return location for child process ID, or NULL.
1677 * @param error The return location for error or @a NULL.
1679 * @return @c TRUE on success, @c FALSE if an error was set.
1681 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1682 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1683 GError **error)
1685 gboolean result;
1687 if (argv == NULL)
1689 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1690 return FALSE;
1693 #ifdef G_OS_WIN32
1694 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1695 #else
1696 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1697 #endif
1698 return result;
1702 /* Retrieves the path for the given URI.
1703 * It returns:
1704 * - the path which was determined by g_filename_from_uri() or GIO
1705 * - NULL if the URI is non-local and gvfs-fuse is not installed
1706 * - a new copy of 'uri' if it is not an URI. */
1707 gchar *utils_get_path_from_uri(const gchar *uri)
1709 gchar *locale_filename;
1711 g_return_val_if_fail(uri != NULL, NULL);
1713 if (! utils_is_uri(uri))
1714 return g_strdup(uri);
1716 /* this will work only for 'file://' URIs */
1717 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1718 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1719 if (locale_filename == NULL)
1721 GFile *file = g_file_new_for_uri(uri);
1722 locale_filename = g_file_get_path(file);
1723 g_object_unref(file);
1724 if (locale_filename == NULL)
1726 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1727 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1731 return locale_filename;
1735 gboolean utils_is_uri(const gchar *uri)
1737 g_return_val_if_fail(uri != NULL, FALSE);
1739 return (strstr(uri, "://") != NULL);
1743 /* path should be in locale encoding */
1744 gboolean utils_is_remote_path(const gchar *path)
1746 g_return_val_if_fail(path != NULL, FALSE);
1748 /* if path is an URI and it doesn't start "file://", we take it as remote */
1749 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1750 return TRUE;
1752 #ifndef G_OS_WIN32
1754 static gchar *fuse_path = NULL;
1755 static gsize len = 0;
1757 if (G_UNLIKELY(fuse_path == NULL))
1759 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1760 len = strlen(fuse_path);
1762 /* Comparing the file path against a hardcoded path is not the most elegant solution
1763 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1764 * proper GFile objects for Fuse paths, but it only does in future GVFS
1765 * versions (gvfs 1.1.1). */
1766 return (strncmp(path, fuse_path, len) == 0);
1768 #endif
1770 return FALSE;
1774 /* Remove all relative and untidy elements from the path of @a filename.
1775 * @param filename must be a valid absolute path.
1776 * @see tm_get_real_path() - also resolves links. */
1777 void utils_tidy_path(gchar *filename)
1779 GString *str;
1780 const gchar *needle;
1781 gboolean preserve_double_backslash = FALSE;
1783 g_return_if_fail(g_path_is_absolute(filename));
1785 str = g_string_new(filename);
1787 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1788 preserve_double_backslash = TRUE;
1790 #ifdef G_OS_WIN32
1791 /* using MSYS we can get Unix-style separators */
1792 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1793 #endif
1794 /* replace "/./" and "//" */
1795 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1796 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1798 if (preserve_double_backslash)
1799 g_string_prepend(str, "\\");
1801 /* replace "/../" */
1802 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1803 while (1)
1805 const gchar *c = strstr(str->str, needle);
1806 if (c == NULL)
1807 break;
1808 else
1810 gssize pos, sub_len;
1812 pos = c - str->str;
1813 if (pos <= 3)
1814 break; /* bad path */
1816 /* replace "/../" */
1817 g_string_erase(str, pos, strlen(needle));
1818 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1820 /* search for last "/" before found "/../" */
1821 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1822 sub_len = pos - (c - str->str);
1823 if (! c)
1824 break; /* bad path */
1826 pos = c - str->str; /* position of previous "/" */
1827 g_string_erase(str, pos, sub_len);
1830 if (str->len <= strlen(filename))
1831 memcpy(filename, str->str, str->len + 1);
1832 else
1833 g_warn_if_reached();
1834 g_string_free(str, TRUE);
1839 * Removes characters from a string, in place.
1841 * @param string String to search.
1842 * @param chars Characters to remove.
1844 * @return @a string - return value is only useful when nesting function calls, e.g.:
1845 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1847 * @see @c g_strdelimit.
1849 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1851 const gchar *r;
1852 gchar *w = string;
1854 g_return_val_if_fail(string, NULL);
1855 if (G_UNLIKELY(EMPTY(chars)))
1856 return string;
1858 foreach_str(r, string)
1860 if (!strchr(chars, *r))
1861 *w++ = *r;
1863 *w = 0x0;
1864 return string;
1868 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1869 GSList *utils_get_config_files(const gchar *subdir)
1871 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1872 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1873 GSList *syslist, *node;
1875 if (!list)
1877 utils_mkdir(path, FALSE);
1879 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1880 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1881 /* merge lists */
1882 list = g_slist_concat(list, syslist);
1884 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1885 /* remove duplicates (next to each other after sorting) */
1886 foreach_slist(node, list)
1888 if (node->next && utils_str_equal(node->next->data, node->data))
1890 GSList *old = node->next;
1892 g_free(old->data);
1893 node->next = old->next;
1894 g_slist_free1(old);
1897 g_free(path);
1898 return list;
1902 /* Suffix can be NULL or a string which should be appended to the Help URL like
1903 * an anchor link, e.g. "#some_anchor". */
1904 gchar *utils_get_help_url(const gchar *suffix)
1906 gint skip;
1907 gchar *uri;
1909 #ifdef G_OS_WIN32
1910 skip = 8;
1911 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1912 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1913 #else
1914 skip = 7;
1915 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1916 #endif
1918 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1919 { /* fall back to online documentation if it is not found on the hard disk */
1920 g_free(uri);
1921 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1924 if (suffix != NULL)
1926 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1929 return uri;
1933 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1935 const gchar **p;
1937 for (p = haystack; *p != NULL; ++p)
1939 if (utils_str_equal(*p, needle))
1940 return TRUE;
1942 return FALSE;
1947 * Copies the current environment into a new array.
1948 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1949 * All further arguments are key, value pairs of variables which should be added to
1950 * the environment.
1952 * The argument list must be @c NULL-terminated.
1954 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1955 * @param first_varname Name of the first variable to copy into the new array.
1956 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1958 * @return The new environment array.
1960 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1962 gchar **result;
1963 gchar **p;
1964 gchar **env;
1965 va_list args;
1966 const gchar *key, *value;
1967 guint n, o;
1969 /* count the additional variables */
1970 va_start(args, first_varname);
1971 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1972 va_end(args);
1973 /* the passed arguments should be even (key, value pairs) */
1974 g_return_val_if_fail(o % 2 == 0, NULL);
1976 o /= 2;
1978 /* get all the environ variables */
1979 env = g_listenv();
1981 /* create an array large enough to hold the new environment */
1982 n = g_strv_length(env);
1983 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1984 result = g_new(gchar *, n + o + 1);
1986 /* copy the environment */
1987 for (n = 0, p = env; *p != NULL; ++p)
1989 /* copy the variable */
1990 value = g_getenv(*p);
1991 if (G_LIKELY(value != NULL))
1993 /* skip excluded variables */
1994 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1995 continue;
1997 result[n++] = g_strconcat(*p, "=", value, NULL);
2000 g_strfreev(env);
2002 /* now add additional variables */
2003 va_start(args, first_varname);
2004 key = first_varname;
2005 value = va_arg(args, gchar*);
2006 while (key != NULL)
2008 result[n++] = g_strconcat(key, "=", value, NULL);
2010 key = va_arg(args, gchar*);
2011 if (key == NULL)
2012 break;
2013 value = va_arg(args, gchar*);
2015 va_end(args);
2017 result[n] = NULL;
2019 return result;
2023 /* Joins @a first and @a second into a new string vector, freeing the originals.
2024 * The original contents are reused. */
2025 gchar **utils_strv_join(gchar **first, gchar **second)
2027 gchar **strv;
2028 gchar **rptr, **wptr;
2030 if (!first)
2031 return second;
2032 if (!second)
2033 return first;
2035 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2036 wptr = strv;
2038 foreach_strv(rptr, first)
2039 *wptr++ = *rptr;
2040 foreach_strv(rptr, second)
2041 *wptr++ = *rptr;
2043 g_free(first);
2044 g_free(second);
2045 return strv;
2049 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2050 * obviously g_date_set_parse() uses some magic.
2051 * The returned GDate object must be freed. */
2052 GDate *utils_parse_date(const gchar *input)
2054 GDate *date = g_date_new();
2056 g_date_set_parse(date, input);
2058 if (g_date_valid(date))
2059 return date;
2061 g_date_free(date);
2062 return NULL;
2066 gchar *utils_parse_and_format_build_date(const gchar *input)
2068 gchar date_buf[255];
2069 GDate *date = utils_parse_date(input);
2071 if (date != NULL)
2073 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2074 g_date_free(date);
2075 return g_strdup(date_buf);
2078 return g_strdup(input);