163
167
case ']': return TRUE;
164
168
default: return FALSE;
171
gboolean utils_is_opening_brace(gchar c)
173
gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
173
// match < only if desired, because I don't like it, but some people do
174
if (app->brace_match_ltgt)
178
case '<': return TRUE;
178
return include_angles;
186
182
case '[': return TRUE;
187
183
default: return FALSE;
194
const GList *utils_get_tag_list(gint idx, guint tag_types)
196
static GList *tag_names = NULL;
198
if (idx >= 0 && doc_list[idx].is_valid && doc_list[idx].tm_file &&
199
doc_list[idx].tm_file->tags_array)
204
gboolean doc_is_utf8 = FALSE;
210
for (tmp = tag_names; tmp; tmp = g_list_next(tmp))
212
g_free(((GeanySymbol*)tmp->data)->str);
215
g_list_free(tag_names);
219
// do this comparison only once
220
if (utils_strcmp(doc_list[idx].encoding, "UTF-8")) doc_is_utf8 = TRUE;
222
for (i = 0; i < (doc_list[idx].tm_file)->tags_array->len; ++i)
224
tag = TM_TAG((doc_list[idx].tm_file)->tags_array->pdata[i]);
228
if (tag->type & tag_types)
230
if (! doc_is_utf8) utf8_name = utils_convert_to_utf8_from_charset(tag->name, -1,
231
doc_list[idx].encoding, TRUE);
232
else utf8_name = tag->name;
233
if ((tag->atts.entry.scope != NULL) && isalpha(tag->atts.entry.scope[0]))
235
symbol = g_new0(GeanySymbol, 1);
236
symbol->str = g_strdup_printf("%s::%s [%ld]", tag->atts.entry.scope, utf8_name, tag->atts.entry.line);
237
symbol->type = tag->type;
238
symbol->line = tag->atts.entry.line;
239
tag_names = g_list_prepend(tag_names, symbol);
243
symbol = g_new0(GeanySymbol, 1);
244
symbol->str = g_strdup_printf("%s [%ld]", utf8_name, tag->atts.entry.line);
245
symbol->type = tag->type;
246
symbol->line = tag->atts.entry.line;
247
tag_names = g_list_prepend(tag_names, symbol);
249
if (! doc_is_utf8) g_free(utf8_name);
252
tag_names = g_list_sort(tag_names, (GCompareFunc) utils_compare_symbol);
259
/* returns the line of the given tag */
260
gint utils_get_local_tag(gint idx, const gchar *qual_name)
265
g_return_val_if_fail((doc_list[idx].sci && qual_name), -1);
267
spos = strchr(qual_name, '[');
268
if (spos && strchr(spos+1, ']'))
270
line = atol(spos + 1);
280
gboolean utils_goto_file_line(const gchar *file, gboolean is_tm_filename, gint line)
188
/* line is counted with 1 as the first line, not 0 */
189
gboolean utils_goto_file_pos(const gchar *file, gboolean is_tm_filename, gint pos)
282
191
gint file_idx = document_find_by_filename(file, is_tm_filename);
284
193
if (file_idx < 0) return FALSE;
286
return utils_goto_line(file_idx, line);
195
return utils_goto_pos(file_idx, pos);
199
/* line is counted with 1 as the first line, not 0 */
290
200
gboolean utils_goto_line(gint idx, gint line)
294
line--; // the User counts lines from 1, we begin at 0 so bring the User line to our one
296
if (idx == -1 || ! doc_list[idx].is_valid || line < 0)
300
sci_marker_delete_all(doc_list[idx].sci, 0);
301
sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
303
sci_goto_line_scroll(doc_list[idx].sci, line, 0.25);
305
// finally switch to the page
306
page_num = gtk_notebook_page_num(GTK_NOTEBOOK(app->notebook), GTK_WIDGET(doc_list[idx].sci));
307
gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), page_num);
204
line--; /* the user counts lines from 1, we begin at 0 so bring the user line to our one */
206
if (! DOC_IDX_VALID(idx) || line < 0)
210
sci_marker_delete_all(doc_list[idx].sci, 0);
211
sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
213
sci_goto_line(doc_list[idx].sci, line, TRUE);
214
doc_list[idx].scroll_percent = 0.25F;
216
/* finally switch to the page */
217
page_num = gtk_notebook_page_num(GTK_NOTEBOOK(app->notebook), GTK_WIDGET(doc_list[idx].sci));
218
gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), page_num);
224
gboolean utils_goto_pos(gint idx, gint pos)
229
if (! DOC_IDX_VALID(idx) || pos < 0)
232
line = sci_get_line_from_position(doc_list[idx].sci, pos);
235
sci_marker_delete_all(doc_list[idx].sci, 0);
236
sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
238
sci_goto_pos(doc_list[idx].sci, pos, TRUE);
239
doc_list[idx].scroll_percent = 0.25F;
241
/* finally switch to the page */
242
page_num = gtk_notebook_page_num(GTK_NOTEBOOK(app->notebook), GTK_WIDGET(doc_list[idx].sci));
243
gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), page_num);
250
* Write the given @c text into a file with @c filename.
251
* If the file doesn't exist, it will be created.
252
* If it already exists, it will be overwritten.
254
* @param filename The filename of the file to write, in locale encoding.
255
* @param text The text to write into the file.
257
* @return 0 if the directory was successfully created, otherwise the @c errno of the
258
* failed operation is returned.
313
260
gint utils_write_file(const gchar *filename, const gchar *text)
340
geany_debug("utils_write_file(): could not write to file %s", filename);
287
geany_debug("utils_write_file(): could not write to file %s (%s)",
288
filename, g_strerror(errno));
347
/* Converts a string from the given charset to UTF-8.
348
* If fast is set, no further checks are performed. */
349
gchar *utils_convert_to_utf8_from_charset(const gchar *buffer, gsize size, const gchar *charset,
352
gchar *utf8_content = NULL;
353
GError *conv_error = NULL;
354
gchar* converted_contents = NULL;
357
g_return_val_if_fail(buffer != NULL, NULL);
358
g_return_val_if_fail(charset != NULL, NULL);
360
converted_contents = g_convert(buffer, size, "UTF-8", charset, NULL,
361
&bytes_written, &conv_error);
365
utf8_content = converted_contents;
366
if (conv_error != NULL) g_error_free(conv_error);
368
else if (conv_error != NULL || ! g_utf8_validate(converted_contents, bytes_written, NULL))
370
if (conv_error != NULL)
372
geany_debug("Couldn't convert from %s to UTF-8 (%s).", charset, conv_error->message);
373
g_error_free(conv_error);
377
geany_debug("Couldn't convert from %s to UTF-8.", charset);
380
if (converted_contents != NULL) g_free(converted_contents);
384
geany_debug("Converted from %s to UTF-8.", charset);
385
utf8_content = converted_contents;
392
gchar *utils_convert_to_utf8(const gchar *buffer, gsize size, gchar **used_encoding)
394
gchar *locale_charset = NULL;
397
gboolean check_current = FALSE;
400
// current locale is not UTF-8, we have to check this charset
401
check_current = ! g_get_charset((const gchar**)&locale_charset);
403
for (i = 0; i < GEANY_ENCODINGS_MAX; i++)
407
check_current = FALSE;
408
charset = locale_charset;
412
charset = encodings[i].charset;
414
geany_debug("Trying to convert %d bytes of data from %s into UTF-8.", size, charset);
415
utf8_content = utils_convert_to_utf8_from_charset(buffer, size, charset, FALSE);
417
if (utf8_content != NULL)
419
if (used_encoding != NULL)
421
if (*used_encoding != NULL)
423
g_free(*used_encoding);
424
geany_debug("%s:%d", __FILE__, __LINE__);
426
*used_encoding = g_strdup(charset);
437
296
* (stolen from anjuta and modified)
438
297
* Search backward through size bytes looking for a '<', then return the tag if any
439
298
* @return The tag name
441
300
gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag)
443
// 40 chars per tag should be enough, or not?
302
/* 40 chars per tag should be enough, or not? */
444
303
gint i = 0, max_tag_size = 40;
445
304
gchar *result = g_malloc(max_tag_size);
446
305
const gchar *begin, *cur;
449
// Smallest tag is "<p>" which is 3 characters
309
/* Smallest tag is "<p>" which is 3 characters */
476
336
result[i] = '\0';
477
// Return the tag name or ""
337
/* Return the tag name or "" */
482
gboolean utils_check_disk_status(gint idx)
342
static gboolean reload_idx(gpointer data)
344
gint idx = GPOINTER_TO_INT(data);
346
/* check idx is still valid now we're idle, in case it was closed */
347
if (DOC_IDX_VALID(idx))
349
document_reload_file(idx, NULL);
355
static gboolean check_reload(gint idx)
357
gchar *base_name = g_path_get_basename(doc_list[idx].file_name);
358
gboolean want_reload;
360
want_reload = dialogs_show_question_full(NULL, _("_Reload"), GTK_STOCK_CANCEL,
361
_("Do you want to reload it?"),
362
_("The file '%s' on the disk is more recent than\n"
363
"the current buffer."), base_name);
366
/* delay reloading because we need to wait for any pending scintilla messages
367
* to be processed, otherwise the reloaded document might not be colourised
369
g_idle_add(reload_idx, GINT_TO_POINTER(idx));
376
/* Set force to force a disk check, otherwise it is ignored if there was a check
377
* in the last GEANY_CHECK_FILE_DELAY seconds.
378
* @return @c TRUE if the file has changed. */
379
gboolean utils_check_disk_status(gint idx, gboolean force)
486
383
gchar *locale_filename;
384
gboolean ret = FALSE;
488
386
if (idx == -1 || doc_list[idx].file_name == NULL) return FALSE;
492
if (doc_list[idx].last_check > (t - GEANY_CHECK_FILE_DELAY)) return FALSE;
390
if (! force && doc_list[idx].last_check > (t - GEANY_CHECK_FILE_DELAY)) return FALSE;
494
392
locale_filename = utils_get_locale_from_utf8(doc_list[idx].file_name);
495
if (stat(locale_filename, &st) != 0) return FALSE;
497
if (doc_list[idx].mtime > t || st.st_mtime > t)
393
if (g_stat(locale_filename, &st) != 0)
395
/* TODO: warn user file on disk is missing */
397
else if (doc_list[idx].mtime > t || st.st_mtime > t)
499
399
geany_debug("Strange: Something is wrong with the time stamps.");
503
if (doc_list[idx].mtime < st.st_mtime)
401
else if (doc_list[idx].mtime < st.st_mtime)
505
gchar *basename = g_path_get_basename(doc_list[idx].file_name);
507
if (dialogs_show_question_full(_("_Reload"), GTK_STOCK_CANCEL,
508
_("Do you want to reload it?"),
509
_("The file '%s' on the disk is more recent than\n"
510
"the current buffer."), basename))
403
if (check_reload(idx))
512
document_reload_file(idx, NULL);
513
doc_list[idx].last_check = t;
405
/* Disable checking until after reload, so ignore this change for now */
516
406
doc_list[idx].mtime = st.st_mtime;
519
return TRUE; //file has changed
409
doc_list[idx].mtime = st.st_mtime; /* Ignore this change on disk completely */
411
ret = TRUE; /* file has changed */
413
g_free(locale_filename);
418
/* This could perhaps be improved to check for #if, class etc. */
419
static gint get_function_fold_number(gint idx)
421
/* for Java the functions are always one fold level above the class scope */
422
if (FILETYPE_ID(doc_list[idx].file_type) == GEANY_FILETYPES_JAVA)
423
return SC_FOLDLEVELBASE + 1;
425
return SC_FOLDLEVELBASE;
429
/* Should be used only with utils_get_current_function. */
430
static gboolean current_function_changed(gint cur_idx, gint cur_line, gint fold_level)
432
static gint old_line = -2;
433
static gint old_idx = -1;
434
static gint old_fold_num = -1;
435
const gint fold_num = fold_level & SC_FOLDLEVELNUMBERMASK;
438
/* check if the cached line and file index have changed since last time: */
439
if (cur_idx < 0 || cur_idx != old_idx)
442
if (cur_line == old_line)
446
/* if the line has only changed by 1 */
447
if (abs(cur_line - old_line) == 1)
450
get_function_fold_number(cur_idx);
451
/* It's the same function if the fold number hasn't changed, or both the new
452
* and old fold numbers are above the function fold number. */
454
fold_num == old_fold_num ||
455
(old_fold_num > fn_fold && fold_num > fn_fold);
462
/* record current line and file index for next time */
465
old_fold_num = fold_num;
470
/* Parse the function name up to 2 lines before tag_line.
471
* C++ like syntax should be parsed by parse_cpp_function_at_line, otherwise the return
472
* type or argument names can be confused with the function name. */
473
static gchar *parse_function_at_line(ScintillaObject *sci, gint tag_line)
475
gint start, end, max_pos;
479
switch (sci_get_lexer(sci))
481
case SCLEX_RUBY: fn_style = SCE_RB_DEFNAME; break;
482
case SCLEX_PYTHON: fn_style = SCE_P_DEFNAME; break;
483
default: fn_style = SCE_C_IDENTIFIER; /* several lexers use SCE_C_IDENTIFIER */
485
start = sci_get_position_from_line(sci, tag_line - 2);
486
max_pos = sci_get_position_from_line(sci, tag_line + 1);
487
while (sci_get_style_at(sci, start) != fn_style
488
&& start < max_pos) start++;
491
while (sci_get_style_at(sci, end) == fn_style
492
&& end < max_pos) end++;
494
if (start == end) return NULL;
495
cur_tag = g_malloc(end - start + 1);
496
sci_get_text_range(sci, start, end, cur_tag);
501
/* Parse the function name */
502
static gchar *parse_cpp_function_at_line(ScintillaObject *sci, gint tag_line)
504
gint start, end, first_pos, max_pos;
509
first_pos = end = sci_get_position_from_line(sci, tag_line);
510
max_pos = sci_get_position_from_line(sci, tag_line + 1);
512
/* goto the begin of function body */
513
while (end < max_pos &&
514
(tmp = sci_get_char_at(sci, end)) != '{' &&
516
if (tmp == 0) end --;
518
/* go back to the end of function identifier */
519
while (end > 0 && end > first_pos - 500 &&
520
(tmp = sci_get_char_at(sci, end)) != '(' &&
523
if (end < 0) end = 0;
525
/* skip whitespaces between identifier and ( */
526
while (end > 0 && isspace(sci_get_char_at(sci, end))) end--;
530
/* Use tmp to find SCE_C_IDENTIFIER or SCE_C_GLOBALCLASS chars */
531
while (start >= 0 && ((tmp = sci_get_style_at(sci, start)) == SCE_C_IDENTIFIER
532
|| tmp == SCE_C_GLOBALCLASS
533
|| (c = sci_get_char_at(sci, start)) == '~'
536
if (start != 0 && start < end) start++; /* correct for last non-matching char */
538
if (start == end) return NULL;
539
cur_tag = g_malloc(end - start + 2);
540
sci_get_text_range(sci, start, end + 1, cur_tag);
545
/* Sets *tagname to point at the current function or tag name.
546
* If idx is -1, reset the cached current tag data to ensure it will be reparsed on the next
547
* call to this function.
548
* Returns: line number of the current tag, or -1 if unknown. */
525
549
gint utils_get_current_function(gint idx, const gchar **tagname)
527
551
static gint tag_line = -1;
529
static gint old_line = -1;
530
static gint old_idx = -1;
531
552
static gchar *cur_tag = NULL;
533
gint start, end, last_pos;
537
line = sci_get_current_line(doc_list[idx].sci, -1);
538
// check if the cached line and file index have changed since last time:
539
if (line == old_line && idx == old_idx)
541
// we can assume same current function as before
545
g_free(cur_tag); // free the old tag, it will be replaced.
546
//record current line and file index for next time
550
// look first in the tag list
551
tags = utils_get_tag_list(idx, tm_tag_max_t);
552
for (; tags; tags = g_list_next(tags))
554
tag_line = ((GeanySymbol*)tags->data)->line;
555
if (line + 1 == tag_line)
555
TMWorkObject *tm_file;
557
if (! DOC_IDX_VALID(idx)) /* reset current function */
559
current_function_changed(-1, -1, -1);
561
cur_tag = g_strdup(_("unknown"));
568
line = sci_get_current_line(doc_list[idx].sci);
569
fold_level = sci_get_fold_level(doc_list[idx].sci, line);
570
/* check if the cached line and file index have changed since last time: */
571
if (! current_function_changed(idx, line, fold_level))
573
/* we can assume same current function as before */
577
g_free(cur_tag); /* free the old tag, it will be replaced. */
579
/* if line is at base fold level, we're not in a function */
580
if ((fold_level & SC_FOLDLEVELNUMBERMASK) == SC_FOLDLEVELBASE)
582
cur_tag = g_strdup(_("unknown"));
587
tm_file = doc_list[idx].tm_file;
589
/* if the document has no changes, get the previous function name from TM */
590
if(! doc_list[idx].changed && tm_file != NULL && tm_file->tags_array != NULL)
592
const TMTag *tag = (const TMTag*) tm_get_current_function(tm_file->tags_array, line);
557
cur_tag = g_strdup(strtok(((GeanySymbol*)tags->data)->str, " "));
597
tmp = tag->atts.entry.scope;
598
cur_tag = tmp ? g_strconcat(tmp, "::", tag->name, NULL) : g_strdup(tag->name);
558
599
*tagname = cur_tag;
600
tag_line = tag->atts.entry.line;
605
/* parse the current function name here because TM line numbers may have changed,
606
* and it would take too long to reparse the whole file. */
563
607
if (doc_list[idx].file_type != NULL &&
564
doc_list[idx].file_type->id != GEANY_FILETYPES_JAVA &&
565
608
doc_list[idx].file_type->id != GEANY_FILETYPES_ALL)
567
fold_level = sci_get_fold_level(doc_list[idx].sci, line);
569
if ((fold_level & 0xFF) != 0)
572
while((fold_level & SC_FOLDLEVELNUMBERMASK) != SC_FOLDLEVELBASE && tag_line >= 0)
610
const gint fn_fold = get_function_fold_number(idx);
613
do /* find the top level fold point */
615
tag_line = sci_get_fold_parent(doc_list[idx].sci, tag_line);
616
fold_level = sci_get_fold_level(doc_list[idx].sci, tag_line);
617
} while (tag_line >= 0 &&
618
(fold_level & SC_FOLDLEVELNUMBERMASK) != fn_fold);
622
if (sci_get_lexer(doc_list[idx].sci) == SCLEX_CPP)
623
cur_tag = parse_cpp_function_at_line(doc_list[idx].sci, tag_line);
625
cur_tag = parse_function_at_line(doc_list[idx].sci, tag_line);
574
fold_level = sci_get_fold_level(doc_list[idx].sci, --tag_line);
577
start = sci_get_position_from_line(doc_list[idx].sci, tag_line - 2);
578
last_pos = sci_get_length(doc_list[idx].sci);
580
while (sci_get_style_at(doc_list[idx].sci, start) != SCE_C_IDENTIFIER
581
&& sci_get_style_at(doc_list[idx].sci, start) != SCE_C_GLOBALCLASS
582
&& start < last_pos) start++;
584
// Use tmp to find SCE_C_IDENTIFIER or SCE_C_GLOBALCLASS chars
585
// this fails on C++ code like 'Vek3 Vek3::mul(double s)' this code returns
586
// "Vek3" because the return type of the prototype is also a GLOBALCLASS,
587
// no idea how to fix at the moment
588
// fails also in C with code like
589
// typedef void viod;
590
// viod do_nothing() {} -> return viod instead of do_nothing
591
// perhaps: get the first colon, read forward the second colon and then method
592
// name, then go from the first colon backwards and read class name until space
593
while (((tmp = sci_get_style_at(doc_list[idx].sci, end)) == SCE_C_IDENTIFIER
594
|| tmp == SCE_C_GLOBALCLASS
595
|| sci_get_char_at(doc_list[idx].sci, end) == '~'
596
|| sci_get_char_at(doc_list[idx].sci, end) == ':')
597
&& end < last_pos) end++;
599
cur_tag = g_malloc(end - start + 1);
600
sci_get_text_range(doc_list[idx].sci, start, end, cur_tag);
792
gchar *utils_get_hostname()
775
gchar *utils_get_hostname(void)
794
#ifndef HAVE_GETHOSTNAME
778
return win32_get_hostname();
779
#elif defined(HAVE_GETHOSTNAME)
781
if (gethostname(hostname, sizeof(hostname)) == 0)
782
return g_strdup(hostname);
795
784
return g_strdup("localhost");
797
gchar *host = g_malloc(25);
798
if (gethostname(host, 24) == 0)
805
return g_strdup("localhost");
811
gint utils_make_settings_dir(const gchar *dir, const gchar *data_dir, const gchar *doc_dir)
814
gchar *filetypes_readme = g_strconcat(
815
dir, G_DIR_SEPARATOR_S, "template.README", NULL);
816
gchar *filedefs_dir = g_strconcat(dir, G_DIR_SEPARATOR_S,
788
/* Checks whether the given file can be written. locale_filename is expected in locale encoding.
789
* Returns 0 if it can be written, otherwise it returns errno */
790
gint utils_is_file_writeable(const gchar *locale_filename)
795
if (! g_file_test(locale_filename, G_FILE_TEST_EXISTS) &&
796
! g_file_test(locale_filename, G_FILE_TEST_IS_DIR))
797
/* get the file's directory to check for write permission if it doesn't yet exist */
798
file = g_path_get_dirname(locale_filename);
800
file = g_strdup(locale_filename);
803
/* use _waccess on Windows, access() doesn't accept special characters */
804
ret = win32_check_write_permission(file);
807
/* access set also errno to "FILE NOT FOUND" even if locale_filename is writeable, so use
808
* errno only when access() explicitly returns an error */
809
if (access(file, R_OK | W_OK) != 0)
820
# define DIR_SEP "\\" /* on Windows we need an additional dir separator */
825
gint utils_make_settings_dir(void)
827
gint saved_errno = 0;
828
gchar *conf_file = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "geany.conf", NULL);
829
gchar *filedefs_dir = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
817
830
GEANY_FILEDEFS_SUBDIR, G_DIR_SEPARATOR_S, NULL);
818
gchar *filedefs_readme = g_strconcat(dir, G_DIR_SEPARATOR_S, GEANY_FILEDEFS_SUBDIR,
819
G_DIR_SEPARATOR_S, "filetypes.README", NULL);
821
if (! g_file_test(dir, G_FILE_TEST_EXISTS))
823
geany_debug("creating config directory %s", dir);
825
if (mkdir(dir) != 0) error_nr = errno;
827
if (mkdir(dir, 0700) != 0) error_nr = errno;
831
if (error_nr == 0 && ! g_file_test(filetypes_readme, G_FILE_TEST_EXISTS))
832
{ // try to write template.README
835
"There are several template files in this directory. For these templates you can use wildcards.\n\
836
For more information read the documentation (in ", doc_dir, "index.html or visit " GEANY_HOMEPAGE ").",
838
error_nr = utils_write_file(filetypes_readme, text);
841
if (error_nr == 0 && ! g_file_test(filetypes_readme, G_FILE_TEST_EXISTS))
842
{ // check whether write test was successful, otherwise directory is not writable
843
geany_debug("The chosen configuration directory is not writable.");
848
// make subdir for filetype definitions
832
gchar *templates_dir = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
833
GEANY_TEMPLATES_SUBDIR, G_DIR_SEPARATOR_S, NULL);
835
if (! g_file_test(app->configdir, G_FILE_TEST_EXISTS))
837
geany_debug("creating config directory %s", app->configdir);
838
saved_errno = utils_mkdir(app->configdir, FALSE);
841
if (saved_errno == 0 && ! g_file_test(conf_file, G_FILE_TEST_EXISTS))
842
{ /* check whether geany.conf can be written */
843
saved_errno = utils_is_file_writeable(app->configdir);
846
/* make subdir for filetype definitions */
847
if (saved_errno == 0)
849
gchar *filedefs_readme = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
850
GEANY_FILEDEFS_SUBDIR, G_DIR_SEPARATOR_S, "filetypes.README", NULL);
851
852
if (! g_file_test(filedefs_dir, G_FILE_TEST_EXISTS))
854
if (mkdir(filedefs_dir) != 0) error_nr = errno;
856
if (mkdir(filedefs_dir, 0700) != 0) error_nr = errno;
854
saved_errno = utils_mkdir(filedefs_dir, FALSE);
859
if (error_nr == 0 && ! g_file_test(filedefs_readme, G_FILE_TEST_EXISTS))
856
if (saved_errno == 0 && ! g_file_test(filedefs_readme, G_FILE_TEST_EXISTS))
861
858
gchar *text = g_strconcat(
862
"Copy files from ", data_dir, " to this directory to overwrite "
859
"Copy files from ", app->datadir, " to this directory to overwrite "
863
860
"them. To use the defaults, just delete the file in this directory.\nFor more information read "
864
"the documentation (in ", doc_dir, "index.html or visit " GEANY_HOMEPAGE ").", NULL);
861
"the documentation (in ", app->docdir, DIR_SEP "index.html or visit " GEANY_HOMEPAGE ").", NULL);
865
862
utils_write_file(filedefs_readme, text);
870
g_free(filetypes_readme);
865
g_free(filedefs_readme);
868
/* make subdir for template files */
869
if (saved_errno == 0)
871
gchar *templates_readme = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
872
GEANY_TEMPLATES_SUBDIR, G_DIR_SEPARATOR_S, "templates.README", NULL);
874
if (! g_file_test(templates_dir, G_FILE_TEST_EXISTS))
876
saved_errno = utils_mkdir(templates_dir, FALSE);
878
if (saved_errno == 0 && ! g_file_test(templates_readme, G_FILE_TEST_EXISTS))
880
gchar *text = g_strconcat(
881
"There are several template files in this directory. For these templates you can use wildcards.\n\
882
For more information read the documentation (in ", app->docdir, DIR_SEP "index.html or visit " GEANY_HOMEPAGE ").",
884
utils_write_file(templates_readme, text);
887
g_free(templates_readme);
871
890
g_free(filedefs_dir);
872
g_free(filedefs_readme);
891
g_free(templates_dir);
878
/* replaces all occurrences of needle in haystack with replacement
879
* all strings have to NULL-terminated and needle and replacement have to be different,
898
/* Replaces all occurrences of needle in haystack with replacement.
899
* New code should use utils_string_replace_all() instead.
900
* All strings have to be NULL-terminated and needle and replacement have to be different,
880
901
* e.g. needle "%" and replacement "%%" causes an endless loop */
881
902
gchar *utils_str_replace(gchar *haystack, const gchar *needle, const gchar *replacement)
1519
* Converts the given UTF-8 encoded string into locale encoding.
1520
* On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1522
* @param utf8_text UTF-8 encoded text.
1524
* @return The converted string in locale encoding, or a copy of the input string if conversion
1525
* failed. Should be freed with g_free(). If @a utf8_text is @c NULL, @c NULL is returned.
1503
1527
gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1505
gchar *locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1506
if (locale_text == NULL) locale_text = g_strdup(utf8_text);
1530
/* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1531
* which would result in wrongly converted strings */
1532
return g_strdup(utf8_text);
1538
locale_text = g_locale_from_utf8(utf8_text, -1, NULL, NULL, NULL);
1539
if (locale_text == NULL)
1540
locale_text = g_strdup(utf8_text);
1507
1541
return locale_text;
1547
* Converts the given string (in locale encoding) into UTF-8 encoding.
1548
* On Windows platforms, it does nothing and instead it just returns a copy of the input string.
1550
* @param locale_text Text in locale encoding.
1552
* @return The converted string in UTF-8 encoding, or a copy of the input string if conversion
1553
* failed. Should be freed with g_free(). If @a locale_text is @c NULL, @c NULL is returned.
1511
1555
gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1513
gchar *utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1514
if (utf8_text == NULL) utf8_text = g_strdup(locale_text);
1558
/* just do nothing on Windows platforms, this ifdef is just to prevent unwanted conversions
1559
* which would result in wrongly converted strings */
1560
return g_strdup(locale_text);
1566
utf8_text = g_locale_to_utf8(locale_text, -1, NULL, NULL, NULL);
1567
if (utf8_text == NULL)
1568
utf8_text = g_strdup(locale_text);
1515
1569
return utf8_text;
1574
/* Frees all passed pointers if they are *ALL* non-NULL.
1575
* Do not use if any pointers may be NULL.
1576
* The first argument is nothing special, it will also be freed.
1577
* The list must be ended with NULL. */
1578
void utils_free_pointers(gpointer first, ...)
1583
for (va_start(a, first); (sa = va_arg(a, gpointer), sa!=NULL);)
1595
/* Creates a string array deep copy of a series of non-NULL strings.
1596
* The first argument is nothing special.
1597
* The list must be ended with NULL.
1598
* If first is NULL, NULL is returned. */
1599
gchar **utils_strv_new(const gchar *first, ...)
1609
strvlen = 1; /* for first argument */
1611
/* count other arguments */
1612
va_start(args, first);
1613
for (; va_arg(args, gchar*) != NULL; strvlen++);
1616
strv = g_new(gchar*, strvlen + 1); /* +1 for NULL terminator */
1617
strv[0] = g_strdup(first);
1619
va_start(args, first);
1620
for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
1622
strv[i] = g_strdup(str);
1631
#if ! GLIB_CHECK_VERSION(2, 8, 0)
1632
/* Taken from GLib SVN, 2007-03-10 */
1634
* g_mkdir_with_parents:
1635
* @pathname: a pathname in the GLib file name encoding
1636
* @mode: permissions to use for newly created directories
1638
* Create a directory if it doesn't already exist. Create intermediate
1639
* parent directories as needed, too.
1641
* Returns: 0 if the directory already exists, or was successfully
1642
* created. Returns -1 if an error occurred, with errno set.
1647
g_mkdir_with_parents (const gchar *pathname,
1652
if (pathname == NULL || *pathname == '\0')
1658
fn = g_strdup (pathname);
1660
if (g_path_is_absolute (fn))
1661
p = (gchar *) g_path_skip_root (fn);
1667
while (*p && !G_IS_DIR_SEPARATOR (*p))
1675
if (!g_file_test (fn, G_FILE_TEST_EXISTS))
1677
if (g_mkdir (fn, mode) == -1)
1679
int errno_save = errno;
1685
else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
1693
*p++ = G_DIR_SEPARATOR;
1694
while (*p && G_IS_DIR_SEPARATOR (*p))
1708
* Create a directory if it doesn't already exist.
1709
* Create intermediate parent directories as needed, too.
1711
* @param path The path of the directory to create, in locale encoding.
1712
* @param create_parent_dirs Whether to create intermediate parent directories if necessary.
1714
* @return 0 if the directory was successfully created, otherwise the @c errno of the
1715
* failed operation is returned.
1717
gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
1722
if (path == NULL || strlen(path) == 0)
1725
result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
1733
* Gets a sorted list of files from the specified directory.
1734
* Locale encoding is expected for path and used for the file list. The list and the data
1735
* in the list should be freed after use.
1737
* @param path The path of the directory to scan, in locale encoding.
1738
* @param length The location to store the number of non-@a NULL data items in the list,
1740
* @param error The is the location for storing a possible error, or @a NULL.
1742
* @return A newly allocated list or @a NULL if no files found. The list and its data should be
1743
* freed when no longer needed.
1745
GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
1747
GSList *list = NULL;
1755
g_return_val_if_fail(path != NULL, NULL);
1757
dir = g_dir_open(path, 0, error);
1763
const gchar *filename = g_dir_read_name(dir);
1764
if (filename == NULL) break;
1766
list = g_slist_insert_sorted(list, g_strdup(filename), (GCompareFunc) strcmp);
1777
/* returns TRUE if any letter in str is a capital, FALSE otherwise. Should be Unicode safe. */
1778
gboolean utils_str_has_upper(const gchar *str)
1782
if (str == NULL || *str == '\0' || ! g_utf8_validate(str, -1, NULL))
1785
while (*str != '\0')
1787
c = g_utf8_get_char(str);
1788
/* check only letters and stop once the first non-capital was found */
1789
if (g_unichar_isalpha(c) && g_unichar_isupper(c))
1791
/* FIXME don't write a const string */
1792
str = g_utf8_next_char(str);
1799
* Replaces all occurrences of @c needle in @c haystack with @c replace.
1800
* Currently this is not safe if @c replace matches @c needle, so @c needle and @c replace
1801
* must not be equal.
1803
* @param haystack The input string to operate on. This string is modified in place.
1804
* @param needle The string which should be replaced.
1805
* @param replace The replacement for @c needle.
1807
* @return @a TRUE if @c needle was found, else @a FALSE.
1809
gboolean utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
1814
g_return_val_if_fail(NZV(needle), FALSE);
1815
g_return_val_if_fail(! NZV(replace) || strstr(replace, needle) == NULL, FALSE);
1819
c = strstr(haystack->str, needle);
1824
pos = c - haystack->str;
1825
g_string_erase(haystack, pos, strlen(needle));
1827
g_string_insert(haystack, pos, replace);
1834
/* Get project or default startup directory (if set), or NULL. */
1835
const gchar *utils_get_default_dir_utf8(void)
1837
if (app->project && NZV(app->project->base_path))
1839
return app->project->base_path;
1842
if (NZV(prefs.default_open_path))
1844
return prefs.default_open_path;
1850
static gboolean check_error(GError **error)
1852
if (error != NULL && *error != NULL)
1854
/* imitate the GLib warning */
1856
"GError set over the top of a previous GError or uninitialized memory.\n"
1857
"This indicates a bug in someone's code. You must ensure an error is NULL "
1858
"before it's set.");
1859
/* after returning the code may segfault, but we don't care because we should
1860
* make sure *error is NULL */
1868
* This is a wrapper function for g_spawn_sync() and internally calls this function on Unix-like
1869
* systems. On Win32 platforms, it uses the Windows API.
1871
* @param dir The child's current working directory, or @a NULL to inherit parent's.
1872
* @param argv The child's argument vector.
1873
* @param env The child's environment, or @a NULL to inherit parent's.
1874
* @param flags Flags from GSpawnFlags.
1875
* @param child_setup A function to run in the child just before exec().
1876
* @param user_data The user data for child_setup.
1877
* @param std_out The return location for child output.
1878
* @param std_err The return location for child error messages.
1879
* @param exit_status The child exit status, as returned by waitpid().
1880
* @param error The return location for error or @a NULL.
1882
* @return @a TRUE on success, @a FALSE if an error was set.
1884
gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1885
GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out,
1886
gchar **std_err, gint *exit_status, GError **error)
1890
if (! check_error(error))
1895
*error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1906
result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
1908
result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
1916
* This is a wrapper function for g_spawn_async() and internally calls this function on Unix-like
1917
* systems. On Win32 platforms, it uses the Windows API.
1919
* @param dir The child's current working directory, or @a NULL to inherit parent's.
1920
* @param argv The child's argument vector.
1921
* @param env The child's environment, or @a NULL to inherit parent's.
1922
* @param flags Flags from GSpawnFlags.
1923
* @param child_setup A function to run in the child just before exec().
1924
* @param user_data The user data for child_setup.
1925
* @param child_pid The return location for child process ID, or NULL.
1926
* @param error The return location for error or @a NULL.
1928
* @return @a TRUE on success, @a FALSE if an error was set.
1930
gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1931
GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
1936
if (! check_error(error))
1941
*error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
1946
result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
1948
result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);