Update HACKING for changed doc generation instructions
[geany-mirror.git] / src / utils.c
blobe4c544842291fbbb014155ef501cdeb9fa180648
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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "utils.h"
32 #include "app.h"
33 #include "dialogs.h"
34 #include "document.h"
35 #include "prefs.h"
36 #include "sciwrappers.h"
37 #include "support.h"
38 #include "templates.h"
39 #include "ui_utils.h"
40 #include "win32.h"
42 #include <stdlib.h>
43 #include <ctype.h>
44 #include <math.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <stdarg.h>
50 #ifdef HAVE_SYS_STAT_H
51 # include <sys/stat.h>
52 #endif
53 #ifdef HAVE_SYS_TYPES_H
54 # include <sys/types.h>
55 #endif
57 #include <glib/gstdio.h>
58 #include <gio/gio.h>
61 /**
62 * Tries to open the given URI in a browser.
63 * On Windows, the system's default browser is opened.
64 * On non-Windows systems, the browser command set in the preferences dialog is used. In case
65 * that fails or it is unset, the user is asked to correct or fill it.
67 * @param uri The URI to open in the web browser.
69 * @since 0.16
70 **/
71 void utils_open_browser(const gchar *uri)
73 #ifdef G_OS_WIN32
74 g_return_if_fail(uri != NULL);
75 win32_open_browser(uri);
76 #else
77 gboolean again = TRUE;
79 g_return_if_fail(uri != NULL);
81 while (again)
83 gchar *cmdline = g_strconcat(tool_prefs.browser_cmd, " \"", uri, "\"", NULL);
85 if (g_spawn_command_line_async(cmdline, NULL))
86 again = FALSE;
87 else
89 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
90 _("Failed to spawn the configured browser command. "
91 "Please correct it or enter another one."),
92 tool_prefs.browser_cmd);
94 if (new_cmd == NULL) /* user canceled */
95 again = FALSE;
96 else
97 SETPTR(tool_prefs.browser_cmd, new_cmd);
99 g_free(cmdline);
101 #endif
105 /* taken from anjuta, to determine the EOL mode of the file */
106 gint utils_get_line_endings(const gchar* buffer, gsize size)
108 gsize i;
109 guint cr, lf, crlf, max_mode;
110 gint mode;
112 cr = lf = crlf = 0;
114 for (i = 0; i < size ; i++)
116 if (buffer[i] == 0x0a)
118 /* LF */
119 lf++;
121 else if (buffer[i] == 0x0d)
123 if (i >= (size - 1))
125 /* Last char, CR */
126 cr++;
128 else
130 if (buffer[i + 1] != 0x0a)
132 /* CR */
133 cr++;
135 else
137 /* CRLF */
138 crlf++;
140 i++;
145 /* Vote for the maximum */
146 mode = SC_EOL_LF;
147 max_mode = lf;
148 if (crlf > max_mode)
150 mode = SC_EOL_CRLF;
151 max_mode = crlf;
153 if (cr > max_mode)
155 mode = SC_EOL_CR;
156 max_mode = cr;
159 return mode;
163 gboolean utils_isbrace(gchar c, gboolean include_angles)
165 switch (c)
167 case '<':
168 case '>':
169 return include_angles;
171 case '(':
172 case ')':
173 case '{':
174 case '}':
175 case '[':
176 case ']': return TRUE;
177 default: return FALSE;
182 gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
184 switch (c)
186 case '<':
187 return include_angles;
189 case '(':
190 case '{':
191 case '[': return TRUE;
192 default: return FALSE;
198 * Writes @a text into a file named @a filename.
199 * If the file doesn't exist, it will be created.
200 * If it already exists, it will be overwritten.
202 * @warning You should use @c g_file_set_contents() instead if you don't need
203 * file permissions and other metadata to be preserved, as that always handles
204 * disk exhaustion safely.
206 * @param filename The filename of the file to write, in locale encoding.
207 * @param text The text to write into the file.
209 * @return 0 if the file was successfully written, otherwise the @c errno of the
210 * failed operation is returned.
212 gint utils_write_file(const gchar *filename, const gchar *text)
214 g_return_val_if_fail(filename != NULL, ENOENT);
215 g_return_val_if_fail(text != NULL, EINVAL);
217 if (file_prefs.use_safe_file_saving)
219 GError *error = NULL;
220 if (! g_file_set_contents(filename, text, -1, &error))
222 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
223 g_error_free(error);
224 return EIO;
227 else
229 FILE *fp;
230 gsize bytes_written, len;
231 gboolean fail = FALSE;
233 if (filename == NULL)
234 return ENOENT;
236 len = strlen(text);
237 errno = 0;
238 fp = g_fopen(filename, "w");
239 if (fp == NULL)
240 fail = TRUE;
241 else
243 bytes_written = fwrite(text, sizeof(gchar), len, fp);
245 if (len != bytes_written)
247 fail = TRUE;
248 geany_debug(
249 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
250 bytes_written, len, filename);
252 if (fclose(fp) != 0)
253 fail = TRUE;
255 if (fail)
257 geany_debug("utils_write_file(): could not write to file %s (%s)",
258 filename, g_strerror(errno));
259 return FALLBACK(errno, EIO);
262 return 0;
266 /** Searches backward through @a size bytes looking for a '<'.
267 * @param sel .
268 * @param size .
269 * @return The tag name (newly allocated) or @c NULL if no opening tag was found.
271 gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
273 const gchar *cur, *begin;
274 gsize len;
276 cur = utils_find_open_xml_tag_pos(sel, size);
277 if (cur == NULL)
278 return NULL;
280 cur++; /* skip the bracket */
281 begin = cur;
282 while (strchr(":_-.", *cur) || isalnum(*cur))
283 cur++;
285 len = (gsize)(cur - begin);
286 return len ? g_strndup(begin, len) : NULL;
290 /** Searches backward through @a size bytes looking for a '<'.
291 * @param sel .
292 * @param size .
293 * @return pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
295 const gchar *utils_find_open_xml_tag_pos(const gchar sel[], gint size)
297 /* stolen from anjuta and modified */
298 const gchar *begin, *cur;
300 if (G_UNLIKELY(size < 3))
301 { /* Smallest tag is "<p>" which is 3 characters */
302 return NULL;
304 begin = &sel[0];
305 cur = &sel[size - 1];
307 /* Skip to the character before the closing brace */
308 while (cur > begin)
310 if (*cur == '>')
311 break;
312 --cur;
314 --cur;
315 /* skip whitespace */
316 while (cur > begin && isspace(*cur))
317 cur--;
318 if (*cur == '/')
319 return NULL; /* we found a short tag which doesn't need to be closed */
320 while (cur > begin)
322 if (*cur == '<')
323 break;
324 /* exit immediately if such non-valid XML/HTML is detected, e.g. "<script>if a >" */
325 else if (*cur == '>')
326 break;
327 --cur;
330 /* if the found tag is an opening, not a closing tag or empty <> */
331 if (*cur == '<' && *(cur + 1) != '/' && *(cur + 1) != '>')
332 return cur;
334 return NULL;
338 /* Returns true if tag_name is a self-closing tag */
339 gboolean utils_is_short_html_tag(const gchar *tag_name)
341 const gchar names[][20] = {
342 "area",
343 "base",
344 "basefont", /* < or not < */
345 "br",
346 "col",
347 "command",
348 "embed",
349 "frame",
350 "hr",
351 "img",
352 "input",
353 "keygen",
354 "link",
355 "meta",
356 "param",
357 "source",
358 "track",
359 "wbr"
362 if (tag_name)
364 if (bsearch(tag_name, names, G_N_ELEMENTS(names), 20,
365 (GCompareFunc)g_ascii_strcasecmp))
366 return TRUE;
368 return FALSE;
372 const gchar *utils_get_eol_name(gint eol_mode)
374 switch (eol_mode)
376 case SC_EOL_CRLF: return _("Win (CRLF)"); break;
377 case SC_EOL_CR: return _("Mac (CR)"); break;
378 default: return _("Unix (LF)"); break;
383 const gchar *utils_get_eol_char(gint eol_mode)
385 switch (eol_mode)
387 case SC_EOL_CRLF: return "\r\n"; break;
388 case SC_EOL_CR: return "\r"; break;
389 default: return "\n"; break;
394 /* Converts line endings to @a target_eol_mode. */
395 void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
397 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
399 /* first convert data to LF only */
400 utils_string_replace_all(string, "\r\n", "\n");
401 utils_string_replace_all(string, "\r", "\n");
403 if (target_eol_mode == SC_EOL_LF)
404 return;
406 /* now convert to desired line endings */
407 utils_string_replace_all(string, "\n", eol_str);
411 gboolean utils_atob(const gchar *str)
413 if (G_UNLIKELY(str == NULL))
414 return FALSE;
415 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
416 return TRUE;
417 return FALSE;
421 /* NULL-safe version of g_path_is_absolute(). */
422 gboolean utils_is_absolute_path(const gchar *path)
424 if (G_UNLIKELY(EMPTY(path)))
425 return FALSE;
427 return g_path_is_absolute(path);
431 /* Skips root if path is absolute, do nothing otherwise.
432 * This is a relative-safe version of g_path_skip_root().
434 const gchar *utils_path_skip_root(const gchar *path)
436 const gchar *path_relative;
438 path_relative = g_path_skip_root(path);
440 return (path_relative != NULL) ? path_relative : path;
444 gdouble utils_scale_round(gdouble val, gdouble factor)
446 /*val = floor(val * factor + 0.5);*/
447 val = floor(val);
448 val = MAX(val, 0);
449 val = MIN(val, factor);
451 return val;
455 /* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
456 * returns NULL on charset conversion failure */
457 static gchar *utf8_strdown(const gchar *str)
459 gchar *down;
461 if (g_utf8_validate(str, -1, NULL))
462 down = g_utf8_strdown(str, -1);
463 else
465 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
466 if (down)
467 SETPTR(down, g_utf8_strdown(down, -1));
470 return down;
475 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
476 * It converts both strings into lowercase using g_utf8_strdown() and then compare
477 * both strings using strcmp().
478 * This is not completely accurate regarding locale-specific case sorting rules
479 * but seems to be a good compromise between correctness and performance.
481 * The input strings should be in UTF-8 or locale encoding.
483 * @param s1 Pointer to first string or @c NULL.
484 * @param s2 Pointer to second string or @c NULL.
486 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
487 * to be less than, to match, or to be greater than @a s2.
489 * @since 0.16
491 gint utils_str_casecmp(const gchar *s1, const gchar *s2)
493 gchar *tmp1, *tmp2;
494 gint result;
496 g_return_val_if_fail(s1 != NULL, 1);
497 g_return_val_if_fail(s2 != NULL, -1);
499 /* ensure strings are UTF-8 and lowercase */
500 tmp1 = utf8_strdown(s1);
501 if (! tmp1)
502 return 1;
503 tmp2 = utf8_strdown(s2);
504 if (! tmp2)
506 g_free(tmp1);
507 return -1;
510 /* compare */
511 result = strcmp(tmp1, tmp2);
513 g_free(tmp1);
514 g_free(tmp2);
515 return result;
520 * Truncates the input string to a given length.
521 * Characters are removed from the middle of the string, so the start and the end of string
522 * won't change.
524 * @param string Input string.
525 * @param truncate_length The length in characters of the resulting string.
527 * @return A copy of @a string which is truncated to @a truncate_length characters,
528 * should be freed when no longer needed.
530 * @since 0.17
532 /* This following function is taken from Gedit. */
533 gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
535 GString *truncated;
536 guint length;
537 guint n_chars;
538 guint num_left_chars;
539 guint right_offset;
540 guint delimiter_length;
541 const gchar *delimiter = "\342\200\246";
543 g_return_val_if_fail(string != NULL, NULL);
545 length = strlen(string);
547 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
549 /* It doesnt make sense to truncate strings to less than the size of the delimiter plus 2
550 * characters (one on each side) */
551 delimiter_length = g_utf8_strlen(delimiter, -1);
552 if (truncate_length < (delimiter_length + 2))
553 return g_strdup(string);
555 n_chars = g_utf8_strlen(string, length);
557 /* Make sure the string is not already small enough. */
558 if (n_chars <= truncate_length)
559 return g_strdup (string);
561 /* Find the 'middle' where the truncation will occur. */
562 num_left_chars = (truncate_length - delimiter_length) / 2;
563 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
565 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
566 g_string_append(truncated, delimiter);
567 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
569 return g_string_free(truncated, FALSE);
574 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
575 * or if @a a and @a b refer to valid strings which are equal.
577 * @param a Pointer to first string or @c NULL.
578 * @param b Pointer to second string or @c NULL.
580 * @return @c TRUE if @a a equals @a b, else @c FALSE.
582 gboolean utils_str_equal(const gchar *a, const gchar *b)
584 /* (taken from libexo from os-cillation) */
585 if (a == NULL && b == NULL) return TRUE;
586 else if (a == NULL || b == NULL) return FALSE;
588 return strcmp(a, b) == 0;
593 * Removes the extension from @a filename and return the result in a newly allocated string.
595 * @param filename The filename to operate on.
597 * @return A newly-allocated string, should be freed when no longer needed.
599 gchar *utils_remove_ext_from_filename(const gchar *filename)
601 gchar *last_dot;
602 gchar *result;
603 gsize len;
605 g_return_val_if_fail(filename != NULL, NULL);
607 last_dot = strrchr(filename, '.');
608 if (! last_dot)
609 return g_strdup(filename);
611 len = (gsize) (last_dot - filename);
612 result = g_malloc(len + 1);
613 memcpy(result, filename, len);
614 result[len] = 0;
616 return result;
620 gchar utils_brace_opposite(gchar ch)
622 switch (ch)
624 case '(': return ')';
625 case ')': return '(';
626 case '[': return ']';
627 case ']': return '[';
628 case '{': return '}';
629 case '}': return '{';
630 case '<': return '>';
631 case '>': return '<';
632 default: return '\0';
637 /* Checks whether the given file can be written. locale_filename is expected in locale encoding.
638 * Returns 0 if it can be written, otherwise it returns errno */
639 gint utils_is_file_writable(const gchar *locale_filename)
641 gchar *file;
642 gint ret;
644 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
645 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
646 /* get the file's directory to check for write permission if it doesn't yet exist */
647 file = g_path_get_dirname(locale_filename);
648 else
649 file = g_strdup(locale_filename);
651 #ifdef G_OS_WIN32
652 /* use _waccess on Windows, access() doesn't accept special characters */
653 ret = win32_check_write_permission(file);
654 #else
656 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
657 * errno only when access() explicitly returns an error */
658 if (access(file, R_OK | W_OK) != 0)
659 ret = errno;
660 else
661 ret = 0;
662 #endif
663 g_free(file);
664 return ret;
668 /* Replaces all occurrences of needle in haystack with replacement.
669 * Warning: *haystack must be a heap address; it may be freed and reassigned.
670 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
671 * than @a needle.
672 * All strings have to be NULL-terminated.
673 * See utils_string_replace_all() for details. */
674 void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
676 GString *str;
678 g_return_if_fail(*haystack != NULL);
680 str = g_string_new(*haystack);
682 g_free(*haystack);
683 utils_string_replace_all(str, needle, replacement);
685 *haystack = g_string_free(str, FALSE);
689 gint utils_strpos(const gchar *haystack, const gchar *needle)
691 const gchar *sub;
693 if (! *needle)
694 return -1;
696 sub = strstr(haystack, needle);
697 if (! sub)
698 return -1;
700 return sub - haystack;
705 * Retrieves a formatted date/time string from strftime().
706 * This function should be preferred to directly calling strftime() since this function
707 * works on UTF-8 encoded strings.
709 * @param format The format string to pass to strftime(3). See the strftime(3)
710 * documentation for details, in UTF-8 encoding.
711 * @param time_to_use The date/time to use, in time_t format or NULL to use the current time.
713 * @return A newly-allocated string, should be freed when no longer needed.
715 * @since 0.16
717 gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
719 const struct tm *tm;
720 static gchar date[1024];
721 gchar *locale_format;
722 gsize len;
724 g_return_val_if_fail(format != NULL, NULL);
726 if (! g_utf8_validate(format, -1, NULL))
728 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
729 if (locale_format == NULL)
730 return NULL;
732 else
733 locale_format = g_strdup(format);
735 if (time_to_use != NULL)
736 tm = localtime(time_to_use);
737 else
739 time_t tp = time(NULL);
740 tm = localtime(&tp);
743 len = strftime(date, 1024, locale_format, tm);
744 g_free(locale_format);
745 if (len == 0)
746 return NULL;
748 if (! g_utf8_validate(date, len, NULL))
749 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
750 else
751 return g_strdup(date);
755 gchar *utils_get_initials(const gchar *name)
757 gint i = 1, j = 1;
758 gchar *initials = g_malloc0(5);
760 initials[0] = name[0];
761 while (name[i] != '\0' && j < 4)
763 if (name[i] == ' ' && name[i + 1] != ' ')
765 initials[j++] = name[i + 1];
767 i++;
769 return initials;
774 * Wraps g_key_file_get_integer() to add a default value argument.
776 * @param config A GKeyFile object.
777 * @param section The group name to look in for the key.
778 * @param key The key to find.
779 * @param default_value The default value which will be returned when @a section or @a key
780 * don't exist.
782 * @return The value associated with @a key as an integer, or the given default value if the value
783 * could not be retrieved.
785 gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
786 const gint default_value)
788 gint tmp;
789 GError *error = NULL;
791 g_return_val_if_fail(config, default_value);
793 tmp = g_key_file_get_integer(config, section, key, &error);
794 if (error)
796 g_error_free(error);
797 return default_value;
799 return tmp;
804 * Wraps g_key_file_get_boolean() to add a default value argument.
806 * @param config A GKeyFile object.
807 * @param section The group name to look in for the key.
808 * @param key The key to find.
809 * @param default_value The default value which will be returned when @c section or @c key
810 * don't exist.
812 * @return The value associated with @a key as a boolean, or the given default value if the value
813 * could not be retrieved.
815 gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
816 const gboolean default_value)
818 gboolean tmp;
819 GError *error = NULL;
821 g_return_val_if_fail(config, default_value);
823 tmp = g_key_file_get_boolean(config, section, key, &error);
824 if (error)
826 g_error_free(error);
827 return default_value;
829 return tmp;
834 * Wraps g_key_file_get_string() to add a default value argument.
836 * @param config A GKeyFile object.
837 * @param section The group name to look in for the key.
838 * @param key The key to find.
839 * @param default_value The default value which will be returned when @a section or @a key
840 * don't exist.
842 * @return A newly allocated string, either the value for @a key or a copy of the given
843 * default value if it could not be retrieved.
845 gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
846 const gchar *default_value)
848 gchar *tmp;
850 g_return_val_if_fail(config, g_strdup(default_value));
852 tmp = g_key_file_get_string(config, section, key, NULL);
853 if (!tmp)
855 return g_strdup(default_value);
857 return tmp;
861 gchar *utils_get_hex_from_color(GdkColor *color)
863 g_return_val_if_fail(color != NULL, NULL);
865 return g_strdup_printf("#%02X%02X%02X",
866 (guint) (utils_scale_round(color->red / 256, 255)),
867 (guint) (utils_scale_round(color->green / 256, 255)),
868 (guint) (utils_scale_round(color->blue / 256, 255)));
872 /* Get directory from current file in the notebook.
873 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
874 * Returned string is in UTF-8 encoding */
875 gchar *utils_get_current_file_dir_utf8(void)
877 GeanyDocument *doc = document_get_current();
879 if (doc != NULL)
881 /* get current filename */
882 const gchar *cur_fname = doc->file_name;
884 if (cur_fname != NULL)
886 /* get folder part from current filename */
887 return g_path_get_dirname(cur_fname); /* returns "." if no path */
891 return NULL; /* no file open */
895 /* very simple convenience function */
896 void utils_beep(void)
898 if (prefs.beep_on_errors)
899 gdk_beep();
903 /* taken from busybox, thanks */
904 gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
905 gulong display_unit)
907 /* The code will adjust for additional (appended) units. */
908 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
909 static const gchar fmt[] = "%Lu %c%c";
910 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
912 guint64 val;
913 gint frac;
914 const gchar *u;
915 const gchar *f;
917 u = zero_and_units;
918 f = fmt;
919 frac = 0;
921 val = size * block_size;
922 if (val == 0)
923 return g_strdup(u);
925 if (display_unit)
927 val += display_unit/2; /* Deal with rounding. */
928 val /= display_unit; /* Don't combine with the line above!!! */
930 else
932 ++u;
933 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
935 f = fmt_tenths;
936 ++u;
937 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
938 val /= 1024;
940 if (frac >= 10)
941 { /* We need to round up here. */
942 ++val;
943 frac = 0;
947 /* If f==fmt then 'frac' and 'u' are ignored. */
948 return g_strdup_printf(f, val, frac, *u, 'b');
952 /* converts a color representation using gdk_color_parse(), with additional
953 * support of the "0x" prefix as a synonym for "#" */
954 gboolean utils_parse_color(const gchar *spec, GdkColor *color)
956 gchar buf[64] = {0};
958 g_return_val_if_fail(spec != NULL, -1);
960 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
962 /* convert to # format for GDK to understand it */
963 buf[0] = '#';
964 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
965 spec = buf;
968 return gdk_color_parse(spec, color);
972 /* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
973 * returns a 24 bits BGR color, or -1 on failure */
974 gint utils_color_to_bgr(const GdkColor *c)
976 g_return_val_if_fail(c != NULL, -1);
977 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
981 /* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
982 * utils_color_to_bgr() */
983 gint utils_parse_color_to_bgr(const gchar *spec)
985 GdkColor color;
986 if (utils_parse_color(spec, &color))
987 return utils_color_to_bgr(&color);
988 else
989 return -1;
993 /* Returns: newly allocated string with the current time formatted HH:MM:SS. */
994 gchar *utils_get_current_time_string(void)
996 const time_t tp = time(NULL);
997 const struct tm *tmval = localtime(&tp);
998 gchar *result = g_malloc0(9);
1000 strftime(result, 9, "%H:%M:%S", tmval);
1001 result[8] = '\0';
1002 return result;
1006 GIOChannel *utils_set_up_io_channel(
1007 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1009 GIOChannel *ioc;
1010 /*const gchar *encoding;*/
1012 #ifdef G_OS_WIN32
1013 ioc = g_io_channel_win32_new_fd(fd);
1014 #else
1015 ioc = g_io_channel_unix_new(fd);
1016 #endif
1018 if (nblock)
1019 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1021 g_io_channel_set_encoding(ioc, NULL, NULL);
1023 if (! g_get_charset(&encoding))
1024 { // hope this works reliably
1025 GError *error = NULL;
1026 g_io_channel_set_encoding(ioc, encoding, &error);
1027 if (error)
1029 geany_debug("%s: %s", G_STRFUNC, error->message);
1030 g_error_free(error);
1031 return ioc;
1035 /* "auto-close" ;-) */
1036 g_io_channel_set_close_on_unref(ioc, TRUE);
1038 g_io_add_watch(ioc, cond, func, data);
1039 g_io_channel_unref(ioc);
1041 return ioc;
1045 gchar **utils_read_file_in_array(const gchar *filename)
1047 gchar **result = NULL;
1048 gchar *data;
1050 g_return_val_if_fail(filename != NULL, NULL);
1052 g_file_get_contents(filename, &data, NULL, NULL);
1054 if (data != NULL)
1056 result = g_strsplit_set(data, "\r\n", -1);
1057 g_free(data);
1060 return result;
1064 /* Contributed by Stefan Oltmanns, thanks.
1065 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1066 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1067 gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1069 gsize i, j, len;
1070 guint unicodechar;
1072 g_return_val_if_fail(string != NULL, FALSE);
1074 j = 0;
1075 len = strlen(string);
1076 for (i = 0; i < len; i++)
1078 if (string[i]=='\\')
1080 if (i++ >= strlen(string))
1082 return FALSE;
1084 switch (string[i])
1086 case '\\':
1087 if (keep_backslash)
1088 string[j++] = '\\';
1089 string[j] = '\\';
1090 break;
1091 case 'n':
1092 string[j] = '\n';
1093 break;
1094 case 'r':
1095 string[j] = '\r';
1096 break;
1097 case 't':
1098 string[j] = '\t';
1099 break;
1100 #if 0
1101 case 'x': /* Warning: May produce illegal utf-8 string! */
1102 i += 2;
1103 if (i >= strlen(string))
1105 return FALSE;
1107 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1108 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1109 else return FALSE;
1110 string[j] <<= 4;
1111 if (isdigit(string[i])) string[j] |= string[i] - 48;
1112 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1113 else return FALSE;
1114 break;
1115 #endif
1116 case 'u':
1118 i += 2;
1119 if (i >= strlen(string))
1121 return FALSE;
1123 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1124 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1125 else return FALSE;
1126 unicodechar <<= 4;
1127 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1128 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1129 else return FALSE;
1130 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1131 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1133 i += 2;
1134 unicodechar <<= 8;
1135 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1136 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1137 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1138 else unicodechar |= tolower(string[i])-87;
1140 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1141 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1143 i += 2;
1144 unicodechar <<= 8;
1145 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1146 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1147 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1148 else unicodechar |= tolower(string[i])-87;
1150 if (unicodechar < 0x80)
1152 string[j] = unicodechar;
1154 else if (unicodechar < 0x800)
1156 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1157 j++;
1158 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1160 else if (unicodechar < 0x10000)
1162 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1163 j++;
1164 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1165 j++;
1166 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1168 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1170 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1171 j++;
1172 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1173 j++;
1174 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1175 j++;
1176 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1178 else
1180 return FALSE;
1182 break;
1184 default:
1185 /* unnecessary escapes are allowed */
1186 if (keep_backslash)
1187 string[j++] = '\\';
1188 string[j] = string[i];
1191 else
1193 string[j] = string[i];
1195 j++;
1197 while (j < i)
1199 string[j] = 0;
1200 j++;
1202 return TRUE;
1206 /* Wraps a string in place, replacing a space with a newline character.
1207 * wrapstart is the minimum position to start wrapping or -1 for default */
1208 gboolean utils_wrap_string(gchar *string, gint wrapstart)
1210 gchar *pos, *linestart;
1211 gboolean ret = FALSE;
1213 if (wrapstart < 0)
1214 wrapstart = 80;
1216 for (pos = linestart = string; *pos != '\0'; pos++)
1218 if (pos - linestart >= wrapstart && *pos == ' ')
1220 *pos = '\n';
1221 linestart = pos;
1222 ret = TRUE;
1225 return ret;
1230 * Converts the given UTF-8 encoded string into locale encoding.
1231 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1233 * @param utf8_text UTF-8 encoded text.
1235 * @return The converted string in locale encoding, or a copy of the input string if conversion
1236 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1238 gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1240 #ifdef G_OS_WIN32
1241 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1242 * which would result in wrongly converted strings */
1243 return g_strdup(utf8_text);
1244 #else
1245 gchar *locale_text;
1247 if (! utf8_text)
1248 return NULL;
1249 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1250 if (locale_text == NULL)
1251 locale_text = g_strdup(utf8_text);
1252 return locale_text;
1253 #endif
1258 * Converts the given string (in locale encoding) into UTF-8 encoding.
1259 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1261 * @param locale_text Text in locale encoding.
1263 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1264 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1266 gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1268 #ifdef G_OS_WIN32
1269 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1270 * which would result in wrongly converted strings */
1271 return g_strdup(locale_text);
1272 #else
1273 gchar *utf8_text;
1275 if (! locale_text)
1276 return NULL;
1277 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1278 if (utf8_text == NULL)
1279 utf8_text = g_strdup(locale_text);
1280 return utf8_text;
1281 #endif
1285 /* Pass pointers to free after arg_count.
1286 * The last argument must be NULL as an extra check that arg_count is correct. */
1287 void utils_free_pointers(gsize arg_count, ...)
1289 va_list a;
1290 gsize i;
1291 gpointer ptr;
1293 va_start(a, arg_count);
1294 for (i = 0; i < arg_count; i++)
1296 ptr = va_arg(a, gpointer);
1297 g_free(ptr);
1299 ptr = va_arg(a, gpointer);
1300 if (ptr)
1301 g_warning("Wrong arg_count!");
1302 va_end(a);
1306 /* currently unused */
1307 #if 0
1308 /* Creates a string array deep copy of a series of non-NULL strings.
1309 * The first argument is nothing special.
1310 * The list must be ended with NULL.
1311 * If first is NULL, NULL is returned. */
1312 gchar **utils_strv_new(const gchar *first, ...)
1314 gsize strvlen, i;
1315 va_list args;
1316 gchar *str;
1317 gchar **strv;
1319 g_return_val_if_fail(first != NULL, NULL);
1321 strvlen = 1; /* for first argument */
1323 /* count other arguments */
1324 va_start(args, first);
1325 for (; va_arg(args, gchar*) != NULL; strvlen++);
1326 va_end(args);
1328 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1329 strv[0] = g_strdup(first);
1331 va_start(args, first);
1332 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1334 strv[i] = g_strdup(str);
1336 va_end(args);
1338 strv[i] = NULL;
1339 return strv;
1341 #endif
1345 * Creates a directory if it doesn't already exist.
1346 * Creates intermediate parent directories as needed, too.
1347 * The permissions of the created directory are set 0700.
1349 * @param path The path of the directory to create, in locale encoding.
1350 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1352 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1353 * failed operation is returned.
1355 gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1357 gint mode = 0700;
1358 gint result;
1360 if (path == NULL || strlen(path) == 0)
1361 return EFAULT;
1363 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1364 if (result != 0)
1365 return errno;
1366 return 0;
1371 * Gets a list of files from the specified directory.
1372 * Locale encoding is expected for @a path and used for the file list. The list and the data
1373 * in the list should be freed after use, e.g.:
1374 * @code
1375 * g_slist_foreach(list, (GFunc) g_free, NULL);
1376 * g_slist_free(list); @endcode
1378 * @note If you don't need a list you should use the foreach_dir() macro instead -
1379 * it's more efficient.
1381 * @param path The path of the directory to scan, in locale encoding.
1382 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1383 * will use more memory.
1384 * @param sort Whether to sort alphabetically (UTF-8 safe).
1385 * @param error The location for storing a possible error, or @c NULL.
1387 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1388 * freed when no longer needed.
1389 * @see utils_get_file_list().
1391 GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1393 GSList *list = NULL;
1394 GDir *dir;
1395 const gchar *filename;
1397 if (error)
1398 *error = NULL;
1399 g_return_val_if_fail(path != NULL, NULL);
1401 dir = g_dir_open(path, 0, error);
1402 if (dir == NULL)
1403 return NULL;
1405 foreach_dir(filename, dir)
1407 list = g_slist_prepend(list, full_path ?
1408 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1410 g_dir_close(dir);
1411 /* sorting last is quicker than on insertion */
1412 if (sort)
1413 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1414 return list;
1419 * Gets a sorted list of files from the specified directory.
1420 * Locale encoding is expected for @a path and used for the file list. The list and the data
1421 * in the list should be freed after use, e.g.:
1422 * @code
1423 * g_slist_foreach(list, (GFunc) g_free, NULL);
1424 * g_slist_free(list); @endcode
1426 * @param path The path of the directory to scan, in locale encoding.
1427 * @param length The location to store the number of non-@c NULL data items in the list,
1428 * unless @c NULL.
1429 * @param error The location for storing a possible error, or @c NULL.
1431 * @return A newly allocated list or @c NULL if no files were found. The list and its data should be
1432 * freed when no longer needed.
1433 * @see utils_get_file_list_full().
1435 GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1437 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1439 if (length)
1440 *length = g_slist_length(list);
1441 return list;
1445 /* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1446 gboolean utils_str_has_upper(const gchar *str)
1448 gunichar c;
1450 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1451 return FALSE;
1453 while (*str != '\0')
1455 c = g_utf8_get_char(str);
1456 /* check only letters and stop once the first non-capital was found */
1457 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1458 return TRUE;
1459 /* FIXME don't write a const string */
1460 str = g_utf8_next_char(str);
1462 return FALSE;
1466 /* end can be -1 for haystack->len.
1467 * returns: position of found text or -1. */
1468 gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1470 gint pos;
1472 g_return_val_if_fail(haystack != NULL, -1);
1473 if (haystack->len == 0)
1474 return -1;
1476 g_return_val_if_fail(start >= 0, -1);
1477 if (start >= (gint)haystack->len)
1478 return -1;
1480 g_return_val_if_fail(!EMPTY(needle), -1);
1482 if (end < 0)
1483 end = haystack->len;
1485 pos = utils_strpos(haystack->str + start, needle);
1486 if (pos == -1)
1487 return -1;
1489 pos += start;
1490 if (pos >= end)
1491 return -1;
1492 return pos;
1496 /* Replaces @len characters from offset @a pos.
1497 * len can be -1 to replace the remainder of @a str.
1498 * returns: pos + strlen(replace). */
1499 gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1501 g_string_erase(str, pos, len);
1502 if (replace)
1504 g_string_insert(str, pos, replace);
1505 pos += strlen(replace);
1507 return pos;
1512 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1513 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1514 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1516 * @param haystack The input string to operate on. This string is modified in place.
1517 * @param needle The string which should be replaced.
1518 * @param replace The replacement for @a needle.
1520 * @return Number of replacements made.
1522 guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1524 guint count = 0;
1525 gint pos = 0;
1526 gsize needle_length = strlen(needle);
1528 while (1)
1530 pos = utils_string_find(haystack, pos, -1, needle);
1532 if (pos == -1)
1533 break;
1535 pos = utils_string_replace(haystack, pos, needle_length, replace);
1536 count++;
1538 return count;
1543 * Replaces only the first occurrence of @a needle in @a haystack
1544 * with @a replace.
1545 * For details, see utils_string_replace_all().
1547 * @param haystack The input string to operate on. This string is modified in place.
1548 * @param needle The string which should be replaced.
1549 * @param replace The replacement for @a needle.
1551 * @return Number of replacements made.
1553 * @since 0.16
1555 guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1557 gint pos = utils_string_find(haystack, 0, -1, needle);
1559 if (pos == -1)
1560 return 0;
1562 utils_string_replace(haystack, pos, strlen(needle), replace);
1563 return 1;
1567 /* Similar to g_regex_replace but allows matching a subgroup.
1568 * match_num: which match to replace, 0 for whole match.
1569 * literal: FALSE to interpret escape sequences in @a replace.
1570 * returns: number of replacements.
1571 * bug: replaced text can affect matching of ^ or \b */
1572 guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1573 guint match_num, const gchar *replace, gboolean literal)
1575 GMatchInfo *minfo;
1576 guint ret = 0;
1577 gint start = 0;
1579 g_assert(literal); /* escapes not implemented yet */
1580 g_return_val_if_fail(replace, 0);
1582 /* ensure haystack->str is not null */
1583 if (haystack->len == 0)
1584 return 0;
1586 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1587 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1589 gint end, len;
1591 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1592 len = end - start;
1593 utils_string_replace(haystack, start, len, replace);
1594 ret++;
1596 /* skip past whole match */
1597 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1598 start = end - len + strlen(replace);
1599 g_match_info_free(minfo);
1601 g_match_info_free(minfo);
1602 return ret;
1606 /* Get project or default startup directory (if set), or NULL. */
1607 const gchar *utils_get_default_dir_utf8(void)
1609 if (app->project && !EMPTY(app->project->base_path))
1611 return app->project->base_path;
1614 if (!EMPTY(prefs.default_open_path))
1616 return prefs.default_open_path;
1618 return NULL;
1623 * Wraps g_spawn_sync() and internally calls this function on Unix-like
1624 * systems. On Win32 platforms, it uses the Windows API.
1626 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1627 * @param argv The child's argument vector.
1628 * @param env The child's environment, or @a NULL to inherit parent's.
1629 * @param flags Flags from GSpawnFlags.
1630 * @param child_setup A function to run in the child just before exec().
1631 * @param user_data The user data for child_setup.
1632 * @param std_out The return location for child output.
1633 * @param std_err The return location for child error messages.
1634 * @param exit_status The child exit status, as returned by waitpid().
1635 * @param error The return location for error or @a NULL.
1637 * @return @c TRUE on success, @c FALSE if an error was set.
1639 gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1640 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1641 gchar **std_err, gint *exit_status, GError **error)
1643 gboolean result;
1645 if (argv == NULL)
1647 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1648 return FALSE;
1651 if (std_out)
1652 *std_out = NULL;
1654 if (std_err)
1655 *std_err = NULL;
1657 #ifdef G_OS_WIN32
1658 result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1659 #else
1660 result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1661 #endif
1663 return result;
1668 * Wraps g_spawn_async() and internally calls this function on Unix-like
1669 * systems. On Win32 platforms, it uses the Windows API.
1671 * @param dir The child's current working directory, or @a NULL to inherit parent's.
1672 * @param argv The child's argument vector.
1673 * @param env The child's environment, or @a NULL to inherit parent's.
1674 * @param flags Flags from GSpawnFlags.
1675 * @param child_setup A function to run in the child just before exec().
1676 * @param user_data The user data for child_setup.
1677 * @param child_pid The return location for child process ID, or NULL.
1678 * @param error The return location for error or @a NULL.
1680 * @return @c TRUE on success, @c FALSE if an error was set.
1682 gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1683 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1684 GError **error)
1686 gboolean result;
1688 if (argv == NULL)
1690 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1691 return FALSE;
1694 #ifdef G_OS_WIN32
1695 result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1696 #else
1697 result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
1698 #endif
1699 return result;
1703 /* Retrieves the path for the given URI.
1704 * It returns:
1705 * - the path which was determined by g_filename_from_uri() or GIO
1706 * - NULL if the URI is non-local and gvfs-fuse is not installed
1707 * - a new copy of 'uri' if it is not an URI. */
1708 gchar *utils_get_path_from_uri(const gchar *uri)
1710 gchar *locale_filename;
1712 g_return_val_if_fail(uri != NULL, NULL);
1714 if (! utils_is_uri(uri))
1715 return g_strdup(uri);
1717 /* this will work only for 'file://' URIs */
1718 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1719 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1720 if (locale_filename == NULL)
1722 GFile *file = g_file_new_for_uri(uri);
1723 locale_filename = g_file_get_path(file);
1724 g_object_unref(file);
1725 if (locale_filename == NULL)
1727 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1728 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1732 return locale_filename;
1736 gboolean utils_is_uri(const gchar *uri)
1738 g_return_val_if_fail(uri != NULL, FALSE);
1740 return (strstr(uri, "://") != NULL);
1744 /* path should be in locale encoding */
1745 gboolean utils_is_remote_path(const gchar *path)
1747 g_return_val_if_fail(path != NULL, FALSE);
1749 /* if path is an URI and it doesn't start "file://", we take it as remote */
1750 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1751 return TRUE;
1753 #ifndef G_OS_WIN32
1755 static gchar *fuse_path = NULL;
1756 static gsize len = 0;
1758 if (G_UNLIKELY(fuse_path == NULL))
1760 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1761 len = strlen(fuse_path);
1763 /* Comparing the file path against a hardcoded path is not the most elegant solution
1764 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1765 * proper GFile objects for Fuse paths, but it only does in future GVFS
1766 * versions (gvfs 1.1.1). */
1767 return (strncmp(path, fuse_path, len) == 0);
1769 #endif
1771 return FALSE;
1775 /* Remove all relative and untidy elements from the path of @a filename.
1776 * @param filename must be a valid absolute path.
1777 * @see tm_get_real_path() - also resolves links. */
1778 void utils_tidy_path(gchar *filename)
1780 GString *str;
1781 const gchar *needle;
1782 gboolean preserve_double_backslash = FALSE;
1784 g_return_if_fail(g_path_is_absolute(filename));
1786 str = g_string_new(filename);
1788 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1789 preserve_double_backslash = TRUE;
1791 #ifdef G_OS_WIN32
1792 /* using MSYS we can get Unix-style separators */
1793 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1794 #endif
1795 /* replace "/./" and "//" */
1796 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1797 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1799 if (preserve_double_backslash)
1800 g_string_prepend(str, "\\");
1802 /* replace "/../" */
1803 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1804 while (1)
1806 const gchar *c = strstr(str->str, needle);
1807 if (c == NULL)
1808 break;
1809 else
1811 gssize pos, sub_len;
1813 pos = c - str->str;
1814 if (pos <= 3)
1815 break; /* bad path */
1817 /* replace "/../" */
1818 g_string_erase(str, pos, strlen(needle));
1819 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1821 /* search for last "/" before found "/../" */
1822 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1823 sub_len = pos - (c - str->str);
1824 if (! c)
1825 break; /* bad path */
1827 pos = c - str->str; /* position of previous "/" */
1828 g_string_erase(str, pos, sub_len);
1831 if (str->len <= strlen(filename))
1832 memcpy(filename, str->str, str->len + 1);
1833 else
1834 g_warn_if_reached();
1835 g_string_free(str, TRUE);
1840 * Removes characters from a string, in place.
1842 * @param string String to search.
1843 * @param chars Characters to remove.
1845 * @return @a string - return value is only useful when nesting function calls, e.g.:
1846 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1848 * @see @c g_strdelimit.
1850 gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1852 const gchar *r;
1853 gchar *w = string;
1855 g_return_val_if_fail(string, NULL);
1856 if (G_UNLIKELY(EMPTY(chars)))
1857 return string;
1859 foreach_str(r, string)
1861 if (!strchr(chars, *r))
1862 *w++ = *r;
1864 *w = 0x0;
1865 return string;
1869 /* Gets list of sorted filenames with no path and no duplicates from user and system config */
1870 GSList *utils_get_config_files(const gchar *subdir)
1872 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1873 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1874 GSList *syslist, *node;
1876 if (!list)
1878 utils_mkdir(path, FALSE);
1880 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1881 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1882 /* merge lists */
1883 list = g_slist_concat(list, syslist);
1885 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1886 /* remove duplicates (next to each other after sorting) */
1887 foreach_slist(node, list)
1889 if (node->next && utils_str_equal(node->next->data, node->data))
1891 GSList *old = node->next;
1893 g_free(old->data);
1894 node->next = old->next;
1895 g_slist_free1(old);
1898 g_free(path);
1899 return list;
1903 /* Suffix can be NULL or a string which should be appended to the Help URL like
1904 * an anchor link, e.g. "#some_anchor". */
1905 gchar *utils_get_help_url(const gchar *suffix)
1907 gint skip;
1908 gchar *uri;
1910 #ifdef G_OS_WIN32
1911 skip = 8;
1912 uri = g_strconcat("file:///", app->docdir, "/Manual.html", NULL);
1913 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1914 #else
1915 skip = 7;
1916 uri = g_strconcat("file://", app->docdir, "/index.html", NULL);
1917 #endif
1919 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1920 { /* fall back to online documentation if it is not found on the hard disk */
1921 g_free(uri);
1922 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1925 if (suffix != NULL)
1927 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1930 return uri;
1934 static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1936 const gchar **p;
1938 for (p = haystack; *p != NULL; ++p)
1940 if (utils_str_equal(*p, needle))
1941 return TRUE;
1943 return FALSE;
1948 * Copies the current environment into a new array.
1949 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1950 * All further arguments are key, value pairs of variables which should be added to
1951 * the environment.
1953 * The argument list must be @c NULL-terminated.
1955 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1956 * @param first_varname Name of the first variable to copy into the new array.
1957 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1959 * @return The new environment array.
1961 gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1963 gchar **result;
1964 gchar **p;
1965 gchar **env;
1966 va_list args;
1967 const gchar *key, *value;
1968 guint n, o;
1970 /* count the additional variables */
1971 va_start(args, first_varname);
1972 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1973 va_end(args);
1974 /* the passed arguments should be even (key, value pairs) */
1975 g_return_val_if_fail(o % 2 == 0, NULL);
1977 o /= 2;
1979 /* get all the environ variables */
1980 env = g_listenv();
1982 /* create an array large enough to hold the new environment */
1983 n = g_strv_length(env);
1984 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1985 result = g_new(gchar *, n + o + 1);
1987 /* copy the environment */
1988 for (n = 0, p = env; *p != NULL; ++p)
1990 /* copy the variable */
1991 value = g_getenv(*p);
1992 if (G_LIKELY(value != NULL))
1994 /* skip excluded variables */
1995 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1996 continue;
1998 result[n++] = g_strconcat(*p, "=", value, NULL);
2001 g_strfreev(env);
2003 /* now add additional variables */
2004 va_start(args, first_varname);
2005 key = first_varname;
2006 value = va_arg(args, gchar*);
2007 while (key != NULL)
2009 result[n++] = g_strconcat(key, "=", value, NULL);
2011 key = va_arg(args, gchar*);
2012 if (key == NULL)
2013 break;
2014 value = va_arg(args, gchar*);
2016 va_end(args);
2018 result[n] = NULL;
2020 return result;
2024 /* Joins @a first and @a second into a new string vector, freeing the originals.
2025 * The original contents are reused. */
2026 gchar **utils_strv_join(gchar **first, gchar **second)
2028 gchar **strv;
2029 gchar **rptr, **wptr;
2031 if (!first)
2032 return second;
2033 if (!second)
2034 return first;
2036 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2037 wptr = strv;
2039 foreach_strv(rptr, first)
2040 *wptr++ = *rptr;
2041 foreach_strv(rptr, second)
2042 *wptr++ = *rptr;
2044 g_free(first);
2045 g_free(second);
2046 return strv;
2050 /* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2051 * obviously g_date_set_parse() uses some magic.
2052 * The returned GDate object must be freed. */
2053 GDate *utils_parse_date(const gchar *input)
2055 GDate *date = g_date_new();
2057 g_date_set_parse(date, input);
2059 if (g_date_valid(date))
2060 return date;
2062 g_date_free(date);
2063 return NULL;
2067 gchar *utils_parse_and_format_build_date(const gchar *input)
2069 gchar date_buf[255];
2070 GDate *date = utils_parse_date(input);
2072 if (date != NULL)
2074 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2075 g_date_free(date);
2076 return g_strdup(date_buf);
2079 return g_strdup(input);