geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

utils.c
Go to the documentation of this file.
1/*
2 * utils.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*
22 * General utility functions, non-GTK related.
23 */
24
25#ifdef HAVE_CONFIG_H
26# include "config.h"
27#endif
28
29#include "utils.h"
30
31#include "app.h"
32#include "dialogs.h"
33#include "document.h"
34#include "prefs.h"
35#include "prefix.h"
36#include "sciwrappers.h"
37#include "spawn.h"
38#include "support.h"
39#include "tm_source_file.h" // for tm_get_real_path()
40#include "templates.h"
41#include "ui_utils.h"
42#include "win32.h"
43#include "osx.h"
44
45#include <stdlib.h>
46#include <ctype.h>
47#include <math.h>
48#include <unistd.h>
49#include <string.h>
50#include <errno.h>
51#include <stdarg.h>
52
53#ifdef HAVE_SYS_STAT_H
54# include <sys/stat.h>
55#endif
56#ifdef HAVE_SYS_TYPES_H
57# include <sys/types.h>
58#endif
59
60#include <glib/gstdio.h>
61#include <gio/gio.h>
62
63
64/**
65 * Tries to open the given URI in a browser.
66 * On Windows, the system's default browser is opened.
67 * On non-Windows systems, the browser command set in the preferences dialog is used. In case
68 * that fails or it is unset, the user is asked to correct or fill it.
69 *
70 * @param uri The URI to open in the web browser.
71 *
72 * @since 0.16
73 **/
74GEANY_API_SYMBOL
75void utils_open_browser(const gchar *uri)
76{
77#ifdef G_OS_WIN32
78 g_return_if_fail(uri != NULL);
79 win32_open_browser(uri);
80#else
81 gchar *argv[2] = { (gchar *) uri, NULL };
82
83 g_return_if_fail(uri != NULL);
84
86 {
87 gchar *new_cmd = dialogs_show_input(_("Select Browser"), GTK_WINDOW(main_widgets.window),
88 _("Failed to spawn the configured browser command. "
89 "Please correct it or enter another one."),
91
92 if (new_cmd == NULL) /* user canceled */
93 break;
94
96 }
97#endif
98}
99
100
101/* taken from anjuta, to determine the EOL mode of the file */
102gint utils_get_line_endings(const gchar* buffer, gsize size)
103{
104 gsize i;
105 guint cr, lf, crlf, max_mode;
106 gint mode;
107
108 cr = lf = crlf = 0;
109
110 for (i = 0; i < size ; i++)
111 {
112 if (buffer[i] == 0x0a)
113 {
114 /* LF */
115 lf++;
116 }
117 else if (buffer[i] == 0x0d)
118 {
119 if (i >= (size - 1))
120 {
121 /* Last char, CR */
122 cr++;
123 }
124 else
125 {
126 if (buffer[i + 1] != 0x0a)
127 {
128 /* CR */
129 cr++;
130 }
131 else
132 {
133 /* CRLF */
134 crlf++;
135 }
136 i++;
137 }
138 }
139 }
140
141 /* Vote for the maximum */
142 mode = SC_EOL_LF;
143 max_mode = lf;
144 if (crlf > max_mode)
145 {
146 mode = SC_EOL_CRLF;
147 max_mode = crlf;
148 }
149 if (cr > max_mode)
150 {
151 mode = SC_EOL_CR;
152 max_mode = cr;
153 }
154
155 return mode;
156}
157
158
159gboolean utils_isbrace(gchar c, gboolean include_angles)
160{
161 switch (c)
162 {
163 case '<':
164 case '>':
165 return include_angles;
166
167 case '(':
168 case ')':
169 case '{':
170 case '}':
171 case '[':
172 case ']': return TRUE;
173 default: return FALSE;
174 }
175}
176
177
178gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
179{
180 switch (c)
181 {
182 case '<':
183 return include_angles;
184
185 case '(':
186 case '{':
187 case '[': return TRUE;
188 default: return FALSE;
189 }
190}
191
192
193/**
194 * Writes @a text into a file named @a filename.
195 * If the file doesn't exist, it will be created.
196 * If it already exists, it will be overwritten.
197 *
198 * @warning You should use @c g_file_set_contents() instead if you don't need
199 * file permissions and other metadata to be preserved, as that always handles
200 * disk exhaustion safely.
201 *
202 * @param filename The filename of the file to write, in locale encoding.
203 * @param text The text to write into the file.
204 *
205 * @return 0 if the file was successfully written, otherwise the @c errno of the
206 * failed operation is returned.
207 **/
208GEANY_API_SYMBOL
209gint utils_write_file(const gchar *filename, const gchar *text)
210{
211 g_return_val_if_fail(filename != NULL, ENOENT);
212 g_return_val_if_fail(text != NULL, EINVAL);
213
215 {
216 GError *error = NULL;
217 if (! g_file_set_contents(filename, text, -1, &error))
218 {
219 geany_debug("%s: could not write to file %s (%s)", G_STRFUNC, filename, error->message);
220 g_error_free(error);
221 return EIO;
222 }
223 }
224 else
225 {
226 FILE *fp;
227 gsize bytes_written, len;
228 gboolean fail = FALSE;
229
230 if (filename == NULL)
231 return ENOENT;
232
233 len = strlen(text);
234 errno = 0;
235 fp = g_fopen(filename, "w");
236 if (fp == NULL)
237 fail = TRUE;
238 else
239 {
240 bytes_written = fwrite(text, sizeof(gchar), len, fp);
241
242 if (len != bytes_written)
243 {
244 fail = TRUE;
246 "utils_write_file(): written only %"G_GSIZE_FORMAT" bytes, had to write %"G_GSIZE_FORMAT" bytes to %s",
247 bytes_written, len, filename);
248 }
249 if (fclose(fp) != 0)
250 fail = TRUE;
251 }
252 if (fail)
253 {
254 geany_debug("utils_write_file(): could not write to file %s (%s)",
255 filename, g_strerror(errno));
256 return FALLBACK(errno, EIO);
257 }
258 }
259 return 0;
260}
261
262
263/** Searches backward through @a size bytes looking for a '<'.
264 * @param sel .
265 * @param size .
266 * @return @nullable The tag name (newly allocated) or @c NULL if no opening tag was found.
267 */
268GEANY_API_SYMBOL
269gchar *utils_find_open_xml_tag(const gchar sel[], gint size)
270{
271 const gchar *cur, *begin;
272 gsize len;
273
274 cur = utils_find_open_xml_tag_pos(sel, size);
275 if (cur == NULL)
276 return NULL;
277
278 cur++; /* skip the bracket */
279 begin = cur;
280 while (strchr(":_-.", *cur) || isalnum(*cur))
281 cur++;
282
283 len = (gsize)(cur - begin);
284 return len ? g_strndup(begin, len) : NULL;
285}
286
287
288/** Searches backward through @a size bytes looking for a '<'.
289 * @param sel .
290 * @param size .
291 * @return @nullable pointer to '<' of the found opening tag within @a sel, or @c NULL if no opening tag was found.
292 */
293GEANY_API_SYMBOL
294const gchar *utils_find_open_xml_tag_pos(const gchar sel[], gint size)
295{
296 /* stolen from anjuta and modified */
297 const gchar *begin, *cur;
298
299 if (G_UNLIKELY(size < 3))
300 { /* Smallest tag is "<p>" which is 3 characters */
301 return NULL;
302 }
303 begin = &sel[0];
304 cur = &sel[size - 1];
305
306 /* Skip to the character before the closing brace */
307 while (cur > begin)
308 {
309 if (*cur == '>')
310 break;
311 --cur;
312 }
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)
320 {
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;
327 }
328
329 /* if the found tag is an opening, not a closing tag or empty <> */
330 if (*cur == '<' && *(cur + 1) != '/' && *(cur + 1) != '>')
331 return cur;
332
333 return NULL;
334}
335
336
337/* Returns true if tag_name is a self-closing tag */
338gboolean utils_is_short_html_tag(const gchar *tag_name)
339{
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"
359 };
360
361 if (tag_name)
362 {
363 if (bsearch(tag_name, names, G_N_ELEMENTS(names), 20,
364 (GCompareFunc)g_ascii_strcasecmp))
365 return TRUE;
366 }
367 return FALSE;
368}
369
370
371const gchar *utils_get_eol_name(gint eol_mode)
372{
373 switch (eol_mode)
374 {
375 case SC_EOL_CRLF: return _("Windows (CRLF)"); break;
376 case SC_EOL_CR: return _("Classic Mac (CR)"); break;
377 default: return _("Unix (LF)"); break;
378 }
379}
380
381
382const gchar *utils_get_eol_short_name(gint eol_mode)
383{
384 switch (eol_mode)
385 {
386 case SC_EOL_CRLF: return _("CRLF"); break;
387 case SC_EOL_CR: return _("CR"); break;
388 default: return _("LF"); break;
389 }
390}
391
392
393const gchar *utils_get_eol_char(gint eol_mode)
394{
395 switch (eol_mode)
396 {
397 case SC_EOL_CRLF: return "\r\n"; break;
398 case SC_EOL_CR: return "\r"; break;
399 default: return "\n"; break;
400 }
401}
402
403
404/* Converts line endings to @a target_eol_mode. */
405void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
406{
407 const gchar *eol_str = utils_get_eol_char(target_eol_mode);
408
409 /* first convert data to LF only */
410 utils_string_replace_all(string, "\r\n", "\n");
411 utils_string_replace_all(string, "\r", "\n");
412
413 if (target_eol_mode == SC_EOL_LF)
414 return;
415
416 /* now convert to desired line endings */
417 utils_string_replace_all(string, "\n", eol_str);
418}
419
420
421gboolean utils_atob(const gchar *str)
422{
423 if (G_UNLIKELY(str == NULL))
424 return FALSE;
425 else if (strcmp(str, "TRUE") == 0 || strcmp(str, "true") == 0)
426 return TRUE;
427 return FALSE;
428}
429
430
431/* NULL-safe version of g_path_is_absolute(). */
432gboolean utils_is_absolute_path(const gchar *path)
433{
434 if (G_UNLIKELY(EMPTY(path)))
435 return FALSE;
436
437 return g_path_is_absolute(path);
438}
439
440
441/* Skips root if path is absolute, do nothing otherwise.
442 * This is a relative-safe version of g_path_skip_root().
443 */
444const gchar *utils_path_skip_root(const gchar *path)
445{
446 const gchar *path_relative;
447
448 path_relative = g_path_skip_root(path);
449
450 return (path_relative != NULL) ? path_relative : path;
451}
452
453
454/* Convert a fractional @a val in the range [0, 1] to a whole value in the range [0, @a factor].
455 * In particular, this is used for converting a @c GdkColor to the "#RRGGBB" format in a way that
456 * agrees with GTK+, so the "#RRGGBB" in the color picker is the same "#RRGGBB" that is inserted
457 * into the document. See https://github.com/geany/geany/issues/1527
458 */
459gdouble utils_scale_round(gdouble val, gdouble factor)
460{
461 val = floor(val * factor + 0.5);
462 val = MAX(val, 0);
463 val = MIN(val, factor);
464
465 return val;
466}
467
468
469/* like g_utf8_strdown() but if @str is not valid UTF8, convert it from locale first.
470 * returns NULL on charset conversion failure */
471static gchar *utf8_strdown(const gchar *str)
472{
473 gchar *down;
474
475 if (g_utf8_validate(str, -1, NULL))
476 down = g_utf8_strdown(str, -1);
477 else
478 {
479 down = g_locale_to_utf8(str, -1, NULL, NULL, NULL);
480 if (down)
481 SETPTR(down, g_utf8_strdown(down, -1));
482 }
483
484 return down;
485}
486
487
488/**
489 * A replacement function for g_strncasecmp() to compare strings case-insensitive.
490 * It converts both strings into lowercase using g_utf8_strdown() and then compare
491 * both strings using strcmp().
492 * This is not completely accurate regarding locale-specific case sorting rules
493 * but seems to be a good compromise between correctness and performance.
494 *
495 * The input strings should be in UTF-8 or locale encoding.
496 *
497 * @param s1 @nullable Pointer to first string or @c NULL.
498 * @param s2 @nullable Pointer to second string or @c NULL.
499 *
500 * @return an integer less than, equal to, or greater than zero if @a s1 is found, respectively,
501 * to be less than, to match, or to be greater than @a s2.
502 *
503 * @since 0.16
504 */
505GEANY_API_SYMBOL
506gint utils_str_casecmp(const gchar *s1, const gchar *s2)
507{
508 gchar *tmp1, *tmp2;
509 gint result;
510
511 g_return_val_if_fail(s1 != NULL, 1);
512 g_return_val_if_fail(s2 != NULL, -1);
513
514 /* ensure strings are UTF-8 and lowercase */
515 tmp1 = utf8_strdown(s1);
516 if (! tmp1)
517 return 1;
518 tmp2 = utf8_strdown(s2);
519 if (! tmp2)
520 {
521 g_free(tmp1);
522 return -1;
523 }
524
525 /* compare */
526 result = strcmp(tmp1, tmp2);
527
528 g_free(tmp1);
529 g_free(tmp2);
530 return result;
531}
532
533
534/**
535 * Truncates the input string to a given length.
536 * Characters are removed from the middle of the string, so the start and the end of string
537 * won't change.
538 *
539 * @param string Input string.
540 * @param truncate_length The length in characters of the resulting string.
541 *
542 * @return A copy of @a string which is truncated to @a truncate_length characters,
543 * should be freed when no longer needed.
544 *
545 * @since 0.17
546 */
547/* This following function is taken from Gedit. */
548GEANY_API_SYMBOL
549gchar *utils_str_middle_truncate(const gchar *string, guint truncate_length)
550{
551 GString *truncated;
552 guint length;
553 guint n_chars;
554 guint num_left_chars;
555 guint right_offset;
556 guint delimiter_length;
557 const gchar *delimiter = "\342\200\246";
558
559 g_return_val_if_fail(string != NULL, NULL);
560
561 length = strlen(string);
562
563 g_return_val_if_fail(g_utf8_validate(string, length, NULL), NULL);
564
565 /* It doesn't make sense to truncate strings to less than the size of the delimiter plus 2
566 * characters (one on each side) */
567 delimiter_length = g_utf8_strlen(delimiter, -1);
568 if (truncate_length < (delimiter_length + 2))
569 return g_strdup(string);
570
571 n_chars = g_utf8_strlen(string, length);
572
573 /* Make sure the string is not already small enough. */
574 if (n_chars <= truncate_length)
575 return g_strdup (string);
576
577 /* Find the 'middle' where the truncation will occur. */
578 num_left_chars = (truncate_length - delimiter_length) / 2;
579 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
580
581 truncated = g_string_new_len(string, g_utf8_offset_to_pointer(string, num_left_chars) - string);
582 g_string_append(truncated, delimiter);
583 g_string_append(truncated, g_utf8_offset_to_pointer(string, right_offset));
584
585 return g_string_free(truncated, FALSE);
586}
587
588
589/**
590 * @c NULL-safe string comparison. Returns @c TRUE if both @a a and @a b are @c NULL
591 * or if @a a and @a b refer to valid strings which are equal.
592 *
593 * @param a @nullable Pointer to first string or @c NULL.
594 * @param b @nullable Pointer to second string or @c NULL.
595 *
596 * @return @c TRUE if @a a equals @a b, else @c FALSE.
597 **/
598GEANY_API_SYMBOL
599gboolean utils_str_equal(const gchar *a, const gchar *b)
600{
601 /* (taken from libexo from os-cillation) */
602 if (a == NULL && b == NULL) return TRUE;
603 else if (a == NULL || b == NULL) return FALSE;
604
605 return strcmp(a, b) == 0;
606}
607
608
609/**
610 * Removes the extension from @a filename and return the result in a newly allocated string.
611 *
612 * @param filename The filename to operate on.
613 *
614 * @return A newly-allocated string, should be freed when no longer needed.
615 **/
616GEANY_API_SYMBOL
618{
619 gchar *last_dot;
620 gchar *result;
621 gsize len;
622
623 g_return_val_if_fail(filename != NULL, NULL);
624
625 last_dot = strrchr(filename, '.');
626 if (! last_dot)
627 return g_strdup(filename);
628
629 len = (gsize) (last_dot - filename);
630 result = g_malloc(len + 1);
631 memcpy(result, filename, len);
632 result[len] = 0;
633
634 return result;
635}
636
637
638gchar utils_brace_opposite(gchar ch)
639{
640 switch (ch)
641 {
642 case '(': return ')';
643 case ')': return '(';
644 case '[': return ']';
645 case ']': return '[';
646 case '{': return '}';
647 case '}': return '{';
648 case '<': return '>';
649 case '>': return '<';
650 default: return '\0';
651 }
652}
653
654
655/* Checks whether the given file can be written. locale_filename is expected in locale encoding.
656 * Returns 0 if it can be written, otherwise it returns errno */
657gint utils_is_file_writable(const gchar *locale_filename)
658{
659 gchar *file;
660 gint ret;
661
662 if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
663 ! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
664 /* get the file's directory to check for write permission if it doesn't yet exist */
665 file = g_path_get_dirname(locale_filename);
666 else
667 file = g_strdup(locale_filename);
668
669#ifdef G_OS_WIN32
670 /* use _waccess on Windows, access() doesn't accept special characters */
671 ret = win32_check_write_permission(file);
672#else
673
674 /* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
675 * errno only when access() explicitly returns an error */
676 if (access(file, R_OK | W_OK) != 0)
677 ret = errno;
678 else
679 ret = 0;
680#endif
681 g_free(file);
682 return ret;
683}
684
685
686/* Replaces all occurrences of needle in haystack with replacement.
687 * Warning: *haystack must be a heap address; it may be freed and reassigned.
688 * Note: utils_string_replace_all() will always be faster when @a replacement is longer
689 * than @a needle.
690 * All strings have to be NULL-terminated.
691 * See utils_string_replace_all() for details. */
692void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
693{
694 GString *str;
695
696 g_return_if_fail(*haystack != NULL);
697
698 str = g_string_new(*haystack);
699
700 g_free(*haystack);
701 utils_string_replace_all(str, needle, replacement);
702
703 *haystack = g_string_free(str, FALSE);
704}
705
706
707gint utils_strpos(const gchar *haystack, const gchar *needle)
708{
709 const gchar *sub;
710
711 if (! *needle)
712 return -1;
713
714 sub = strstr(haystack, needle);
715 if (! sub)
716 return -1;
717
718 return sub - haystack;
719}
720
721
722/**
723 * Retrieves a formatted date/time string from strftime().
724 * This function should be preferred to directly calling strftime() since this function
725 * works on UTF-8 encoded strings.
726 *
727 * @param format The format string to pass to strftime(3). See the strftime(3)
728 * documentation for details, in UTF-8 encoding.
729 * @param time_to_use @nullable The date/time to use, in time_t format or @c NULL to use the current time.
730 *
731 * @return A newly-allocated string, should be freed when no longer needed.
732 *
733 * @since 0.16
734 */
735GEANY_API_SYMBOL
736gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
737{
738 const struct tm *tm;
739 static gchar date[1024];
740 gchar *locale_format;
741 gsize len;
742
743 g_return_val_if_fail(format != NULL, NULL);
744
745 if (! g_utf8_validate(format, -1, NULL))
746 {
747 locale_format = g_locale_from_utf8(format, -1, NULL, NULL, NULL);
748 if (locale_format == NULL)
749 return NULL;
750 }
751 else
752 locale_format = g_strdup(format);
753
754 if (time_to_use != NULL)
755 tm = localtime(time_to_use);
756 else
757 {
758 time_t tp = time(NULL);
759 tm = localtime(&tp);
760 }
761
762 len = strftime(date, 1024, locale_format, tm);
763 g_free(locale_format);
764 if (len == 0)
765 return NULL;
766
767 if (! g_utf8_validate(date, len, NULL))
768 return g_locale_to_utf8(date, len, NULL, NULL, NULL);
769 else
770 return g_strdup(date);
771}
772
773
774gchar *utils_get_initials(const gchar *name)
775{
776 gint i = 1, j = 1;
777 gchar *initials = g_malloc0(5);
778
779 initials[0] = name[0];
780 while (name[i] != '\0' && j < 4)
781 {
782 if (name[i] == ' ' && name[i + 1] != ' ')
783 {
784 initials[j++] = name[i + 1];
785 }
786 i++;
787 }
788 return initials;
789}
790
791
792/**
793 * Wraps g_key_file_get_integer() to add a default value argument.
794 *
795 * @param config A GKeyFile object.
796 * @param section The group name to look in for the key.
797 * @param key The key to find.
798 * @param default_value The default value which will be returned when @a section or @a key
799 * don't exist.
800 *
801 * @return The value associated with @a key as an integer, or the given default value if the value
802 * could not be retrieved.
803 **/
804GEANY_API_SYMBOL
805gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
806 const gint default_value)
807{
808 gint tmp;
809 GError *error = NULL;
810
811 g_return_val_if_fail(config, default_value);
812
813 tmp = g_key_file_get_integer(config, section, key, &error);
814 if (error)
815 {
816 g_error_free(error);
817 return default_value;
818 }
819 return tmp;
820}
821
822
823/**
824 * Wraps g_key_file_get_boolean() to add a default value argument.
825 *
826 * @param config A GKeyFile object.
827 * @param section The group name to look in for the key.
828 * @param key The key to find.
829 * @param default_value The default value which will be returned when @c section or @c key
830 * don't exist.
831 *
832 * @return The value associated with @a key as a boolean, or the given default value if the value
833 * could not be retrieved.
834 **/
835GEANY_API_SYMBOL
836gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
837 const gboolean default_value)
838{
839 gboolean tmp;
840 GError *error = NULL;
841
842 g_return_val_if_fail(config, default_value);
843
844 tmp = g_key_file_get_boolean(config, section, key, &error);
845 if (error)
846 {
847 g_error_free(error);
848 return default_value;
849 }
850 return tmp;
851}
852
853
854/**
855 * Wraps g_key_file_get_string() to add a default value argument.
856 *
857 * @param config A GKeyFile object.
858 * @param section The group name to look in for the key.
859 * @param key The key to find.
860 * @param default_value The default value which will be returned when @a section or @a key
861 * don't exist.
862 *
863 * @return A newly allocated string, either the value for @a key or a copy of the given
864 * default value if it could not be retrieved.
865 **/
866GEANY_API_SYMBOL
867gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
868 const gchar *default_value)
869{
870 gchar *tmp;
871
872 g_return_val_if_fail(config, g_strdup(default_value));
873
874 tmp = g_key_file_get_string(config, section, key, NULL);
875 if (!tmp)
876 {
877 return g_strdup(default_value);
878 }
879 return tmp;
880}
881
882
884{
885 g_return_val_if_fail(color != NULL, NULL);
886
887 return g_strdup_printf("#%02X%02X%02X",
888 (guint) (utils_scale_round(color->red / 65535.0, 255)),
889 (guint) (utils_scale_round(color->green / 65535.0, 255)),
890 (guint) (utils_scale_round(color->blue / 65535.0, 255)));
891}
892
893
894/* Get directory from current file in the notebook.
895 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
896 * Returned string is in UTF-8 encoding */
898{
900
901 if (doc != NULL)
902 {
903 /* get current filename */
904 const gchar *cur_fname = doc->file_name;
905
906 if (cur_fname != NULL)
907 {
908 /* get folder part from current filename */
909 return g_path_get_dirname(cur_fname); /* returns "." if no path */
910 }
911 }
912
913 return NULL; /* no file open */
914}
915
916
917/* very simple convenience function */
918void utils_beep(void)
919{
921 gdk_beep();
922}
923
924
925/* taken from busybox, thanks */
926gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
927 gulong display_unit)
928{
929 /* The code will adjust for additional (appended) units. */
930 static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
931 static const gchar fmt[] = "%Lu %c%c";
932 static const gchar fmt_tenths[] = "%Lu.%d %c%c";
933
934 guint64 val;
935 gint frac;
936 const gchar *u;
937 const gchar *f;
938
939 u = zero_and_units;
940 f = fmt;
941 frac = 0;
942
943 val = size * block_size;
944 if (val == 0)
945 return g_strdup(u);
946
947 if (display_unit)
948 {
949 val += display_unit/2; /* Deal with rounding. */
950 val /= display_unit; /* Don't combine with the line above!!! */
951 }
952 else
953 {
954 ++u;
955 while ((val >= 1024) && (u < zero_and_units + sizeof(zero_and_units) - 1))
956 {
957 f = fmt_tenths;
958 ++u;
959 frac = ((((gint)(val % 1024)) * 10) + (1024 / 2)) / 1024;
960 val /= 1024;
961 }
962 if (frac >= 10)
963 { /* We need to round up here. */
964 ++val;
965 frac = 0;
966 }
967 }
968
969 /* If f==fmt then 'frac' and 'u' are ignored. */
970 return g_strdup_printf(f, val, frac, *u, 'b');
971}
972
973
974/* converts a color representation using gdk_color_parse(), with additional
975 * support of the "0x" prefix as a synonym for "#" */
976gboolean utils_parse_color(const gchar *spec, GdkColor *color)
977{
978 gchar buf[64] = {0};
979
980 g_return_val_if_fail(spec != NULL, -1);
981
982 if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X'))
983 {
984 /* convert to # format for GDK to understand it */
985 buf[0] = '#';
986 strncpy(buf + 1, spec + 2, sizeof(buf) - 2);
987 spec = buf;
988 }
989
990 return gdk_color_parse(spec, color);
991}
992
993
994/* converts a GdkColor to the packed 24 bits BGR format, as understood by Scintilla
995 * returns a 24 bits BGR color, or -1 on failure */
996gint utils_color_to_bgr(const GdkColor *c)
997{
998 g_return_val_if_fail(c != NULL, -1);
999 return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16);
1000}
1001
1002
1003/* parses @p spec using utils_parse_color() and convert it to 24 bits BGR using
1004 * utils_color_to_bgr() */
1005gint utils_parse_color_to_bgr(const gchar *spec)
1006{
1007 GdkColor color;
1008 if (utils_parse_color(spec, &color))
1009 return utils_color_to_bgr(&color);
1010 else
1011 return -1;
1012}
1013
1014
1015/* Returns: newly allocated string with the current time formatted HH:MM:SS.
1016 * If "include_microseconds" is TRUE, microseconds are appended.
1017 *
1018 * The returned string should be freed with g_free(). */
1019gchar *utils_get_current_time_string(gboolean include_microseconds)
1020{
1021 GDateTime *now = g_date_time_new_now_local();
1022 const gchar *format = include_microseconds ? "%H:%M:%S.%f" : "%H:%M:%S";
1023 gchar *time_string = g_date_time_format(now, format);
1024 g_date_time_unref(now);
1025 return time_string;
1026}
1027
1028
1030 gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1031{
1032 GIOChannel *ioc;
1033 /*const gchar *encoding;*/
1034
1035 #ifdef G_OS_WIN32
1036 ioc = g_io_channel_win32_new_fd(fd);
1037 #else
1038 ioc = g_io_channel_unix_new(fd);
1039 #endif
1040
1041 if (nblock)
1042 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
1043
1044 g_io_channel_set_encoding(ioc, NULL, NULL);
1045/*
1046 if (! g_get_charset(&encoding))
1047 { // hope this works reliably
1048 GError *error = NULL;
1049 g_io_channel_set_encoding(ioc, encoding, &error);
1050 if (error)
1051 {
1052 geany_debug("%s: %s", G_STRFUNC, error->message);
1053 g_error_free(error);
1054 return ioc;
1055 }
1056 }
1057*/
1058 /* "auto-close" ;-) */
1059 g_io_channel_set_close_on_unref(ioc, TRUE);
1060
1061 g_io_add_watch(ioc, cond, func, data);
1062 g_io_channel_unref(ioc);
1063
1064 return ioc;
1065}
1066
1067
1068/* Contributed by Stefan Oltmanns, thanks.
1069 * Replaces \\, \r, \n, \t and \uXXX by their real counterparts.
1070 * keep_backslash is used for regex strings to leave '\\' and '\?' in place */
1071gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
1072{
1073 gsize i, j, len;
1074 guint unicodechar;
1075
1076 g_return_val_if_fail(string != NULL, FALSE);
1077
1078 j = 0;
1079 len = strlen(string);
1080 for (i = 0; i < len; i++)
1081 {
1082 if (string[i]=='\\')
1083 {
1084 if (i++ >= strlen(string))
1085 {
1086 return FALSE;
1087 }
1088 switch (string[i])
1089 {
1090 case '\\':
1091 if (keep_backslash)
1092 string[j++] = '\\';
1093 string[j] = '\\';
1094 break;
1095 case 'n':
1096 string[j] = '\n';
1097 break;
1098 case 'r':
1099 string[j] = '\r';
1100 break;
1101 case 't':
1102 string[j] = '\t';
1103 break;
1104#if 0
1105 case 'x': /* Warning: May produce illegal utf-8 string! */
1106 i += 2;
1107 if (i >= strlen(string))
1108 {
1109 return FALSE;
1110 }
1111 if (isdigit(string[i - 1])) string[j] = string[i - 1] - 48;
1112 else if (isxdigit(string[i - 1])) string[j] = tolower(string[i - 1])-87;
1113 else return FALSE;
1114 string[j] <<= 4;
1115 if (isdigit(string[i])) string[j] |= string[i] - 48;
1116 else if (isxdigit(string[i])) string[j] |= tolower(string[i])-87;
1117 else return FALSE;
1118 break;
1119#endif
1120 case 'u':
1121 {
1122 i += 2;
1123 if (i >= strlen(string))
1124 {
1125 return FALSE;
1126 }
1127 if (isdigit(string[i - 1])) unicodechar = string[i - 1] - 48;
1128 else if (isxdigit(string[i - 1])) unicodechar = tolower(string[i - 1])-87;
1129 else return FALSE;
1130 unicodechar <<= 4;
1131 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1132 else if (isxdigit(string[i])) unicodechar |= tolower(string[i])-87;
1133 else return FALSE;
1134 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1135 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1136 {
1137 i += 2;
1138 unicodechar <<= 8;
1139 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1140 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1141 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1142 else unicodechar |= tolower(string[i])-87;
1143 }
1144 if (((i + 2) < strlen(string)) && (isdigit(string[i + 1]) || isxdigit(string[i + 1]))
1145 && (isdigit(string[i + 2]) || isxdigit(string[i + 2])))
1146 {
1147 i += 2;
1148 unicodechar <<= 8;
1149 if (isdigit(string[i - 1])) unicodechar |= ((string[i - 1] - 48) << 4);
1150 else unicodechar |= ((tolower(string[i - 1])-87) << 4);
1151 if (isdigit(string[i])) unicodechar |= string[i] - 48;
1152 else unicodechar |= tolower(string[i])-87;
1153 }
1154 if (unicodechar < 0x80)
1155 {
1156 string[j] = unicodechar;
1157 }
1158 else if (unicodechar < 0x800)
1159 {
1160 string[j] = (unsigned char) ((unicodechar >> 6) | 0xC0);
1161 j++;
1162 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1163 }
1164 else if (unicodechar < 0x10000)
1165 {
1166 string[j] = (unsigned char) ((unicodechar >> 12) | 0xE0);
1167 j++;
1168 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1169 j++;
1170 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1171 }
1172 else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1173 {
1174 string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1175 j++;
1176 string[j] = (unsigned char) (((unicodechar >> 12) & 0x3F) | 0x80);
1177 j++;
1178 string[j] = (unsigned char) (((unicodechar >> 6) & 0x3F) | 0x80);
1179 j++;
1180 string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1181 }
1182 else
1183 {
1184 return FALSE;
1185 }
1186 break;
1187 }
1188 default:
1189 /* unnecessary escapes are allowed */
1190 if (keep_backslash)
1191 string[j++] = '\\';
1192 string[j] = string[i];
1193 }
1194 }
1195 else
1196 {
1197 string[j] = string[i];
1198 }
1199 j++;
1200 }
1201 while (j < i)
1202 {
1203 string[j] = 0;
1204 j++;
1205 }
1206 return TRUE;
1207}
1208
1209
1210/* Wraps a string in place, replacing a space with a newline character.
1211 * wrapstart is the minimum position to start wrapping or -1 for default */
1212gboolean utils_wrap_string(gchar *string, gint wrapstart)
1213{
1214 gchar *pos, *linestart;
1215 gboolean ret = FALSE;
1216
1217 if (wrapstart < 0)
1218 wrapstart = 80;
1219
1220 for (pos = linestart = string; *pos != '\0'; pos++)
1221 {
1222 if (pos - linestart >= wrapstart && *pos == ' ')
1223 {
1224 *pos = '\n';
1225 linestart = pos;
1226 ret = TRUE;
1227 }
1228 }
1229 return ret;
1230}
1231
1232
1233/**
1234 * Converts the given UTF-8 encoded string into locale encoding.
1235 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1236 *
1237 * @param utf8_text UTF-8 encoded text.
1238 *
1239 * @return The converted string in locale encoding, or a copy of the input string if conversion
1240 * failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1241 **/
1242GEANY_API_SYMBOL
1243gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1244{
1245#ifdef G_OS_WIN32
1246 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1247 * which would result in wrongly converted strings */
1248 return g_strdup(utf8_text);
1249#else
1250 gchar *locale_text;
1251
1252 if (! utf8_text)
1253 return NULL;
1254 locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1255 if (locale_text == NULL)
1256 locale_text = g_strdup(utf8_text);
1257 return locale_text;
1258#endif
1259}
1260
1261
1262/**
1263 * Converts the given string (in locale encoding) into UTF-8 encoding.
1264 * On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1265 *
1266 * @param locale_text Text in locale encoding.
1267 *
1268 * @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1269 * failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1270 **/
1271GEANY_API_SYMBOL
1272gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1273{
1274#ifdef G_OS_WIN32
1275 /* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1276 * which would result in wrongly converted strings */
1277 return g_strdup(locale_text);
1278#else
1279 gchar *utf8_text;
1280
1281 if (! locale_text)
1282 return NULL;
1283 utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1284 if (utf8_text == NULL)
1285 utf8_text = g_strdup(locale_text);
1286 return utf8_text;
1287#endif
1288}
1289
1290
1291/* Pass pointers to free after arg_count.
1292 * The last argument must be NULL as an extra check that arg_count is correct. */
1293void utils_free_pointers(gsize arg_count, ...)
1294{
1295 va_list a;
1296 gsize i;
1297 gpointer ptr;
1298
1299 va_start(a, arg_count);
1300 for (i = 0; i < arg_count; i++)
1301 {
1302 ptr = va_arg(a, gpointer);
1303 g_free(ptr);
1304 }
1305 ptr = va_arg(a, gpointer);
1306 if (ptr)
1307 g_warning("Wrong arg_count!");
1308 va_end(a);
1309}
1310
1311
1312/* Creates a string array deep copy of a series of non-NULL strings.
1313 * The first argument is nothing special and must not be NULL.
1314 * The list must be terminated with NULL. */
1315GEANY_EXPORT_SYMBOL
1316gchar **utils_strv_new(const gchar *first, ...)
1317{
1318 gsize strvlen, i;
1319 va_list args;
1320 gchar *str;
1321 gchar **strv;
1322
1323 g_return_val_if_fail(first != NULL, NULL);
1324
1325 strvlen = 1; /* for first argument */
1326
1327 /* count other arguments */
1328 va_start(args, first);
1329 for (; va_arg(args, gchar*) != NULL; strvlen++);
1330 va_end(args);
1331
1332 strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1333 strv[0] = g_strdup(first);
1334
1335 va_start(args, first);
1336 for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1337 {
1338 strv[i] = g_strdup(str);
1339 }
1340 va_end(args);
1341
1342 strv[i] = NULL;
1343 return strv;
1344}
1345
1346
1347/**
1348 * Creates a directory if it doesn't already exist.
1349 * Creates intermediate parent directories as needed, too.
1350 * The permissions of the created directory are set 0700.
1351 *
1352 * @param path The path of the directory to create, in locale encoding.
1353 * @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1354 *
1355 * @return 0 if the directory was successfully created, otherwise the @c errno of the
1356 * failed operation is returned.
1357 **/
1358GEANY_API_SYMBOL
1359gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1360{
1361 gint mode = 0700;
1362 gint result;
1363
1364 if (path == NULL || strlen(path) == 0)
1365 return EFAULT;
1366
1367 result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1368 if (result != 0)
1369 return errno;
1370 return 0;
1371}
1372
1373
1374/**
1375 * Gets a list of files from the specified directory.
1376 * Locale encoding is expected for @a path and used for the file list. The list and the data
1377 * in the list should be freed after use, e.g.:
1378 * @code
1379 * g_slist_foreach(list, (GFunc) g_free, NULL);
1380 * g_slist_free(list); @endcode
1381 *
1382 * @note If you don't need a list you should use the foreach_dir() macro instead -
1383 * it's more efficient.
1384 *
1385 * @param path The path of the directory to scan, in locale encoding.
1386 * @param full_path Whether to include the full path for each filename in the list. Obviously this
1387 * will use more memory.
1388 * @param sort Whether to sort alphabetically (UTF-8 safe).
1389 * @param error The location for storing a possible error, or @c NULL.
1390 *
1391 * @return @elementtype{filename} @transfer{full} @nullable A newly allocated list or @c NULL if
1392 * no files were found. The list and its data should be freed when no longer needed.
1393 * @see utils_get_file_list().
1394 **/
1395GEANY_API_SYMBOL
1396GSList *utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
1397{
1398 GSList *list = NULL;
1399 GDir *dir;
1400 const gchar *filename;
1401
1402 if (error)
1403 *error = NULL;
1404 g_return_val_if_fail(path != NULL, NULL);
1405
1406 dir = g_dir_open(path, 0, error);
1407 if (dir == NULL)
1408 return NULL;
1409
1410 foreach_dir(filename, dir)
1411 {
1412 list = g_slist_prepend(list, full_path ?
1413 g_build_path(G_DIR_SEPARATOR_S, path, filename, NULL) : g_strdup(filename));
1414 }
1415 g_dir_close(dir);
1416 /* sorting last is quicker than on insertion */
1417 if (sort)
1418 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1419 return list;
1420}
1421
1422
1423/**
1424 * Gets a sorted list of files from the specified directory.
1425 * Locale encoding is expected for @a path and used for the file list. The list and the data
1426 * in the list should be freed after use, e.g.:
1427 * @code
1428 * g_slist_foreach(list, (GFunc) g_free, NULL);
1429 * g_slist_free(list); @endcode
1430 *
1431 * @param path The path of the directory to scan, in locale encoding.
1432 * @param length The location to store the number of non-@c NULL data items in the list,
1433 * unless @c NULL.
1434 * @param error The location for storing a possible error, or @c NULL.
1435 *
1436 * @return @elementtype{filename} @transfer{full} @nullable A newly allocated list or @c NULL
1437 * if no files were found. The list and its data should be freed when no longer needed.
1438 * @see utils_get_file_list_full().
1439 **/
1440GEANY_API_SYMBOL
1441GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1442{
1443 GSList *list = utils_get_file_list_full(path, FALSE, TRUE, error);
1444
1445 if (length)
1446 *length = g_slist_length(list);
1447 return list;
1448}
1449
1450
1451/* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1452gboolean utils_str_has_upper(const gchar *str)
1453{
1454 gunichar c;
1455
1456 if (EMPTY(str) || ! g_utf8_validate(str, -1, NULL))
1457 return FALSE;
1458
1459 while (*str != '\0')
1460 {
1461 c = g_utf8_get_char(str);
1462 /* check only letters and stop once the first non-capital was found */
1463 if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1464 return TRUE;
1465 /* FIXME don't write a const string */
1466 str = g_utf8_next_char(str);
1467 }
1468 return FALSE;
1469}
1470
1471
1472/* end can be -1 for haystack->len.
1473 * returns: position of found text or -1. */
1474gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
1475{
1476 gint pos;
1477
1478 g_return_val_if_fail(haystack != NULL, -1);
1479 if (haystack->len == 0)
1480 return -1;
1481
1482 g_return_val_if_fail(start >= 0, -1);
1483 if (start >= (gint)haystack->len)
1484 return -1;
1485
1486 g_return_val_if_fail(!EMPTY(needle), -1);
1487
1488 if (end < 0)
1489 end = haystack->len;
1490
1491 pos = utils_strpos(haystack->str + start, needle);
1492 if (pos == -1)
1493 return -1;
1494
1495 pos += start;
1496 if (pos >= end)
1497 return -1;
1498 return pos;
1499}
1500
1501
1502/* Replaces @len characters from offset @a pos.
1503 * len can be -1 to replace the remainder of @a str.
1504 * returns: pos + strlen(replace). */
1505gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
1506{
1507 g_string_erase(str, pos, len);
1508 if (replace)
1509 {
1510 g_string_insert(str, pos, replace);
1511 pos += strlen(replace);
1512 }
1513 return pos;
1514}
1515
1516
1517/**
1518 * Replaces all occurrences of @a needle in @a haystack with @a replace.
1519 * As of Geany 0.16, @a replace can match @a needle, so the following will work:
1520 * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
1521 *
1522 * @param haystack The input string to operate on. This string is modified in place.
1523 * @param needle The string which should be replaced.
1524 * @param replace The replacement for @a needle.
1525 *
1526 * @return Number of replacements made.
1527 **/
1528GEANY_API_SYMBOL
1529guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1530{
1531 guint count = 0;
1532 gint pos = 0;
1533 gsize needle_length = strlen(needle);
1534
1535 while (1)
1536 {
1537 pos = utils_string_find(haystack, pos, -1, needle);
1538
1539 if (pos == -1)
1540 break;
1541
1542 pos = utils_string_replace(haystack, pos, needle_length, replace);
1543 count++;
1544 }
1545 return count;
1546}
1547
1548
1549/**
1550 * Replaces only the first occurrence of @a needle in @a haystack
1551 * with @a replace.
1552 * For details, see utils_string_replace_all().
1553 *
1554 * @param haystack The input string to operate on. This string is modified in place.
1555 * @param needle The string which should be replaced.
1556 * @param replace The replacement for @a needle.
1557 *
1558 * @return Number of replacements made.
1559 *
1560 * @since 0.16
1561 */
1562GEANY_API_SYMBOL
1563guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
1564{
1565 gint pos = utils_string_find(haystack, 0, -1, needle);
1566
1567 if (pos == -1)
1568 return 0;
1569
1570 utils_string_replace(haystack, pos, strlen(needle), replace);
1571 return 1;
1572}
1573
1574
1575/* Similar to g_regex_replace but allows matching a subgroup.
1576 * match_num: which match to replace, 0 for whole match.
1577 * literal: FALSE to interpret escape sequences in @a replace.
1578 * returns: number of replacements.
1579 * bug: replaced text can affect matching of ^ or \b */
1580guint utils_string_regex_replace_all(GString *haystack, GRegex *regex,
1581 guint match_num, const gchar *replace, gboolean literal)
1582{
1583 GMatchInfo *minfo;
1584 guint ret = 0;
1585 gint start = 0;
1586
1587 g_assert(literal); /* escapes not implemented yet */
1588 g_return_val_if_fail(replace, 0);
1589
1590 /* ensure haystack->str is not null */
1591 if (haystack->len == 0)
1592 return 0;
1593
1594 /* passing a start position makes G_REGEX_MATCH_NOTBOL automatic */
1595 while (g_regex_match_full(regex, haystack->str, -1, start, 0, &minfo, NULL))
1596 {
1597 gint end, len;
1598
1599 g_match_info_fetch_pos(minfo, match_num, &start, &end);
1600 len = end - start;
1601 utils_string_replace(haystack, start, len, replace);
1602 ret++;
1603
1604 /* skip past whole match */
1605 g_match_info_fetch_pos(minfo, 0, NULL, &end);
1606 start = end - len + strlen(replace);
1607 g_match_info_free(minfo);
1608 }
1609 g_match_info_free(minfo);
1610 return ret;
1611}
1612
1613
1614/* Get project or default startup directory (if set), or NULL. */
1616{
1617 if (app->project && !EMPTY(app->project->base_path))
1618 {
1619 return app->project->base_path;
1620 }
1621
1623 {
1624 return prefs.default_open_path;
1625 }
1626 return NULL;
1627}
1628
1629
1630/**
1631 * Wraps @c spawn_sync(), which see.
1632 *
1633 * @param dir @nullable The child's current working directory, or @c NULL to inherit parent's.
1634 * @param argv The child's argument vector.
1635 * @param env @nullable The child's environment, or @c NULL to inherit parent's.
1636 * @param flags Ignored.
1637 * @param child_setup @girskip Ignored.
1638 * @param user_data @girskip Ignored.
1639 * @param std_out @out @optional The return location for child output, or @c NULL.
1640 * @param std_err @out @optional The return location for child error messages, or @c NULL.
1641 * @param exit_status @out @optional The child exit status, as returned by waitpid(), or @c NULL.
1642 * @param error The return location for error or @c NULL.
1643 *
1644 * @return @c TRUE on success, @c FALSE if an error was set.
1645 **/
1646GEANY_API_SYMBOL
1647gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1648 GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1649 gchar **std_err, gint *exit_status, GError **error)
1650{
1651 GString *output = std_out ? g_string_new(NULL) : NULL;
1652 GString *errors = std_err ? g_string_new(NULL) : NULL;
1653 gboolean result = spawn_sync(dir, NULL, argv, env, NULL, output, errors, exit_status, error);
1654
1655 if (std_out)
1656 *std_out = g_string_free(output, !result);
1657
1658 if (std_err)
1659 *std_err = g_string_free(errors, !result);
1660
1661 return result;
1662}
1663
1664
1665/**
1666 * Wraps @c spawn_async(), which see.
1667 *
1668 * @param dir @nullable The child's current working directory, or @c NULL to inherit parent's.
1669 * @param argv The child's argument vector.
1670 * @param env @nullable The child's environment, or @c NULL to inherit parent's.
1671 * @param flags Ignored.
1672 * @param child_setup @girskip Ignored.
1673 * @param user_data Ignored.
1674 * @param child_pid @out @nullable The return location for child process ID, or @c NULL.
1675 * @param error The return location for error or @c NULL.
1676 *
1677 * @return @c TRUE on success, @c FALSE if an error was set.
1678 **/
1679GEANY_API_SYMBOL
1680gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1681 GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1682 GError **error)
1683{
1684 return spawn_async(dir, NULL, argv, env, child_pid, error);
1685}
1686
1687
1688/* Returns "file:///" on Windows, "file://" everywhere else */
1690{
1691#ifdef G_OS_WIN32
1692 return "file:///";
1693#else
1694 return "file://";
1695#endif
1696}
1697
1698
1699/* Retrieves the path for the given URI.
1700 * It returns:
1701 * - the path which was determined by g_filename_from_uri() or GIO
1702 * - NULL if the URI is non-local and gvfs-fuse is not installed
1703 * - a new copy of 'uri' if it is not an URI. */
1704gchar *utils_get_path_from_uri(const gchar *uri)
1705{
1706 gchar *locale_filename;
1707
1708 g_return_val_if_fail(uri != NULL, NULL);
1709
1710 if (! utils_is_uri(uri))
1711 return g_strdup(uri);
1712
1713 /* this will work only for 'file://' URIs */
1714 locale_filename = g_filename_from_uri(uri, NULL, NULL);
1715 /* g_filename_from_uri() failed, so we probably have a non-local URI */
1716 if (locale_filename == NULL)
1717 {
1718 GFile *file = g_file_new_for_uri(uri);
1719 locale_filename = g_file_get_path(file);
1720 g_object_unref(file);
1721 if (locale_filename == NULL)
1722 {
1723 geany_debug("The URI '%s' could not be resolved to a local path. This means "
1724 "that the URI is invalid or that you don't have gvfs-fuse installed.", uri);
1725 }
1726 }
1727
1728 return locale_filename;
1729}
1730
1731
1732gboolean utils_is_uri(const gchar *uri)
1733{
1734 g_return_val_if_fail(uri != NULL, FALSE);
1735
1736 return (strstr(uri, "://") != NULL);
1737}
1738
1739
1740/* path should be in locale encoding */
1741gboolean utils_is_remote_path(const gchar *path)
1742{
1743 g_return_val_if_fail(path != NULL, FALSE);
1744
1745 /* if path is an URI and it doesn't start "file://", we take it as remote */
1746 if (utils_is_uri(path) && strncmp(path, "file:", 5) != 0)
1747 return TRUE;
1748
1749#ifndef G_OS_WIN32
1750 {
1751 static gchar *fuse_path = NULL;
1752 static gsize len = 0;
1753
1754 if (G_UNLIKELY(fuse_path == NULL))
1755 {
1756 fuse_path = g_build_filename(g_get_home_dir(), ".gvfs", NULL);
1757 len = strlen(fuse_path);
1758 }
1759 /* Comparing the file path against a hardcoded path is not the most elegant solution
1760 * but for now it is better than nothing. Ideally, g_file_new_for_path() should create
1761 * proper GFile objects for Fuse paths, but it only does in future GVFS
1762 * versions (gvfs 1.1.1). */
1763 return (strncmp(path, fuse_path, len) == 0);
1764 }
1765#endif
1766
1767 return FALSE;
1768}
1769
1770
1771/* Remove all relative and untidy elements from the path of @a filename.
1772 * @param filename must be a valid absolute path.
1773 * @see utils_get_real_path() - also resolves links. */
1775{
1776 GString *str;
1777 const gchar *needle;
1778 gboolean preserve_double_backslash = FALSE;
1779
1780 g_return_if_fail(g_path_is_absolute(filename));
1781
1782 str = g_string_new(filename);
1783
1784 if (str->len >= 2 && strncmp(str->str, "\\\\", 2) == 0)
1785 preserve_double_backslash = TRUE;
1786
1787#ifdef G_OS_WIN32
1788 /* using MSYS we can get Unix-style separators */
1789 utils_string_replace_all(str, "/", G_DIR_SEPARATOR_S);
1790#endif
1791 /* replace "/./" and "//" */
1792 utils_string_replace_all(str, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1793 utils_string_replace_all(str, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
1794
1795 if (preserve_double_backslash)
1796 g_string_prepend(str, "\\");
1797
1798 /* replace "/../" */
1799 needle = G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S;
1800 while (1)
1801 {
1802 const gchar *c = strstr(str->str, needle);
1803 if (c == NULL)
1804 break;
1805 else
1806 {
1807 gssize pos, sub_len;
1808
1809 pos = c - str->str;
1810 if (pos <= 3)
1811 break; /* bad path */
1812
1813 /* replace "/../" */
1814 g_string_erase(str, pos, strlen(needle));
1815 g_string_insert_c(str, pos, G_DIR_SEPARATOR);
1816
1817 /* search for last "/" before found "/../" */
1818 c = g_strrstr_len(str->str, pos, G_DIR_SEPARATOR_S);
1819 sub_len = pos - (c - str->str);
1820 if (! c)
1821 break; /* bad path */
1822
1823 pos = c - str->str; /* position of previous "/" */
1824 g_string_erase(str, pos, sub_len);
1825 }
1826 }
1827 if (str->len <= strlen(filename))
1828 memcpy(filename, str->str, str->len + 1);
1829 else
1830 g_warn_if_reached();
1831 g_string_free(str, TRUE);
1832}
1833
1834
1835/**
1836 * Removes characters from a string, in place.
1837 *
1838 * @param string String to search.
1839 * @param chars Characters to remove.
1840 *
1841 * @return @a string - return value is only useful when nesting function calls, e.g.:
1842 * @code str = utils_str_remove_chars(g_strdup("f_o_o"), "_"); @endcode
1843 *
1844 * @see @c g_strdelimit.
1845 **/
1846GEANY_API_SYMBOL
1847gchar *utils_str_remove_chars(gchar *string, const gchar *chars)
1848{
1849 const gchar *r;
1850 gchar *w = string;
1851
1852 g_return_val_if_fail(string, NULL);
1853 if (G_UNLIKELY(EMPTY(chars)))
1854 return string;
1855
1856 foreach_str(r, string)
1857 {
1858 if (!strchr(chars, *r))
1859 *w++ = *r;
1860 }
1861 *w = 0x0;
1862 return string;
1863}
1864
1865
1866/* Gets list of sorted filenames with no path and no duplicates from user and system config */
1867GSList *utils_get_config_files(const gchar *subdir)
1868{
1869 gchar *path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, subdir, NULL);
1870 GSList *list = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1871 GSList *syslist, *node;
1872
1873 if (!list)
1874 {
1875 utils_mkdir(path, FALSE);
1876 }
1877 SETPTR(path, g_build_path(G_DIR_SEPARATOR_S, app->datadir, subdir, NULL));
1878 syslist = utils_get_file_list_full(path, FALSE, FALSE, NULL);
1879 /* merge lists */
1880 list = g_slist_concat(list, syslist);
1881
1882 list = g_slist_sort(list, (GCompareFunc) utils_str_casecmp);
1883 /* remove duplicates (next to each other after sorting) */
1884 foreach_slist(node, list)
1885 {
1886 if (node->next && utils_str_equal(node->next->data, node->data))
1887 {
1888 GSList *old = node->next;
1889
1890 g_free(old->data);
1891 node->next = old->next;
1892 g_slist_free1(old);
1893 }
1894 }
1895 g_free(path);
1896 return list;
1897}
1898
1899
1900/* Suffix can be NULL or a string which should be appended to the Help URL like
1901 * an anchor link, e.g. "#some_anchor". */
1902gchar *utils_get_help_url(const gchar *suffix)
1903{
1904 gchar *uri;
1905 const gchar *uri_file_prefix = utils_get_uri_file_prefix();
1906 gint skip = strlen(uri_file_prefix);
1907
1908 uri = g_strconcat(uri_file_prefix, app->docdir, "/index.html", NULL);
1909#ifdef G_OS_WIN32
1910 g_strdelimit(uri, "\\", '/'); /* replace '\\' by '/' */
1911#endif
1912
1913 if (! g_file_test(uri + skip, G_FILE_TEST_IS_REGULAR))
1914 { /* fall back to online documentation if it is not found on the hard disk */
1915 g_free(uri);
1916 uri = g_strconcat(GEANY_HOMEPAGE, "manual/", VERSION, "/index.html", NULL);
1917 }
1918
1919 if (suffix != NULL)
1920 {
1921 SETPTR(uri, g_strconcat(uri, suffix, NULL));
1922 }
1923
1924 return uri;
1925}
1926
1927
1928static gboolean str_in_array(const gchar **haystack, const gchar *needle)
1929{
1930 const gchar **p;
1931
1932 for (p = haystack; *p != NULL; ++p)
1933 {
1934 if (utils_str_equal(*p, needle))
1935 return TRUE;
1936 }
1937 return FALSE;
1938}
1939
1940
1941/**
1942 * Copies the current environment into a new array.
1943 * @a exclude_vars is a @c NULL-terminated array of variable names which should be not copied.
1944 * All further arguments are key, value pairs of variables which should be added to
1945 * the environment.
1946 *
1947 * The argument list must be @c NULL-terminated.
1948 *
1949 * @param exclude_vars @c NULL-terminated array of variable names to exclude.
1950 * @param first_varname Name of the first variable to copy into the new array.
1951 * @param ... Key-value pairs of variable names and values, @c NULL-terminated.
1952 *
1953 * @return @transfer{full} The new environment array. Use @c g_strfreev() to free it.
1954 **/
1955GEANY_API_SYMBOL
1956gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...)
1957{
1958 gchar **result;
1959 gchar **p;
1960 gchar **env;
1961 va_list args;
1962 const gchar *key, *value;
1963 guint n, o;
1964
1965 /* count the additional variables */
1966 va_start(args, first_varname);
1967 for (o = 1; va_arg(args, gchar*) != NULL; o++);
1968 va_end(args);
1969 /* the passed arguments should be even (key, value pairs) */
1970 g_return_val_if_fail(o % 2 == 0, NULL);
1971
1972 o /= 2;
1973
1974 /* get all the environ variables */
1975 env = g_listenv();
1976
1977 /* create an array large enough to hold the new environment */
1978 n = g_strv_length(env);
1979 /* 'n + o + 1' could leak a little bit when exclude_vars is set */
1980 result = g_new(gchar *, n + o + 1);
1981
1982 /* copy the environment */
1983 for (n = 0, p = env; *p != NULL; ++p)
1984 {
1985 /* copy the variable */
1986 value = g_getenv(*p);
1987 if (G_LIKELY(value != NULL))
1988 {
1989 /* skip excluded variables */
1990 if (exclude_vars != NULL && str_in_array(exclude_vars, *p))
1991 continue;
1992
1993 result[n++] = g_strconcat(*p, "=", value, NULL);
1994 }
1995 }
1996 g_strfreev(env);
1997
1998 /* now add additional variables */
1999 va_start(args, first_varname);
2000 key = first_varname;
2001 value = va_arg(args, gchar*);
2002 while (key != NULL)
2003 {
2004 result[n++] = g_strconcat(key, "=", value, NULL);
2005
2006 key = va_arg(args, gchar*);
2007 if (key == NULL)
2008 break;
2009 value = va_arg(args, gchar*);
2010 }
2011 va_end(args);
2012
2013 result[n] = NULL;
2014
2015 return result;
2016}
2017
2018
2019/* Joins @a first and @a second into a new string vector, freeing the originals.
2020 * The original contents are reused. */
2021gchar **utils_strv_join(gchar **first, gchar **second)
2022{
2023 gchar **strv;
2024 gchar **rptr, **wptr;
2025
2026 if (!first)
2027 return second;
2028 if (!second)
2029 return first;
2030
2031 strv = g_new0(gchar*, g_strv_length(first) + g_strv_length(second) + 1);
2032 wptr = strv;
2033
2034 foreach_strv(rptr, first)
2035 *wptr++ = *rptr;
2036 foreach_strv(rptr, second)
2037 *wptr++ = *rptr;
2038
2039 g_free(first);
2040 g_free(second);
2041 return strv;
2042}
2043
2044/* * Returns the common prefix in a list of strings.
2045 *
2046 * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv).
2047 *
2048 * @param strv The list of strings to process.
2049 * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL.
2050 *
2051 * @return The common prefix that is part of all strings (maybe empty), or NULL if an empty list
2052 * was passed in.
2053 */
2054GEANY_EXPORT_SYMBOL
2055gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len)
2056{
2057 gsize num;
2058
2059 if (strv_len == 0)
2060 return NULL;
2061
2062 num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len;
2063
2064 for (gsize i = 0; strv[0][i]; i++)
2065 {
2066 for (gsize j = 1; j < num; j++)
2067 {
2068 if (strv[j][i] != strv[0][i])
2069 {
2070 /* return prefix on first mismatch */
2071 return g_strndup(strv[0], i);
2072 }
2073 }
2074 }
2075
2076 return g_strdup(strv[0]);
2077}
2078
2079
2080/* * Returns the longest common substring in a list of strings.
2081 *
2082 * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv).
2083 *
2084 * @param strv The list of strings to process.
2085 * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL.
2086 *
2087 * @return The common prefix that is part of all strings.
2088 */
2089GEANY_EXPORT_SYMBOL
2090gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len, const gchar *delim)
2091{
2092 gchar *first, *_sub, *sub;
2093 gsize num;
2094 gsize n_chars;
2095 gsize len;
2096 gsize max = 0;
2097 char *lcs;
2098 gsize found;
2099
2100 if (strv_len == 0)
2101 return NULL;
2102
2103 num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len;
2104
2105 first = strv[0];
2106 len = strlen(first);
2107
2108 /* sub is the working area where substrings from first are copied to */
2109 sub = g_malloc(len+1);
2110 lcs = g_strdup("");
2111 foreach_str(_sub, first)
2112 {
2113 gsize chars_left = len - (_sub - first);
2114 /* No point in continuing if the remainder is too short */
2115 if (max > chars_left)
2116 break;
2117 /* If delimiters are given, we only need to compare substrings which start and
2118 * end with one of them, so skip any non-delim chars at front ... */
2119 if (NZV(delim) && (strchr(delim, _sub[0]) == NULL))
2120 continue;
2121 for (n_chars = 1; n_chars <= chars_left; n_chars++)
2122 {
2123 if (NZV(delim))
2124 { /* ... and advance to the next delim char at the end, if any */
2125 if (!_sub[n_chars] || strchr(delim, _sub[n_chars]) == NULL)
2126 continue;
2127 n_chars += 1;
2128 }
2129 g_strlcpy(sub, _sub, n_chars+1);
2130 found = 1;
2131 for (gsize i = 1; i < num; i++)
2132 {
2133 if (strstr(strv[i], sub) == NULL)
2134 break;
2135 found++;
2136 }
2137 if (found == num && n_chars > max)
2138 {
2139 max = n_chars;
2140 SETPTR(lcs, g_strdup(sub));
2141 }
2142 }
2143 }
2144 g_free(sub);
2145
2146 return lcs;
2147}
2148
2149
2150/** Transform file names in a list to be shorter.
2151 *
2152 * This function takes a list of file names (probably with absolute paths), and
2153 * transforms the paths such that they are short but still unique. This is intended
2154 * for dialogs which present the file list to the user, where the base name may result
2155 * in duplicates (showing the full path might be inappropriate).
2156 *
2157 * The algorthm strips the common prefix (e-g. the user's home directory) and
2158 * replaces the longest common substring with an ellipsis ("...").
2159 *
2160 * @param file_names @array{length=file_names_len} The list of strings to process.
2161 * @param file_names_len The number of strings contained in @a file_names. Can be -1 if it's
2162 * terminated by @c NULL.
2163 * @return @transfer{full} A newly-allocated array of transformed paths strings, terminated by
2164 @c NULL. Use @c g_strfreev() to free it.
2165 *
2166 * @since 1.34 (API 239)
2167 */
2168GEANY_API_SYMBOL
2169gchar **utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len)
2170{
2171 gsize num;
2172 gsize i;
2173 gchar *prefix, *lcs, *end;
2174 gchar **names;
2175 gsize prefix_len = 0, lcs_len = 0;
2176
2177 if (file_names_len == 0)
2178 return g_new0(gchar *, 1);
2179
2180 g_return_val_if_fail(file_names != NULL, NULL);
2181
2182 num = (file_names_len == -1) ? g_strv_length(file_names) : (gsize) file_names_len;
2183 /* Always include a terminating NULL, enables easy freeing with g_strfreev()
2184 * We just copy the pointers so we can advance them here. But don't
2185 * forget to duplicate the strings before returning.
2186 */
2187 names = g_new(gchar *, num + 1);
2188 memcpy(names, file_names, num * sizeof(gchar *));
2189 /* Always include a terminating NULL, enables easy freeing with g_strfreev() */
2190 names[num] = NULL;
2191
2192 /* First: determine the common prefix, that will be stripped.
2193 * We only want to strip full path components, including the trailing slash.
2194 * Except if the component is just "/".
2195 */
2196 prefix = utils_strv_find_common_prefix(names, num);
2197 end = strrchr(prefix, G_DIR_SEPARATOR);
2198 if (end && end > prefix)
2199 {
2200 prefix_len = end - prefix + 1; /* prefix_len includes the trailing slash */
2201 for (i = 0; i < num; i++)
2202 names[i] += prefix_len;
2203 }
2204
2205 /* Second: determine the longest common substring (lcs), that will be ellipsized. Again,
2206 * we look only for full path compnents so that we ellipsize between separators. This implies
2207 * that the file name cannot be ellipsized which is desirable anyway.
2208 */
2209 lcs = utils_strv_find_lcs(names, num, G_DIR_SEPARATOR_S"/");
2210 if (lcs)
2211 {
2212 lcs_len = strlen(lcs);
2213 /* Don't bother for tiny common parts (which are often just "." or "/"). Beware
2214 * that lcs includes the enclosing dir separators so the part must be at least 5 chars
2215 * to be eligible for ellipsizing.
2216 */
2217 if (lcs_len < 7)
2218 lcs_len = 0;
2219 }
2220
2221 /* Last: build the shortened list of unique file names */
2222 for (i = 0; i < num; i++)
2223 {
2224 if (lcs_len == 0)
2225 { /* no lcs, copy without prefix */
2226 names[i] = g_strdup(names[i]);
2227 }
2228 else
2229 {
2230 const gchar *lcs_start = strstr(names[i], lcs);
2231 const gchar *lcs_end = lcs_start + lcs_len;
2232 /* Dir seperators are included in lcs but shouldn't be elipsized. */
2233 names[i] = g_strdup_printf("%.*s...%s", (int)(lcs_start - names[i] + 1), names[i], lcs_end - 1);
2234 }
2235 }
2236
2237 g_free(lcs);
2238 g_free(prefix);
2239
2240 return names;
2241}
2242
2243
2244/* Try to parse a date using g_date_set_parse(). It doesn't take any format hint,
2245 * obviously g_date_set_parse() uses some magic.
2246 * The returned GDate object must be freed. */
2247GDate *utils_parse_date(const gchar *input)
2248{
2249 GDate *date = g_date_new();
2250
2251 g_date_set_parse(date, input);
2252
2253 if (g_date_valid(date))
2254 return date;
2255
2256 g_date_free(date);
2257 return NULL;
2258}
2259
2260
2261gchar *utils_parse_and_format_build_date(const gchar *input)
2262{
2263 gchar date_buf[255];
2264 GDate *date = utils_parse_date(input);
2265
2266 if (date != NULL)
2267 {
2268 g_date_strftime(date_buf, sizeof(date_buf), GEANY_TEMPLATES_FORMAT_DATE, date);
2269 g_date_free(date);
2270 return g_strdup(date_buf);
2271 }
2272
2273 return g_strdup(input);
2274}
2275
2276
2278{
2279#ifdef G_OS_WIN32
2280 return win32_get_user_config_dir();
2281#else
2282 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
2283#endif
2284}
2285
2286
2287static gboolean is_osx_bundle(void)
2288{
2289#ifdef MAC_INTEGRATION
2290 gchar *bundle_id = gtkosx_application_get_bundle_id();
2291 if (bundle_id)
2292 {
2293 g_free(bundle_id);
2294 return TRUE;
2295 }
2296#endif
2297 return FALSE;
2298}
2299
2300
2301const gchar *utils_resource_dir(GeanyResourceDirType type)
2302{
2303 static const gchar *resdirs[RESOURCE_DIR_COUNT] = {NULL};
2304
2305 if (!resdirs[RESOURCE_DIR_DATA])
2306 {
2307#ifdef G_OS_WIN32
2308 gchar *prefix = win32_get_installation_dir();
2309
2310 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "data", NULL);
2311 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2312 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2313 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2314 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2315 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
2316 g_free(prefix);
2317#else
2318 if (is_osx_bundle())
2319 {
2320# ifdef MAC_INTEGRATION
2321 gchar *prefix = gtkosx_application_get_resource_path();
2322
2323 resdirs[RESOURCE_DIR_DATA] = g_build_filename(prefix, "share", "geany", NULL);
2324 resdirs[RESOURCE_DIR_ICON] = g_build_filename(prefix, "share", "icons", NULL);
2325 resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
2326 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
2327 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
2328 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
2329 g_free(prefix);
2330# endif
2331 }
2332 else
2333 {
2334 resdirs[RESOURCE_DIR_DATA] = g_build_filename(GEANY_DATADIR, "geany", NULL);
2335 resdirs[RESOURCE_DIR_ICON] = g_build_filename(GEANY_DATADIR, "icons", NULL);
2336 resdirs[RESOURCE_DIR_DOC] = g_build_filename(GEANY_DOCDIR, "html", NULL);
2337 resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(GEANY_LOCALEDIR, NULL);
2338 resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(GEANY_LIBDIR, "geany", NULL);
2339 resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(GEANY_LIBEXECDIR, "geany", NULL);
2340 }
2341#endif
2342 }
2343
2344 return resdirs[type];
2345}
2346
2347
2348void utils_start_new_geany_instance(const gchar *doc_path)
2349{
2350 const gchar *command = is_osx_bundle() ? "open" : "geany";
2351 gchar *exec_path = g_find_program_in_path(command);
2352
2353 if (exec_path)
2354 {
2355 GError *err = NULL;
2356 const gchar *argv[6]; // max args + 1
2357 gint argc = 0;
2358
2359 argv[argc++] = exec_path;
2360 if (is_osx_bundle())
2361 {
2362 argv[argc++] = "-n";
2363 argv[argc++] = "-a";
2364 argv[argc++] = "Geany";
2365 argv[argc++] = doc_path;
2366 }
2367 else
2368 {
2369 argv[argc++] = "-i";
2370 argv[argc++] = doc_path;
2371 }
2372 argv[argc] = NULL;
2373
2374 if (!utils_spawn_async(NULL, (gchar**) argv, NULL, 0, NULL, NULL, NULL, &err))
2375 {
2376 g_printerr("Unable to open new window: %s\n", err->message);
2377 g_error_free(err);
2378 }
2379 g_free(exec_path);
2380 }
2381 else
2382 g_printerr("Unable to find 'geany'\n");
2383}
2384
2385
2386/**
2387 * Get a link-dereferenced, absolute version of a file name.
2388 *
2389 * This is similar to the POSIX `realpath` function when passed a
2390 * @c NULL argument.
2391 *
2392 * @warning This function suffers the same problems as the POSIX
2393 * function `realpath()`, namely that it's impossible to determine
2394 * a suitable size for the returned buffer, and so it's limited to a
2395 * maximum of `PATH_MAX`.
2396 *
2397 * @param file_name The file name to get the real path of.
2398 *
2399 * @return A newly-allocated string containing the real path which
2400 * should be freed with `g_free()` when no longer needed, or @c NULL
2401 * if the real path cannot be obtained.
2402 *
2403 * @since 1.32 (API 235)
2404 */
2405GEANY_API_SYMBOL
2406gchar *utils_get_real_path(const gchar *file_name)
2407{
2408 return tm_get_real_path(file_name);
2409}
2410
2411
2412/*
2413 * Get a string describing the OS.
2414 *
2415 * If the OS can be determined, a string which describes the OS will
2416 * be returned. If no OS can be determined then `NULL` will be returned.
2417 *
2418 * @note The format of the returned string is unspecified and is only
2419 * meant to provide diagnostic information to the user.
2420 *
2421 * @return A newly-allocated string containing a description of the
2422 * OS if it can be determined or `NULL` if it cannot.
2423 *
2424 * @since 1.37
2425 */
2427{
2428 gchar *os_info = NULL;
2429
2430#if GLIB_CHECK_VERSION(2, 64, 0)
2431# if ! defined(__APPLE__)
2432 /* on non-macOS operating systems */
2433 {
2434 GString *os_str;
2435 gchar *pretty_name;
2436 gchar *code_name;
2437
2438 pretty_name = g_get_os_info(G_OS_INFO_KEY_PRETTY_NAME);
2439 if (pretty_name == NULL)
2440 return NULL;
2441
2442 os_str = g_string_new(pretty_name);
2443 g_free(pretty_name);
2444
2445 code_name = g_get_os_info(G_OS_INFO_KEY_VERSION_CODENAME);
2446 if (code_name != NULL)
2447 {
2448 g_string_append_printf(os_str, " (%s)", code_name);
2449 g_free(code_name);
2450 }
2451
2452 os_info = g_string_free(os_str, FALSE);
2453 }
2454# else
2455 /* on macOS, only `G_OS_INFO_KEY_NAME` is supported and returns the
2456 * fixed string `macOS` */
2457 os_info = g_get_os_info(G_OS_INFO_KEY_NAME);
2458# endif
2459#else
2460 /* if g_get_os_info() is not available, do it the old-fashioned way */
2461# if defined(_WIN64)
2462 os_info = g_strdup("Microsoft Windows (64-bit)");
2463# elif defined(_WIN32)
2464 os_info = g_strdup("Microsoft Windows");
2465# elif defined(__APPLE__)
2466 os_info = g_strdup("Apple macOS");
2467# elif defined(__linux__)
2468 os_info = g_strdup("Linux");
2469# elif defined(__FreeBSD__)
2470 os_info = g_strdup("FreeBSD");
2471# elif defined(__ANDROID__)
2472 os_info = g_strdup("Android");
2473# endif
2474#endif
2475
2476 return os_info;
2477}
#define SC_EOL_CR
Definition: Scintilla.h:89
#define SC_EOL_LF
Definition: Scintilla.h:90
#define SC_EOL_CRLF
Definition: Scintilla.h:88
Contains the GeanyApp.
GeanyBuildCommand ** ptr
Definition: build.c:2679
const gchar * command
Definition: build.c:2677
gchar * dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text)
Asks the user for text input.
Definition: dialogs.c:1067
File related dialogs, miscellaneous dialogs, font dialog.
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
const gchar * name
Definition: document.c:3219
GeanyFilePrefs file_prefs
Definition: document.c:86
GdkColor color
Definition: document.c:3220
Document related actions: new, save, open, etc.
gchar * text
Definition: editor.c:83
gint pos
Definition: editor.c:87
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
int errno
#define GEANY_HOMEPAGE
Definition: geany.h:45
CobolFormat format
Definition: geany_cobol.c:137
unsigned int count
tokenInfo * list
unsigned int max
accessType access
Definition: geany_php.c:232
const gchar * chars[][2]
Definition: htmlchars.c:72
GeanyApp * app
Definition: libmain.c:86
void geany_debug(gchar const *format,...)
Definition: log.c:67
#define MAX(a, b)
Definition: mio.c:93
GeanyToolPrefs tool_prefs
Definition: prefs.c:67
GeanyPrefs prefs
Definition: prefs.c:66
#define NULL
Definition: rbtree.h:150
char * strstr(const char *str, const char *substr)
Definition: routines.c:304
Wrapper functions for the Scintilla editor widget SCI_* messages.
gboolean spawn_async(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, GPid *child_pid, GError **error)
Executes a child asynchronously.
Definition: spawn.c:814
gboolean spawn_sync(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, SpawnWriteData *stdin_data, GString *stdout_data, GString *stderr_data, gint *exit_status, GError **error)
Executes a child synchronously.
Definition: spawn.c:1357
Portable and convenient process spawning and communication.
const gchar filename[]
Definition: stash-example.c:4
struct GeanyProject * project
Currently active project or NULL if none is open.
Definition: app.h:49
gchar * docdir
Definition: app.h:47
gchar * configdir
User configuration directory, usually ~/.config/geany.
Definition: app.h:45
gchar * datadir
Definition: app.h:46
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
gboolean use_safe_file_saving
Definition: document.h:60
GtkWidget * window
Main window.
Definition: ui_utils.h:80
gboolean beep_on_errors
Definition: prefs.h:35
gchar * default_open_path
Default path to look for files when no other path is appropriate.
Definition: prefs.h:39
gchar * base_path
Base path of the project directory (in UTF-8, maybe relative).
Definition: project.h:39
gchar * browser_cmd
web browser command
Definition: prefs.h:51
Defines internationalization macros.
#define _(String)
Definition: support.h:42
Templates (prefs).
gchar * tm_get_real_path(const gchar *file_name)
Given a file name, returns a newly allocated string containing the realpath() of the file.
The TMSourceFile structure and associated functions are used to maintain tags for individual files.
#define FALLBACK(X, Y)
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
User Interface general utility functions.
gchar * utils_make_human_readable_str(guint64 size, gulong block_size, gulong display_unit)
Definition: utils.c:926
gchar * utils_get_path_from_uri(const gchar *uri)
Definition: utils.c:1704
void utils_open_browser(const gchar *uri)
Tries to open the given URI in a browser.
Definition: utils.c:75
gchar ** utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname,...)
Copies the current environment into a new array.
Definition: utils.c:1956
gboolean utils_parse_color(const gchar *spec, GdkColor *color)
Definition: utils.c:976
gchar * utils_str_middle_truncate(const gchar *string, guint truncate_length)
Truncates the input string to a given length.
Definition: utils.c:549
GEANY_EXPORT_SYMBOL gchar * utils_strv_find_lcs(gchar **strv, gssize strv_len, const gchar *delim)
Definition: utils.c:2090
gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
Wraps spawn_sync(), which see.
Definition: utils.c:1647
GEANY_EXPORT_SYMBOL gchar * utils_strv_find_common_prefix(gchar **strv, gssize strv_len)
Definition: utils.c:2055
gboolean utils_is_short_html_tag(const gchar *tag_name)
Definition: utils.c:338
gchar * utils_get_help_url(const gchar *suffix)
Definition: utils.c:1902
gboolean utils_str_has_upper(const gchar *str)
Definition: utils.c:1452
gint utils_write_file(const gchar *filename, const gchar *text)
Writes text into a file named filename.
Definition: utils.c:209
gint utils_str_casecmp(const gchar *s1, const gchar *s2)
A replacement function for g_strncasecmp() to compare strings case-insensitive.
Definition: utils.c:506
guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
Definition: utils.c:1529
gchar * utils_get_os_info_string(void)
Definition: utils.c:2426
gint utils_color_to_bgr(const GdkColor *c)
Definition: utils.c:996
static gchar * utf8_strdown(const gchar *str)
Definition: utils.c:471
gchar * utils_parse_and_format_build_date(const gchar *input)
Definition: utils.c:2261
gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key, const gint default_value)
Wraps g_key_file_get_integer() to add a default value argument.
Definition: utils.c:805
gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key, const gboolean default_value)
Wraps g_key_file_get_boolean() to add a default value argument.
Definition: utils.c:836
gint utils_strpos(const gchar *haystack, const gchar *needle)
Definition: utils.c:707
GEANY_EXPORT_SYMBOL gchar ** utils_strv_new(const gchar *first,...)
Definition: utils.c:1316
GSList * utils_get_config_files(const gchar *subdir)
Definition: utils.c:1867
gchar * utils_get_utf8_from_locale(const gchar *locale_text)
Converts the given string (in locale encoding) into UTF-8 encoding.
Definition: utils.c:1272
gchar ** utils_strv_join(gchar **first, gchar **second)
Definition: utils.c:2021
const gchar * utils_path_skip_root(const gchar *path)
Definition: utils.c:444
gchar * utils_get_current_file_dir_utf8(void)
Definition: utils.c:897
GSList * utils_get_file_list(const gchar *path, guint *length, GError **error)
Gets a sorted list of files from the specified directory.
Definition: utils.c:1441
GSList * utils_get_file_list_full(const gchar *path, gboolean full_path, gboolean sort, GError **error)
Gets a list of files from the specified directory.
Definition: utils.c:1396
void utils_tidy_path(gchar *filename)
Definition: utils.c:1774
gchar * utils_get_date_time(const gchar *format, time_t *time_to_use)
Retrieves a formatted date/time string from strftime().
Definition: utils.c:736
gchar * utils_get_initials(const gchar *name)
Definition: utils.c:774
void utils_beep(void)
Definition: utils.c:918
gchar * utils_get_current_time_string(gboolean include_microseconds)
Definition: utils.c:1019
gboolean utils_is_absolute_path(const gchar *path)
Definition: utils.c:432
gchar * utils_find_open_xml_tag(const gchar sel[], gint size)
Searches backward through size bytes looking for a '<'.
Definition: utils.c:269
const gchar * utils_find_open_xml_tag_pos(const gchar sel[], gint size)
Searches backward through size bytes looking for a '<'.
Definition: utils.c:294
const gchar * utils_resource_dir(GeanyResourceDirType type)
Definition: utils.c:2301
gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
Definition: utils.c:1071
guint utils_string_regex_replace_all(GString *haystack, GRegex *regex, guint match_num, const gchar *replace, gboolean literal)
Definition: utils.c:1580
static gboolean is_osx_bundle(void)
Definition: utils.c:2287
GIOChannel * utils_set_up_io_channel(gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
Definition: utils.c:1029
void utils_ensure_same_eol_characters(GString *string, gint target_eol_mode)
Definition: utils.c:405
gint utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
Definition: utils.c:1474
gdouble utils_scale_round(gdouble val, gdouble factor)
Definition: utils.c:459
GDate * utils_parse_date(const gchar *input)
Definition: utils.c:2247
gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
Creates a directory if it doesn't already exist.
Definition: utils.c:1359
gint utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
Definition: utils.c:1505
gint utils_get_line_endings(const gchar *buffer, gsize size)
Definition: utils.c:102
gint utils_parse_color_to_bgr(const gchar *spec)
Definition: utils.c:1005
gboolean utils_wrap_string(gchar *string, gint wrapstart)
Definition: utils.c:1212
gboolean utils_is_uri(const gchar *uri)
Definition: utils.c:1732
void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
Definition: utils.c:692
gchar utils_brace_opposite(gchar ch)
Definition: utils.c:638
const gchar * utils_get_uri_file_prefix(void)
Definition: utils.c:1689
gchar * utils_str_remove_chars(gchar *string, const gchar *chars)
Removes characters from a string, in place.
Definition: utils.c:1847
const gchar * utils_get_eol_char(gint eol_mode)
Definition: utils.c:393
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
gboolean utils_isbrace(gchar c, gboolean include_angles)
Definition: utils.c:159
gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
Definition: utils.c:178
gchar ** utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len)
Transform file names in a list to be shorter.
Definition: utils.c:2169
const gchar * utils_get_eol_short_name(gint eol_mode)
Definition: utils.c:382
void utils_start_new_geany_instance(const gchar *doc_path)
Definition: utils.c:2348
const gchar * utils_get_default_dir_utf8(void)
Definition: utils.c:1615
gchar * utils_remove_ext_from_filename(const gchar *filename)
Removes the extension from filename and return the result in a newly allocated string.
Definition: utils.c:617
gchar * utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key, const gchar *default_value)
Wraps g_key_file_get_string() to add a default value argument.
Definition: utils.c:867
gboolean utils_is_remote_path(const gchar *path)
Definition: utils.c:1741
gchar * utils_get_user_config_dir(void)
Definition: utils.c:2277
void utils_free_pointers(gsize arg_count,...)
Definition: utils.c:1293
gint utils_is_file_writable(const gchar *locale_filename)
Definition: utils.c:657
gboolean utils_atob(const gchar *str)
Definition: utils.c:421
gchar * utils_get_hex_from_color(GdkColor *color)
Definition: utils.c:883
gchar * utils_get_real_path(const gchar *file_name)
Get a link-dereferenced, absolute version of a file name.
Definition: utils.c:2406
static gboolean str_in_array(const gchar **haystack, const gchar *needle)
Definition: utils.c:1928
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
const gchar * utils_get_eol_name(gint eol_mode)
Definition: utils.c:371
gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid, GError **error)
Wraps spawn_async(), which see.
Definition: utils.c:1680
guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
Replaces only the first occurrence of needle in haystack with replace.
Definition: utils.c:1563
General utility functions, non-GTK related.
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121
#define foreach_strv(str_ptr, strv)
Iterates a null-terminated string vector.
Definition: utils.h:152
#define NZV(ptr)
Definition: utils.h:43
#define foreach_str(char_ptr, string)
Iterates through each character in string.
Definition: utils.h:145
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define foreach_dir(filename, dir)
Iterates through each unsorted filename in a GDir.
Definition: utils.h:137
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38