~ubuntu-branches/ubuntu/raring/geany/raring-proposed

« back to all changes in this revision

Viewing changes to src/utils.c

  • Committer: Bazaar Package Importer
  • Author(s): Damián Viano
  • Date: 2008-05-02 11:37:45 UTC
  • mto: (3.1.1 lenny) (1.3.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: james.westby@ubuntu.com-20080502113745-7q62rqhl2ku02ptu
Import upstream version 0.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 *      utils.c - this file is part of Geany, a fast and lightweight IDE
3
3
 *
4
 
 *      Copyright 2006 Enrico Troeger <enrico.troeger@uvena.de>
 
4
 *      Copyright 2005-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
 
5
 *      Copyright 2006-2008 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
5
6
 *
6
7
 *      This program is free software; you can redistribute it and/or modify
7
8
 *      it under the terms of the GNU General Public License as published by
17
18
 *      along with this program; if not, write to the Free Software
18
19
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20
 *
20
 
 * $Id: utils.c 836 2006-09-26 16:54:20Z eht16 $
 
21
 * $Id: utils.c 2468 2008-04-10 18:18:34Z eht16 $
21
22
 */
22
23
 
 
24
/*
 
25
 * General utility functions, non-GTK related.
 
26
 */
23
27
 
24
28
#include "SciLexer.h"
25
29
#include "geany.h"
30
34
#include <unistd.h>
31
35
#include <string.h>
32
36
#include <errno.h>
 
37
#include <stdarg.h>
33
38
 
34
39
#ifdef HAVE_SYS_STAT_H
35
40
# include <sys/stat.h>
38
43
# include <sys/types.h>
39
44
#endif
40
45
 
 
46
#include <glib/gstdio.h>
 
47
 
 
48
#include "prefs.h"
41
49
#include "support.h"
42
50
#include "document.h"
 
51
#include "filetypes.h"
43
52
#include "sciwrappers.h"
44
53
#include "dialogs.h"
45
54
#include "win32.h"
46
 
#include "main.h"
47
 
#include "encodings.h"
 
55
#include "project.h"
48
56
 
49
57
#include "utils.h"
50
58
 
56
64
#else
57
65
        const gchar *argv[3];
58
66
 
59
 
        argv[0] = app->tools_browser_cmd;
 
67
        argv[0] = prefs.tools_browser_cmd;
60
68
        argv[1] = uri;
61
69
        argv[2] = NULL;
62
70
 
86
94
 
87
95
 
88
96
/* taken from anjuta, to determine the EOL mode of the file */
89
 
gint utils_get_line_endings(gchar* buffer, glong size)
 
97
gint utils_get_line_endings(const gchar* buffer, glong size)
90
98
{
91
99
        gint i;
92
100
        guint cr, lf, crlf, max_mode;
96
104
 
97
105
        for ( i = 0; i < size ; i++ )
98
106
        {
99
 
                if ( buffer[i] == 0x0a ){
100
 
                        // LF
 
107
                if ( buffer[i] == 0x0a )
 
108
                {
 
109
                        /* LF */
101
110
                        lf++;
102
111
                }
103
112
                else if ( buffer[i] == 0x0d )
104
113
                {
105
114
                        if (i >= (size-1))
106
115
                        {
107
 
                                // Last char
108
 
                                // CR
 
116
                                /* Last char, CR */
109
117
                                cr++;
110
 
                        } else {
 
118
                        }
 
119
                        else
 
120
                        {
111
121
                                if (buffer[i+1] != 0x0a)
112
122
                                {
113
 
                                        // CR
 
123
                                        /* CR */
114
124
                                        cr++;
115
125
                                }
116
126
                                else
117
127
                                {
118
 
                                        // CRLF
 
128
                                        /* CRLF */
119
129
                                        crlf++;
120
130
                                }
121
131
                                i++;
126
136
        /* Vote for the maximum */
127
137
        mode = SC_EOL_LF;
128
138
        max_mode = lf;
129
 
        if (crlf > max_mode) {
 
139
        if (crlf > max_mode)
 
140
        {
130
141
                mode = SC_EOL_CRLF;
131
142
                max_mode = crlf;
132
143
        }
133
 
        if (cr > max_mode) {
 
144
        if (cr > max_mode)
 
145
        {
134
146
                mode = SC_EOL_CR;
135
147
                max_mode = cr;
136
148
        }
137
 
        //geany_debug("EOL chars: LF = %d, CR = %d, CRLF = %d", lf, cr, crlf);
138
 
 
139
149
 
140
150
        return mode;
141
151
}
142
152
 
143
153
 
144
 
gboolean utils_isbrace(gchar c)
 
154
gboolean utils_isbrace(gchar c, gboolean include_angles)
145
155
{
146
 
        // match < and > only if desired, because I don't like it, but some people do
147
 
        if (app->brace_match_ltgt)
148
 
        {
149
 
                switch (c)
150
 
                {
151
 
                        case '<':
152
 
                        case '>': return TRUE;
153
 
                }
154
 
        }
155
 
 
156
156
        switch (c)
157
157
        {
 
158
                case '<':
 
159
                case '>':
 
160
                return include_angles;
 
161
 
158
162
                case '(':
159
163
                case ')':
160
164
                case '{':
163
167
                case ']': return TRUE;
164
168
                default:  return FALSE;
165
169
        }
166
 
 
167
 
        return FALSE;
168
170
}
169
171
 
170
172
 
171
 
gboolean utils_is_opening_brace(gchar c)
 
173
gboolean utils_is_opening_brace(gchar c, gboolean include_angles)
172
174
{
173
 
        // match < only if desired, because I don't like it, but some people do
174
 
        if (app->brace_match_ltgt)
175
 
        {
176
 
                switch (c)
177
 
                {
178
 
                        case '<': return TRUE;
179
 
                }
180
 
        }
181
 
 
182
175
        switch (c)
183
176
        {
 
177
                case '<':
 
178
                return include_angles;
 
179
 
184
180
                case '(':
185
181
                case '{':
186
182
                case '[':  return TRUE;
187
183
                default:  return FALSE;
188
184
        }
189
 
 
190
 
        return FALSE;
191
 
}
192
 
 
193
 
 
194
 
const GList *utils_get_tag_list(gint idx, guint tag_types)
195
 
{
196
 
        static GList *tag_names = NULL;
197
 
 
198
 
        if (idx >= 0 && doc_list[idx].is_valid && doc_list[idx].tm_file &&
199
 
                doc_list[idx].tm_file->tags_array)
200
 
        {
201
 
                TMTag *tag;
202
 
                guint i;
203
 
                GeanySymbol *symbol;
204
 
                gboolean doc_is_utf8 = FALSE;
205
 
                gchar *utf8_name;
206
 
 
207
 
                if (tag_names)
208
 
                {
209
 
                        GList *tmp;
210
 
                        for (tmp = tag_names; tmp; tmp = g_list_next(tmp))
211
 
                        {
212
 
                                g_free(((GeanySymbol*)tmp->data)->str);
213
 
                                g_free(tmp->data);
214
 
                        }
215
 
                        g_list_free(tag_names);
216
 
                        tag_names = NULL;
217
 
                }
218
 
 
219
 
                // do this comparison only once
220
 
                if (utils_strcmp(doc_list[idx].encoding, "UTF-8")) doc_is_utf8 = TRUE;
221
 
 
222
 
                for (i = 0; i < (doc_list[idx].tm_file)->tags_array->len; ++i)
223
 
                {
224
 
                        tag = TM_TAG((doc_list[idx].tm_file)->tags_array->pdata[i]);
225
 
                        if (tag == NULL)
226
 
                                return NULL;
227
 
 
228
 
                        if (tag->type & tag_types)
229
 
                        {
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]))
234
 
                                {
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);
240
 
                                }
241
 
                                else
242
 
                                {
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);
248
 
                                }
249
 
                                if (! doc_is_utf8) g_free(utf8_name);
250
 
                        }
251
 
                }
252
 
                tag_names = g_list_sort(tag_names, (GCompareFunc) utils_compare_symbol);
253
 
                return tag_names;
254
 
        }
255
 
        else
256
 
                return NULL;
257
 
}
258
 
 
259
 
/* returns the line of the given tag */
260
 
gint utils_get_local_tag(gint idx, const gchar *qual_name)
261
 
{
262
 
        guint line;
263
 
        gchar *spos;
264
 
 
265
 
        g_return_val_if_fail((doc_list[idx].sci && qual_name), -1);
266
 
 
267
 
        spos = strchr(qual_name, '[');
268
 
        if (spos && strchr(spos+1, ']'))
269
 
        {
270
 
                line = atol(spos + 1);
271
 
                if (line > 0)
272
 
                {
273
 
                        return line;
274
 
                }
275
 
        }
276
 
        return -1;
277
 
}
278
 
 
279
 
 
280
 
gboolean utils_goto_file_line(const gchar *file, gboolean is_tm_filename, gint line)
 
185
}
 
186
 
 
187
 
 
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)
281
190
{
282
191
        gint file_idx = document_find_by_filename(file, is_tm_filename);
283
192
 
284
193
        if (file_idx < 0) return FALSE;
285
194
 
286
 
        return utils_goto_line(file_idx, line);
 
195
        return utils_goto_pos(file_idx, pos);
287
196
}
288
197
 
289
198
 
 
199
/* line is counted with 1 as the first line, not 0 */
290
200
gboolean utils_goto_line(gint idx, gint line)
291
201
{
292
202
        gint page_num;
293
203
 
294
 
        line--; // the User counts lines from 1, we begin at 0 so bring the User line to our one
295
 
 
296
 
        if (idx == -1 || ! doc_list[idx].is_valid || line < 0)
297
 
                return FALSE;
298
 
 
299
 
        // mark the tag
300
 
        sci_marker_delete_all(doc_list[idx].sci, 0);
301
 
        sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
302
 
 
303
 
        sci_goto_line_scroll(doc_list[idx].sci, line, 0.25);
304
 
 
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);
308
 
 
309
 
        return TRUE;
310
 
}
311
 
 
312
 
 
 
204
        line--; /* the user counts lines from 1, we begin at 0 so bring the user line to our one */
 
205
 
 
206
        if (! DOC_IDX_VALID(idx) || line < 0)
 
207
                return FALSE;
 
208
 
 
209
        /* mark the tag */
 
210
        sci_marker_delete_all(doc_list[idx].sci, 0);
 
211
        sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
 
212
 
 
213
        sci_goto_line(doc_list[idx].sci, line, TRUE);
 
214
        doc_list[idx].scroll_percent = 0.25F;
 
215
 
 
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);
 
219
 
 
220
        return TRUE;
 
221
}
 
222
 
 
223
 
 
224
gboolean utils_goto_pos(gint idx, gint pos)
 
225
{
 
226
        gint page_num;
 
227
        gint line;
 
228
 
 
229
        if (! DOC_IDX_VALID(idx) || pos < 0)
 
230
                return FALSE;
 
231
 
 
232
        line = sci_get_line_from_position(doc_list[idx].sci, pos);
 
233
 
 
234
        /* mark the tag */
 
235
        sci_marker_delete_all(doc_list[idx].sci, 0);
 
236
        sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 0);
 
237
 
 
238
        sci_goto_pos(doc_list[idx].sci, pos, TRUE);
 
239
        doc_list[idx].scroll_percent = 0.25F;
 
240
 
 
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);
 
244
 
 
245
        return TRUE;
 
246
}
 
247
 
 
248
 
 
249
/**
 
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.
 
253
 *
 
254
 *  @param filename The filename of the file to write, in locale encoding.
 
255
 *  @param text The text to write into the file.
 
256
 *
 
257
 *  @return 0 if the directory was successfully created, otherwise the @c errno of the
 
258
 *          failed operation is returned.
 
259
 **/
313
260
gint utils_write_file(const gchar *filename, const gchar *text)
314
261
{
315
262
        FILE *fp;
322
269
 
323
270
        len = strlen(text);
324
271
 
325
 
        fp = fopen(filename, "w");
 
272
        fp = g_fopen(filename, "w");
326
273
        if (fp != NULL)
327
274
        {
328
275
                bytes_written = fwrite(text, sizeof (gchar), len, fp);
337
284
        }
338
285
        else
339
286
        {
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));
341
289
                return errno;
342
290
        }
343
291
        return 0;
344
292
}
345
293
 
346
294
 
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,
350
 
                                                                                  gboolean fast)
351
 
{
352
 
        gchar *utf8_content = NULL;
353
 
        GError *conv_error = NULL;
354
 
        gchar* converted_contents = NULL;
355
 
        gsize bytes_written;
356
 
 
357
 
        g_return_val_if_fail(buffer != NULL, NULL);
358
 
        g_return_val_if_fail(charset != NULL, NULL);
359
 
 
360
 
        converted_contents = g_convert(buffer, size, "UTF-8", charset, NULL,
361
 
                                                                   &bytes_written, &conv_error);
362
 
 
363
 
        if (fast)
364
 
        {
365
 
                utf8_content = converted_contents;
366
 
                if (conv_error != NULL) g_error_free(conv_error);
367
 
        }
368
 
        else if (conv_error != NULL || ! g_utf8_validate(converted_contents, bytes_written, NULL))
369
 
        {
370
 
                if (conv_error != NULL)
371
 
                {
372
 
                        geany_debug("Couldn't convert from %s to UTF-8 (%s).", charset, conv_error->message);
373
 
                        g_error_free(conv_error);
374
 
                        conv_error = NULL;
375
 
                }
376
 
                else
377
 
                        geany_debug("Couldn't convert from %s to UTF-8.", charset);
378
 
 
379
 
                utf8_content = NULL;
380
 
                if (converted_contents != NULL) g_free(converted_contents);
381
 
        }
382
 
        else
383
 
        {
384
 
                geany_debug("Converted from %s to UTF-8.", charset);
385
 
                utf8_content = converted_contents;
386
 
        }
387
 
 
388
 
        return utf8_content;
389
 
}
390
 
 
391
 
 
392
 
gchar *utils_convert_to_utf8(const gchar *buffer, gsize size, gchar **used_encoding)
393
 
{
394
 
        gchar *locale_charset = NULL;
395
 
        gchar *utf8_content;
396
 
        gchar *charset;
397
 
        gboolean check_current = FALSE;
398
 
        guint i;
399
 
 
400
 
        // current locale is not UTF-8, we have to check this charset
401
 
        check_current = ! g_get_charset((const gchar**)&locale_charset);
402
 
 
403
 
        for (i = 0; i < GEANY_ENCODINGS_MAX; i++)
404
 
        {
405
 
                if (check_current)
406
 
                {
407
 
                        check_current = FALSE;
408
 
                        charset = locale_charset;
409
 
                        i = -1;
410
 
                }
411
 
                else
412
 
                        charset = encodings[i].charset;
413
 
 
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);
416
 
 
417
 
                if (utf8_content != NULL)
418
 
                {
419
 
                        if (used_encoding != NULL)
420
 
                        {
421
 
                                if (*used_encoding != NULL)
422
 
                                {
423
 
                                        g_free(*used_encoding);
424
 
                                        geany_debug("%s:%d", __FILE__, __LINE__);
425
 
                                }
426
 
                                *used_encoding = g_strdup(charset);
427
 
                        }
428
 
                        return utf8_content;
429
 
                }
430
 
        }
431
 
 
432
 
        return NULL;
433
 
}
434
 
 
435
 
 
436
 
/**
 
295
/*
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
440
299
 */
441
300
gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag)
442
301
{
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;
447
306
 
448
 
        if (size < 3) {
449
 
                // Smallest tag is "<p>" which is 3 characters
 
307
        if (size < 3)
 
308
        {
 
309
                /* Smallest tag is "<p>" which is 3 characters */
450
310
                return result;
451
311
        }
452
312
        begin = &sel[0];
455
315
        else
456
316
                cur = &sel[size - 1];
457
317
 
458
 
        cur--; // Skip past the >
 
318
        cur--; /* Skip past the > */
459
319
        while (cur > begin)
460
320
        {
461
321
                if (*cur == '<') break;
474
334
        }
475
335
 
476
336
        result[i] = '\0';
477
 
        // Return the tag name or ""
 
337
        /* Return the tag name or "" */
478
338
        return result;
479
339
}
480
340
 
481
341
 
482
 
gboolean utils_check_disk_status(gint idx)
 
342
static gboolean reload_idx(gpointer data)
 
343
{
 
344
        gint idx = GPOINTER_TO_INT(data);
 
345
 
 
346
        /* check idx is still valid now we're idle, in case it was closed */
 
347
        if (DOC_IDX_VALID(idx))
 
348
        {
 
349
                document_reload_file(idx, NULL);
 
350
        }
 
351
        return FALSE;
 
352
}
 
353
 
 
354
 
 
355
static gboolean check_reload(gint idx)
 
356
{
 
357
        gchar *base_name = g_path_get_basename(doc_list[idx].file_name);
 
358
        gboolean want_reload;
 
359
 
 
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);
 
364
        if (want_reload)
 
365
        {
 
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
 
368
                 * properly */
 
369
                g_idle_add(reload_idx, GINT_TO_POINTER(idx));
 
370
        }
 
371
        g_free(base_name);
 
372
        return want_reload;
 
373
}
 
374
 
 
375
 
 
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)
483
380
{
484
381
        struct stat st;
485
382
        time_t t;
486
383
        gchar *locale_filename;
 
384
        gboolean ret = FALSE;
487
385
 
488
386
        if (idx == -1 || doc_list[idx].file_name == NULL) return FALSE;
489
387
 
490
388
        t = time(NULL);
491
389
 
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;
493
391
 
494
392
        locale_filename = utils_get_locale_from_utf8(doc_list[idx].file_name);
495
 
        if (stat(locale_filename, &st) != 0) return FALSE;
496
 
 
497
 
        if (doc_list[idx].mtime > t || st.st_mtime > t)
 
393
        if (g_stat(locale_filename, &st) != 0)
 
394
        {
 
395
                /* TODO: warn user file on disk is missing */
 
396
        }
 
397
        else if (doc_list[idx].mtime > t || st.st_mtime > t)
498
398
        {
499
399
                geany_debug("Strange: Something is wrong with the time stamps.");
500
 
                return FALSE;
501
400
        }
502
 
 
503
 
        if (doc_list[idx].mtime < st.st_mtime)
 
401
        else if (doc_list[idx].mtime < st.st_mtime)
504
402
        {
505
 
                gchar *basename = g_path_get_basename(doc_list[idx].file_name);
506
 
 
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))
511
404
                {
512
 
                        document_reload_file(idx, NULL);
513
 
                        doc_list[idx].last_check = t;
514
 
                }
515
 
                else
 
405
                        /* Disable checking until after reload, so ignore this change for now */
516
406
                        doc_list[idx].mtime = st.st_mtime;
517
 
 
518
 
                g_free(basename);
519
 
                return TRUE; //file has changed
520
 
        }
521
 
        return FALSE;
522
 
}
523
 
 
524
 
 
 
407
                }
 
408
                else
 
409
                        doc_list[idx].mtime = st.st_mtime;      /* Ignore this change on disk completely */
 
410
 
 
411
                ret = TRUE; /* file has changed */
 
412
        }
 
413
        g_free(locale_filename);
 
414
        return ret;
 
415
}
 
416
 
 
417
 
 
418
/* This could perhaps be improved to check for #if, class etc. */
 
419
static gint get_function_fold_number(gint idx)
 
420
{
 
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;
 
424
        else
 
425
                return SC_FOLDLEVELBASE;
 
426
}
 
427
 
 
428
 
 
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)
 
431
{
 
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;
 
436
        gboolean ret;
 
437
 
 
438
        /* check if the cached line and file index have changed since last time: */
 
439
        if (cur_idx < 0 || cur_idx != old_idx)
 
440
                ret = TRUE;
 
441
        else
 
442
        if (cur_line == old_line)
 
443
                ret = FALSE;
 
444
        else
 
445
        {
 
446
                /* if the line has only changed by 1 */
 
447
                if (abs(cur_line - old_line) == 1)
 
448
                {
 
449
                        const gint fn_fold =
 
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. */
 
453
                        gboolean same =
 
454
                                fold_num == old_fold_num ||
 
455
                                (old_fold_num > fn_fold && fold_num > fn_fold);
 
456
 
 
457
                        ret = ! same;
 
458
                }
 
459
                else ret = TRUE;
 
460
        }
 
461
 
 
462
        /* record current line and file index for next time */
 
463
        old_line = cur_line;
 
464
        old_idx = cur_idx;
 
465
        old_fold_num = fold_num;
 
466
        return ret;
 
467
}
 
468
 
 
469
 
 
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)
 
474
{
 
475
        gint start, end, max_pos;
 
476
        gchar *cur_tag;
 
477
        gint fn_style;
 
478
 
 
479
        switch (sci_get_lexer(sci))
 
480
        {
 
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 */
 
484
        }
 
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++;
 
489
 
 
490
        end = start;
 
491
        while (sci_get_style_at(sci, end) == fn_style
 
492
                && end < max_pos) end++;
 
493
 
 
494
        if (start == end) return NULL;
 
495
        cur_tag = g_malloc(end - start + 1);
 
496
        sci_get_text_range(sci, start, end, cur_tag);
 
497
        return cur_tag;
 
498
}
 
499
 
 
500
 
 
501
/* Parse the function name */
 
502
static gchar *parse_cpp_function_at_line(ScintillaObject *sci, gint tag_line)
 
503
{
 
504
        gint start, end, first_pos, max_pos;
 
505
        gint tmp;
 
506
        gchar c;
 
507
        gchar *cur_tag;
 
508
 
 
509
        first_pos = end = sci_get_position_from_line(sci, tag_line);
 
510
        max_pos = sci_get_position_from_line(sci, tag_line + 1);
 
511
        tmp = 0;
 
512
        /* goto the begin of function body */
 
513
        while (end < max_pos &&
 
514
                (tmp = sci_get_char_at(sci, end)) != '{' &&
 
515
                tmp != 0) end++;
 
516
        if (tmp == 0) end --;
 
517
 
 
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)) != '(' &&
 
521
                tmp != 0) end--;
 
522
        end--;
 
523
        if (end < 0) end = 0;
 
524
 
 
525
        /* skip whitespaces between identifier and ( */
 
526
        while (end > 0 && isspace(sci_get_char_at(sci, end))) end--;
 
527
 
 
528
        start = end;
 
529
        c = 0;
 
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)) == '~'
 
534
                 ||  c == ':'))
 
535
                start--;
 
536
        if (start != 0 && start < end) start++; /* correct for last non-matching char */
 
537
 
 
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);
 
541
        return cur_tag;
 
542
}
 
543
 
 
544
 
 
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)
526
550
{
527
551
        static gint tag_line = -1;
528
 
        gint line;
529
 
        static gint old_line = -1;
530
 
        static gint old_idx = -1;
531
552
        static gchar *cur_tag = NULL;
 
553
        gint line;
532
554
        gint fold_level;
533
 
        gint start, end, last_pos;
534
 
        gint tmp;
535
 
        const GList *tags;
536
 
 
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)
540
 
        {
541
 
                // we can assume same current function as before
542
 
                *tagname = cur_tag;
543
 
                return tag_line;
544
 
        }
545
 
        g_free(cur_tag); // free the old tag, it will be replaced.
546
 
        //record current line and file index for next time
547
 
        old_line = line;
548
 
        old_idx = idx;
549
 
 
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))
553
 
        {
554
 
                tag_line = ((GeanySymbol*)tags->data)->line;
555
 
                if (line + 1 == tag_line)
 
555
        TMWorkObject *tm_file;
 
556
 
 
557
        if (! DOC_IDX_VALID(idx))       /* reset current function */
 
558
        {
 
559
                current_function_changed(-1, -1, -1);
 
560
                g_free(cur_tag);
 
561
                cur_tag = g_strdup(_("unknown"));
 
562
                if (tagname != NULL)
 
563
                        *tagname = cur_tag;
 
564
                tag_line = -1;
 
565
                return tag_line;
 
566
        }
 
567
 
 
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))
 
572
        {
 
573
                /* we can assume same current function as before */
 
574
                *tagname = cur_tag;
 
575
                return tag_line;
 
576
        }
 
577
        g_free(cur_tag); /* free the old tag, it will be replaced. */
 
578
 
 
579
        /* if line is at base fold level, we're not in a function */
 
580
        if ((fold_level & SC_FOLDLEVELNUMBERMASK) == SC_FOLDLEVELBASE)
 
581
        {
 
582
                cur_tag = g_strdup(_("unknown"));
 
583
                *tagname = cur_tag;
 
584
                tag_line = -1;
 
585
                return tag_line;
 
586
        }
 
587
        tm_file = doc_list[idx].tm_file;
 
588
 
 
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)
 
591
        {
 
592
                const TMTag *tag = (const TMTag*) tm_get_current_function(tm_file->tags_array, line);
 
593
 
 
594
                if (tag != NULL)
556
595
                {
557
 
                        cur_tag = g_strdup(strtok(((GeanySymbol*)tags->data)->str, " "));
 
596
                        gchar *tmp;
 
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;
559
601
                        return tag_line;
560
602
                }
561
603
        }
562
604
 
 
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)
566
609
        {
567
 
                fold_level = sci_get_fold_level(doc_list[idx].sci, line);
568
 
 
569
 
                if ((fold_level & 0xFF) != 0)
570
 
                {
571
 
                        tag_line = line;
572
 
                        while((fold_level & SC_FOLDLEVELNUMBERMASK) != SC_FOLDLEVELBASE && tag_line >= 0)
 
610
                const gint fn_fold = get_function_fold_number(idx);
 
611
 
 
612
                tag_line = line;
 
613
                do      /* find the top level fold point */
 
614
                {
 
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);
 
619
 
 
620
                if (tag_line >= 0)
 
621
                {
 
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);
 
624
                        else
 
625
                                cur_tag = parse_function_at_line(doc_list[idx].sci, tag_line);
 
626
 
 
627
                        if (cur_tag != NULL)
573
628
                        {
574
 
                                fold_level = sci_get_fold_level(doc_list[idx].sci, --tag_line);
 
629
                                *tagname = cur_tag;
 
630
                                return tag_line;
575
631
                        }
576
 
 
577
 
                        start = sci_get_position_from_line(doc_list[idx].sci, tag_line - 2);
578
 
                        last_pos = sci_get_length(doc_list[idx].sci);
579
 
                        tmp = 0;
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++;
583
 
                        end = 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++;
598
 
 
599
 
                        cur_tag = g_malloc(end - start + 1);
600
 
                        sci_get_text_range(doc_list[idx].sci, start, end, cur_tag);
601
 
                        *tagname = cur_tag;
602
 
 
603
 
                        return tag_line;
604
632
                }
605
633
        }
606
634
 
625
653
 
626
654
 
627
655
/* returns the end-of-line character(s) of the specified editor */
628
 
gchar *utils_get_eol_char(gint idx)
 
656
const gchar *utils_get_eol_char(gint idx)
629
657
{
630
658
        if (idx == -1) return '\0';
631
659
 
639
667
}
640
668
 
641
669
 
642
 
/* mainly debug function, to get TRUE or FALSE as ascii from a gboolean */
643
 
gchar *utils_btoa(gboolean sbool)
644
 
{
645
 
        return (sbool) ? "TRUE" : "FALSE";
646
 
}
647
 
 
648
 
 
649
670
gboolean utils_atob(const gchar *str)
650
671
{
651
672
        if (str == NULL) return FALSE;
654
675
}
655
676
 
656
677
 
657
 
/* (stolen from bluefish, thanks)
658
 
 * Returns number of characters, lines and words in the supplied gchar*.
659
 
 * Handles UTF-8 correctly. Input must be properly encoded UTF-8.
660
 
 * Words are defined as any characters grouped, separated with spaces.
661
 
 */
662
 
void utils_wordcount(gchar *text, guint *chars, guint *lines, guint *words)
663
 
{
664
 
        guint in_word = 0;
665
 
        gunichar utext;
666
 
 
667
 
        if (!text) return; // politely refuse to operate on NULL
668
 
 
669
 
        *chars = *words = *lines = 0;
670
 
        while (*text != '\0')
671
 
        {
672
 
                (*chars)++;
673
 
 
674
 
                switch (*text)
675
 
                {
676
 
                        case '\n':
677
 
                                (*lines)++;
678
 
                        case '\r':
679
 
                        case '\f':
680
 
                        case '\t':
681
 
                        case ' ':
682
 
                        case '\v':
683
 
                                mb_word_separator:
684
 
                                if (in_word)
685
 
                                {
686
 
                                        in_word = 0;
687
 
                                        (*words)++;
688
 
                                }
689
 
                                break;
690
 
                        default:
691
 
                                utext = g_utf8_get_char_validated(text, 2); // This might be an utf-8 char
692
 
                                if (g_unichar_isspace(utext)) // Unicode encoded space?
693
 
                                        goto mb_word_separator;
694
 
                                if (g_unichar_isgraph(utext)) // Is this something printable?
695
 
                                        in_word = 1;
696
 
                                break;
697
 
                }
698
 
                text = g_utf8_next_char(text); // Even if the current char is 2 bytes, this will iterate correctly.
699
 
        }
700
 
 
701
 
        // Capture last word, if there's no whitespace at the end of the file.
702
 
        if (in_word) (*words)++;
703
 
        // We start counting line numbers from 1
704
 
        if (*chars > 0) (*lines)++;
705
 
}
706
 
 
707
 
 
708
 
/* currently unused */
709
678
gboolean utils_is_absolute_path(const gchar *path)
710
679
{
711
680
        if (! path || *path == '\0')
724
693
 
725
694
gdouble utils_scale_round (gdouble val, gdouble factor)
726
695
{
727
 
        //val = floor(val * factor + 0.5);
 
696
        /*val = floor(val * factor + 0.5);*/
728
697
        val = floor(val);
729
698
        val = MAX(val, 0);
730
699
        val = MIN(val, factor);
733
702
}
734
703
 
735
704
 
736
 
/* (taken from libexo from os-cillation)
737
 
 * NULL-safe string comparison. Returns TRUE if both a and b are
738
 
 * NULL or if a and b refer to valid strings which are equal.
739
 
 */
740
 
gboolean utils_strcmp(const gchar *a, const gchar *b)
 
705
/**
 
706
 *  @a NULL-safe string comparison. Returns @a TRUE if both @c a and @c b are @a NULL
 
707
 *  or if @c a and @c b refer to valid strings which are equal.
 
708
 *
 
709
 *  @param a Pointer to first string or @a NULL.
 
710
 *  @param b Pointer to first string or @a NULL.
 
711
 *
 
712
 *  @return @a TRUE if @c a equals @c b, else @a FALSE.
 
713
 **/
 
714
gboolean utils_str_equal(const gchar *a, const gchar *b)
741
715
{
 
716
        /* (taken from libexo from os-cillation) */
742
717
        if (a == NULL && b == NULL) return TRUE;
743
718
        else if (a == NULL || b == NULL) return FALSE;
744
719
 
750
725
}
751
726
 
752
727
 
753
 
/* removes the extension from filename and return the result in
754
 
 * a newly allocated string */
 
728
/**
 
729
 *  Remove the extension from @c filename and return the result in a newly allocated string.
 
730
 *
 
731
 *  @param filename The filename to operate on.
 
732
 *
 
733
 *  @return A newly-allocated string, should be freed when no longer needed.
 
734
 **/
755
735
gchar *utils_remove_ext_from_filename(const gchar *filename)
756
736
{
757
 
        gchar *result = g_malloc0(strlen(filename));
758
737
        gchar *last_dot = strrchr(filename, '.');
759
 
        gint i = 0;
 
738
        gchar *result;
 
739
        gint i;
760
740
 
761
741
        if (filename == NULL) return NULL;
762
742
 
763
743
        if (! last_dot) return g_strdup(filename);
764
744
 
 
745
        /* assumes extension is small, so extra bytes don't matter */
 
746
        result = g_malloc(strlen(filename));
 
747
        i = 0;
765
748
        while ((filename + i) != last_dot)
766
749
        {
767
750
                result[i] = filename[i];
768
751
                i++;
769
752
        }
770
 
 
 
753
        result[i] = 0;
771
754
        return result;
772
755
}
773
756
 
789
772
}
790
773
 
791
774
 
792
 
gchar *utils_get_hostname()
 
775
gchar *utils_get_hostname(void)
793
776
{
794
 
#ifndef HAVE_GETHOSTNAME
 
777
#ifdef G_OS_WIN32
 
778
        return win32_get_hostname();
 
779
#elif defined(HAVE_GETHOSTNAME)
 
780
        gchar hostname[100];
 
781
        if (gethostname(hostname, sizeof(hostname)) == 0)
 
782
                return g_strdup(hostname);
 
783
#endif
795
784
        return g_strdup("localhost");
796
 
#else
797
 
        gchar *host = g_malloc(25);
798
 
        if (gethostname(host, 24) == 0)
799
 
        {
800
 
                return host;
801
 
        }
802
 
        else
803
 
        {
804
 
                g_free(host);
805
 
                return g_strdup("localhost");
806
 
        }
807
 
#endif
808
 
}
809
 
 
810
 
 
811
 
gint utils_make_settings_dir(const gchar *dir, const gchar *data_dir, const gchar *doc_dir)
812
 
{
813
 
        gint error_nr = 0;
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,
 
785
}
 
786
 
 
787
 
 
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)
 
791
{
 
792
        gchar *file;
 
793
        gint ret;
 
794
 
 
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);
 
799
        else
 
800
                file = g_strdup(locale_filename);
 
801
 
 
802
#ifdef G_OS_WIN32
 
803
        /* use _waccess on Windows, access() doesn't accept special characters */
 
804
        ret = win32_check_write_permission(file);
 
805
#else
 
806
 
 
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)
 
810
                ret = errno;
 
811
        else
 
812
                ret = 0;
 
813
#endif
 
814
        g_free(file);
 
815
        return ret;
 
816
}
 
817
 
 
818
 
 
819
#ifdef G_OS_WIN32
 
820
# define DIR_SEP "\\" /* on Windows we need an additional dir separator */
 
821
#else
 
822
# define DIR_SEP ""
 
823
#endif
 
824
 
 
825
gint utils_make_settings_dir(void)
 
826
{
 
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);
820
 
 
821
 
        if (! g_file_test(dir, G_FILE_TEST_EXISTS))
822
 
        {
823
 
                geany_debug("creating config directory %s", dir);
824
 
#ifdef G_OS_WIN32
825
 
                if (mkdir(dir) != 0) error_nr = errno;
826
 
#else
827
 
                if (mkdir(dir, 0700) != 0) error_nr = errno;
828
 
#endif
829
 
        }
830
 
 
831
 
        if (error_nr == 0 && ! g_file_test(filetypes_readme, G_FILE_TEST_EXISTS))
832
 
        {       // try to write template.README
833
 
                gchar *text;
834
 
                text = g_strconcat(
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 ").",
837
 
                                        NULL);
838
 
                error_nr = utils_write_file(filetypes_readme, text);
839
 
                g_free(text);
840
 
 
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.");
844
 
                        errno = EPERM;
845
 
                }
846
 
        }
847
 
 
848
 
        // make subdir for filetype definitions
849
 
        if (error_nr == 0)
850
 
        {
 
831
 
 
832
        gchar *templates_dir = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
 
833
                                        GEANY_TEMPLATES_SUBDIR, G_DIR_SEPARATOR_S, NULL);
 
834
 
 
835
        if (! g_file_test(app->configdir, G_FILE_TEST_EXISTS))
 
836
        {
 
837
                geany_debug("creating config directory %s", app->configdir);
 
838
                saved_errno = utils_mkdir(app->configdir, FALSE);
 
839
        }
 
840
 
 
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);
 
844
        }
 
845
 
 
846
        /* make subdir for filetype definitions */
 
847
        if (saved_errno == 0)
 
848
        {
 
849
                gchar *filedefs_readme = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
 
850
                        GEANY_FILEDEFS_SUBDIR, G_DIR_SEPARATOR_S, "filetypes.README", NULL);
 
851
 
851
852
                if (! g_file_test(filedefs_dir, G_FILE_TEST_EXISTS))
852
853
                {
853
 
#ifdef G_OS_WIN32
854
 
                        if (mkdir(filedefs_dir) != 0) error_nr = errno;
855
 
#else
856
 
                        if (mkdir(filedefs_dir, 0700) != 0) error_nr = errno;
857
 
#endif
 
854
                        saved_errno = utils_mkdir(filedefs_dir, FALSE);
858
855
                }
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))
860
857
                {
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);
866
863
                        g_free(text);
867
864
                }
868
 
        }
869
 
 
870
 
        g_free(filetypes_readme);
 
865
                g_free(filedefs_readme);
 
866
        }
 
867
 
 
868
        /* make subdir for template files */
 
869
        if (saved_errno == 0)
 
870
        {
 
871
                gchar *templates_readme = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
 
872
                        GEANY_TEMPLATES_SUBDIR, G_DIR_SEPARATOR_S, "templates.README", NULL);
 
873
 
 
874
                if (! g_file_test(templates_dir, G_FILE_TEST_EXISTS))
 
875
                {
 
876
                        saved_errno = utils_mkdir(templates_dir, FALSE);
 
877
                }
 
878
                if (saved_errno == 0 && ! g_file_test(templates_readme, G_FILE_TEST_EXISTS))
 
879
                {
 
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 ").",
 
883
                                        NULL);
 
884
                        utils_write_file(templates_readme, text);
 
885
                        g_free(text);
 
886
                }
 
887
                g_free(templates_readme);
 
888
        }
 
889
 
871
890
        g_free(filedefs_dir);
872
 
        g_free(filedefs_readme);
 
891
        g_free(templates_dir);
 
892
        g_free(conf_file);
873
893
 
874
 
        return error_nr;
 
894
        return saved_errno;
875
895
}
876
896
 
877
897
 
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)
882
903
{
886
907
        gchar *result;
887
908
        GString *str;
888
909
 
889
 
        if (haystack == NULL) return NULL;
 
910
        if (haystack == NULL)
 
911
                return NULL;
 
912
 
 
913
        if (needle == NULL || replacement == NULL)
 
914
                return haystack;
 
915
 
 
916
        if (utils_str_equal(needle, replacement))
 
917
                return haystack;
890
918
 
891
919
        start = strstr(haystack, needle);
892
920
        lt_pos = utils_strpos(haystack, needle);
893
921
 
894
 
        if (start == NULL || lt_pos == -1) return haystack;
 
922
        if (start == NULL || lt_pos == -1)
 
923
                return haystack;
895
924
 
896
 
        // substitute by copying
 
925
        /* substitute by copying */
897
926
        str = g_string_sized_new(strlen(haystack));
898
927
        for (i = 0; i < lt_pos; i++)
899
928
        {
945
974
}
946
975
 
947
976
 
948
 
gchar *utils_get_date_time()
949
 
{
950
 
        time_t tp = time(NULL);
951
 
        const struct tm *tm = localtime(&tp);
952
 
        gchar *date = g_malloc0(25);
953
 
 
954
 
        strftime(date, 25, "%d.%m.%Y %H:%M:%S %Z", tm);
955
 
        return date;
956
 
}
957
 
 
958
 
 
959
 
gchar *utils_get_date()
960
 
{
961
 
        time_t tp = time(NULL);
962
 
        const struct tm *tm = localtime(&tp);
963
 
        gchar *date = g_malloc0(11);
964
 
 
965
 
        strftime(date, 11, "%Y-%m-%d", tm);
966
 
        return date;
967
 
}
968
 
 
969
 
 
970
 
gchar *utils_get_initials(gchar *name)
 
977
gchar *utils_get_date_time(const gchar *format, time_t *time_to_use)
 
978
{
 
979
        time_t tp;
 
980
        const struct tm *tm;
 
981
        gchar *date;
 
982
 
 
983
        if (format == NULL)
 
984
                return NULL;
 
985
 
 
986
        if (time_to_use != NULL)
 
987
                tp = *time_to_use;
 
988
        else
 
989
                tp = time(NULL);
 
990
 
 
991
        tm = localtime(&tp);
 
992
        date = g_malloc0(256);
 
993
        strftime(date, 256, format, tm);
 
994
        return date;
 
995
}
 
996
 
 
997
 
 
998
gchar *utils_get_initials(const gchar *name)
971
999
{
972
1000
        gint i = 1, j = 1;
973
1001
        gchar *initials = g_malloc0(5);
985
1013
}
986
1014
 
987
1015
 
988
 
/* Wrapper functions for Key-File-Parser from GLib in keyfile.c to reduce code size */
989
 
gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key, const gint default_value)
 
1016
/**
 
1017
 *  Convenience function for g_key_file_get_integer() to add a default value argument.
 
1018
 *
 
1019
 *  @param config A GKeyFile object.
 
1020
 *  @param section The group name to look in for the key.
 
1021
 *  @param key The key to find.
 
1022
 *  @param default_value The default value which will be returned when @c section or @c key
 
1023
 *         don't exist.
 
1024
 *
 
1025
 *  @return The value associated with c key as an integer, or the given default value if the value
 
1026
 *          could not be retrieved.
 
1027
 **/
 
1028
gint utils_get_setting_integer(GKeyFile *config, const gchar *section, const gchar *key,
 
1029
                                                           const gint default_value)
990
1030
{
991
1031
        gint tmp;
992
1032
        GError *error = NULL;
1003
1043
}
1004
1044
 
1005
1045
 
1006
 
gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key, const gboolean default_value)
 
1046
/**
 
1047
 *  Convenience function for g_key_file_get_boolean() to add a default value argument.
 
1048
 *
 
1049
 *  @param config A GKeyFile object.
 
1050
 *  @param section The group name to look in for the key.
 
1051
 *  @param key The key to find.
 
1052
 *  @param default_value The default value which will be returned when @c section or @c key
 
1053
 *         don't exist.
 
1054
 *
 
1055
 *  @return The value associated with c key as a boolean, or the given default value if the value
 
1056
 *          could not be retrieved.
 
1057
 **/
 
1058
gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key,
 
1059
                                                                   const gboolean default_value)
1007
1060
{
1008
1061
        gboolean tmp;
1009
1062
        GError *error = NULL;
1020
1073
}
1021
1074
 
1022
1075
 
1023
 
gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key, const gchar *default_value)
 
1076
/**
 
1077
 *  Convenience function for g_key_file_get_string() to add a default value argument.
 
1078
 *
 
1079
 *  @param config A GKeyFile object.
 
1080
 *  @param section The group name to look in for the key.
 
1081
 *  @param key The key to find.
 
1082
 *  @param default_value The default value which will be returned when @c section or @c key
 
1083
 *         don't exist.
 
1084
 *
 
1085
 *  @return A newly allocated string, or the given default value if the value could not be
 
1086
 *          retrieved.
 
1087
 **/
 
1088
gchar *utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key,
 
1089
                                                                const gchar *default_value)
1024
1090
{
1025
1091
        gchar *tmp;
1026
1092
        GError *error = NULL;
1042
1108
        gint page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook));
1043
1109
        gint cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(app->notebook));
1044
1110
 
1045
 
        if (direction == LEFT && cur_page > 0)
 
1111
        if (direction == LEFT)
1046
1112
        {
1047
 
                gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), cur_page - 1);
 
1113
                if (cur_page > 0)
 
1114
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), cur_page - 1);
 
1115
                else
 
1116
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), page_count - 1);
1048
1117
        }
1049
 
        else if (direction == RIGHT && cur_page < page_count)
 
1118
        else if (direction == RIGHT)
1050
1119
        {
1051
 
                gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), cur_page + 1);
 
1120
                if (cur_page < page_count - 1)
 
1121
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), cur_page + 1);
 
1122
                else
 
1123
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), 0);
1052
1124
        }
1053
1125
}
1054
1126
 
1064
1136
        filebase = g_strconcat(GEANY_STRING_UNTITLED, ".", (doc_list[idx].file_type)->extension, NULL);
1065
1137
        filename = g_path_get_basename(doc_list[idx].file_name);
1066
1138
 
1067
 
        // only search the first 3 lines
 
1139
        /* only search the first 3 lines */
1068
1140
        ttf.chrg.cpMin = 0;
1069
1141
        ttf.chrg.cpMax = sci_get_position_from_line(doc_list[idx].sci, 3);
1070
1142
        ttf.lpstrText = (gchar*)filebase;
1081
1153
}
1082
1154
 
1083
1155
 
1084
 
/* wrapper function to let strcmp work with GeanySymbol struct */
1085
 
gint utils_compare_symbol(const GeanySymbol *a, const GeanySymbol *b)
1086
 
{
1087
 
        if (a == NULL || b == NULL) return 0;
1088
 
 
1089
 
        return strcmp(a->str, b->str);
1090
 
}
1091
 
 
1092
 
 
1093
1156
gchar *utils_get_hex_from_color(GdkColor *color)
1094
1157
{
1095
1158
        gchar *buffer = g_malloc0(9);
1107
1170
 
1108
1171
/* Get directory from current file in the notebook.
1109
1172
 * Returns dir string that should be freed or NULL, depending on whether current file is valid.
1110
 
 * (thanks to Nick Treleaven for this patch) */
1111
 
gchar *utils_get_current_file_dir()
 
1173
 * Returned string is in UTF-8 encoding */
 
1174
gchar *utils_get_current_file_dir_utf8(void)
1112
1175
{
1113
1176
        gint cur_idx = document_get_cur_idx();
1114
1177
 
1115
 
        if (cur_idx >= 0 && doc_list[cur_idx].is_valid) // if valid page found
 
1178
        if (DOC_IDX_VALID(cur_idx)) /* if valid page found */
1116
1179
        {
1117
 
                // get current filename
 
1180
                /* get current filename */
1118
1181
                const gchar *cur_fname = doc_list[cur_idx].file_name;
1119
1182
 
1120
1183
                if (cur_fname != NULL)
1121
1184
                {
1122
 
                        // get folder part from current filename
1123
 
                        return g_path_get_dirname(cur_fname); // returns "." if no path
 
1185
                        /* get folder part from current filename */
 
1186
                        return g_path_get_dirname(cur_fname); /* returns "." if no path */
1124
1187
                }
1125
1188
        }
1126
1189
 
1127
 
        return NULL; // no file open
 
1190
        return NULL; /* no file open */
1128
1191
}
1129
1192
 
1130
1193
 
1131
1194
/* very simple convenience function */
1132
 
void utils_beep()
 
1195
void utils_beep(void)
1133
1196
{
1134
 
        if (app->beep_on_errors) gdk_beep();
 
1197
        if (prefs.beep_on_errors) gdk_beep();
1135
1198
}
1136
1199
 
1137
1200
 
1138
1201
/* taken from busybox, thanks */
1139
 
gchar *utils_make_human_readable_str(unsigned long long size, unsigned long block_size,
1140
 
                                                                         unsigned long display_unit)
 
1202
gchar *utils_make_human_readable_str(guint64 size, gulong block_size,
 
1203
                                                                         gulong display_unit)
1141
1204
{
1142
1205
        /* The code will adjust for additional (appended) units. */
1143
1206
        static const gchar zero_and_units[] = { '0', 0, 'K', 'M', 'G', 'T' };
1144
1207
        static const gchar fmt[] = "%Lu %c%c";
1145
1208
        static const gchar fmt_tenths[] = "%Lu.%d %c%c";
1146
1209
 
1147
 
        unsigned long long val;
 
1210
        guint64 val;
1148
1211
        gint frac;
1149
1212
        const gchar *u;
1150
1213
        const gchar *f;
1213
1276
                return -1;
1214
1277
        }
1215
1278
 
1216
 
        // offset is set to 1 when the string starts with 0x, otherwise it starts with #
1217
 
        // and we don't need to increase the index
 
1279
        /* offset is set to 1 when the string starts with 0x, otherwise it starts with #
 
1280
         * and we don't need to increase the index */
1218
1281
        if (! with_route)
1219
1282
                offset = 1;
1220
1283
 
1229
1292
}
1230
1293
 
1231
1294
 
1232
 
// Returns: new string with the current time formatted HH:MM:SS.
1233
 
gchar *utils_get_current_time_string()
 
1295
/* Returns: newly allocated string with the current time formatted HH:MM:SS. */
 
1296
gchar *utils_get_current_time_string(void)
1234
1297
{
1235
1298
        const time_t tp = time(NULL);
1236
1299
        const struct tm *tmval = localtime(&tp);
1242
1305
}
1243
1306
 
1244
1307
 
1245
 
TMTag *utils_find_tm_tag(const GPtrArray *tags, const gchar *tag_name)
1246
 
{
1247
 
        guint i;
1248
 
        g_return_val_if_fail(tags != NULL, NULL);
1249
 
 
1250
 
        for (i = 0; i < tags->len; ++i)
1251
 
        {
1252
 
                if (utils_strcmp(TM_TAG(tags->pdata[i])->name, tag_name))
1253
 
                        return TM_TAG(tags->pdata[i]);
1254
 
        }
1255
 
        return NULL;
1256
 
}
1257
 
 
1258
 
 
1259
 
GIOChannel *utils_set_up_io_channel(gint fd, GIOCondition cond, GIOFunc func, gpointer data)
 
1308
GIOChannel *utils_set_up_io_channel(
 
1309
                                gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
1260
1310
{
1261
1311
        GIOChannel *ioc;
1262
 
        GError *error = NULL;
1263
 
        const gchar *encoding;
 
1312
        /*const gchar *encoding;*/
1264
1313
 
 
1314
        #ifdef G_OS_WIN32
 
1315
        ioc = g_io_channel_win32_new_fd(fd);
 
1316
        #else
1265
1317
        ioc = g_io_channel_unix_new(fd);
1266
 
 
1267
 
        g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
 
1318
        #endif
 
1319
 
 
1320
        if (nblock)
 
1321
                g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
 
1322
 
 
1323
        g_io_channel_set_encoding(ioc, NULL, NULL);
 
1324
/*
1268
1325
        if (! g_get_charset(&encoding))
1269
1326
        {       // hope this works reliably
 
1327
                GError *error = NULL;
1270
1328
                g_io_channel_set_encoding(ioc, encoding, &error);
1271
1329
                if (error)
1272
1330
                {
1275
1333
                        return ioc;
1276
1334
                }
1277
1335
        }
1278
 
        // "auto-close" ;-)
 
1336
*/
 
1337
        /* "auto-close" ;-) */
1279
1338
        g_io_channel_set_close_on_unref(ioc, TRUE);
1280
1339
 
1281
1340
        g_io_add_watch(ioc, cond, func, data);
1334
1393
                                        string[j] = '\t';
1335
1394
                                        break;
1336
1395
#if 0
1337
 
                                case 'x': // Warning: May produce illegal utf-8 string!
 
1396
                                case 'x': /* Warning: May produce illegal utf-8 string! */
1338
1397
                                        i += 2;
1339
1398
                                        if (i >= strlen(string))
1340
1399
                                        {
1400
1459
                                                j++;
1401
1460
                                                string[j] = (unsigned char) ((unicodechar & 0x3F) | 0x80);
1402
1461
                                        }
1403
 
                                        else if (unicodechar < 0x110000) // more chars are not allowed in unicode
 
1462
                                        else if (unicodechar < 0x110000) /* more chars are not allowed in unicode */
1404
1463
                                        {
1405
1464
                                                string[j] = (unsigned char) ((unicodechar >> 18) | 0xF0);
1406
1465
                                                j++;
1434
1493
}
1435
1494
 
1436
1495
 
1437
 
gchar *utils_scan_unicode_bom(const gchar *string)
1438
 
{
1439
 
        if ((unsigned char)string[0] == 0xef && (unsigned char)string[1] == 0xbb &&
1440
 
                (unsigned char)string[2] == 0xbf)
1441
 
        {
1442
 
                return g_strdup("UTF-8");
1443
 
        }
1444
 
        else if ((unsigned char)string[0] == 0x00 && (unsigned char)string[1] == 0x00 &&
1445
 
                         (unsigned char)string[2] == 0xfe && (unsigned char)string[3] == 0xff)
1446
 
        {
1447
 
                return g_strdup("UTF-32BE"); // Big endian
1448
 
        }
1449
 
        else if ((unsigned char)string[0] == 0xff && (unsigned char)string[1] == 0xfe &&
1450
 
                         (unsigned char)string[2] == 0x00 && (unsigned char)string[3] == 0x00)
1451
 
        {
1452
 
                return g_strdup("UTF-32LE"); // Little endian
1453
 
        }
1454
 
        else if ((unsigned char)string[0]==0xfe && (unsigned char)string[1] == 0xff)
1455
 
        {
1456
 
                return g_strdup("UTF-16BE"); // Big endian
1457
 
        }
1458
 
        else if ((unsigned char)string[0] == 0xff && (unsigned char)string[1] == 0xfe)
1459
 
        {
1460
 
                return g_strdup("UTF-16LE"); // Little endian
1461
 
        }
1462
 
        else if ((string[0] == 0x2b && string[1] == 0x2f && string[2] == 0x76) &&
1463
 
                         (string[3] == 0x38 || string[3] == 0x39 || string[3] == 0x2b || string[3] == 0x2f))
1464
 
        {
1465
 
                 return g_strdup("UTF-7");
1466
 
        }
1467
 
        return NULL;
1468
 
}
1469
 
 
1470
 
 
1471
 
gboolean utils_is_unicode_charset(const gchar *string)
1472
 
{
1473
 
        if (string != NULL && (strncmp(string, "UTF", 3) == 0 || strncmp(string, "UCS", 3) == 0))
1474
 
        {
1475
 
                return TRUE;
1476
 
        }
1477
 
        return FALSE;
1478
 
}
1479
 
 
1480
 
 
1481
1496
/* Wraps a string in place, replacing a space with a newline character.
1482
1497
 * wrapstart is the minimum position to start wrapping or -1 for default */
1483
1498
gboolean utils_wrap_string(gchar *string, gint wrapstart)
1500
1515
}
1501
1516
 
1502
1517
 
 
1518
/**
 
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.
 
1521
 *
 
1522
 *  @param utf8_text UTF-8 encoded text.
 
1523
 *
 
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.
 
1526
 **/
1503
1527
gchar *utils_get_locale_from_utf8(const gchar *utf8_text)
1504
1528
{
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);
 
1529
#ifdef G_OS_WIN32
 
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);
 
1533
#else
 
1534
        gchar *locale_text;
 
1535
 
 
1536
        if (! utf8_text)
 
1537
                return NULL;
 
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;
 
1542
#endif
1508
1543
}
1509
1544
 
1510
1545
 
 
1546
/**
 
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.
 
1549
 *
 
1550
 *  @param locale_text Text in locale encoding.
 
1551
 *
 
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.
 
1554
 **/
1511
1555
gchar *utils_get_utf8_from_locale(const gchar *locale_text)
1512
1556
{
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);
 
1557
#ifdef G_OS_WIN32
 
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);
 
1561
#else
 
1562
        gchar *utf8_text;
 
1563
 
 
1564
        if (! locale_text)
 
1565
                return NULL;
 
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;
1516
 
}
1517
 
 
1518
 
 
 
1570
#endif
 
1571
}
 
1572
 
 
1573
 
 
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, ...)
 
1579
{
 
1580
        va_list a;
 
1581
        gpointer sa;
 
1582
 
 
1583
    for (va_start(a, first);  (sa = va_arg(a, gpointer), sa!=NULL);)
 
1584
    {
 
1585
        if (sa != NULL)
 
1586
                g_free(sa);
 
1587
        }
 
1588
        va_end(a);
 
1589
 
 
1590
    if (first != NULL)
 
1591
        g_free(first);
 
1592
}
 
1593
 
 
1594
 
 
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, ...)
 
1600
{
 
1601
        gsize strvlen, i;
 
1602
        va_list args;
 
1603
        gchar *str;
 
1604
        gchar **strv;
 
1605
 
 
1606
        if (first == NULL)
 
1607
                return NULL;
 
1608
 
 
1609
        strvlen = 1;    /* for first argument */
 
1610
 
 
1611
    /* count other arguments */
 
1612
    va_start(args, first);
 
1613
    for (; va_arg(args, gchar*) != NULL; strvlen++);
 
1614
        va_end(args);
 
1615
 
 
1616
        strv = g_new(gchar*, strvlen + 1);      /* +1 for NULL terminator */
 
1617
        strv[0] = g_strdup(first);
 
1618
 
 
1619
    va_start(args, first);
 
1620
    for (i = 1; str = va_arg(args, gchar*), str != NULL; i++)
 
1621
    {
 
1622
                strv[i] = g_strdup(str);
 
1623
        }
 
1624
        va_end(args);
 
1625
 
 
1626
        strv[i] = NULL;
 
1627
        return strv;
 
1628
}
 
1629
 
 
1630
 
 
1631
#if ! GLIB_CHECK_VERSION(2, 8, 0)
 
1632
/* Taken from GLib SVN, 2007-03-10 */
 
1633
/**
 
1634
 * g_mkdir_with_parents:
 
1635
 * @pathname: a pathname in the GLib file name encoding
 
1636
 * @mode: permissions to use for newly created directories
 
1637
 *
 
1638
 * Create a directory if it doesn't already exist. Create intermediate
 
1639
 * parent directories as needed, too.
 
1640
 *
 
1641
 * Returns: 0 if the directory already exists, or was successfully
 
1642
 * created. Returns -1 if an error occurred, with errno set.
 
1643
 *
 
1644
 * Since: 2.8
 
1645
 */
 
1646
int
 
1647
g_mkdir_with_parents (const gchar *pathname,
 
1648
                      int          mode)
 
1649
{
 
1650
  gchar *fn, *p;
 
1651
 
 
1652
  if (pathname == NULL || *pathname == '\0')
 
1653
    {
 
1654
      errno = EINVAL;
 
1655
      return -1;
 
1656
    }
 
1657
 
 
1658
  fn = g_strdup (pathname);
 
1659
 
 
1660
  if (g_path_is_absolute (fn))
 
1661
    p = (gchar *) g_path_skip_root (fn);
 
1662
  else
 
1663
    p = fn;
 
1664
 
 
1665
  do
 
1666
    {
 
1667
      while (*p && !G_IS_DIR_SEPARATOR (*p))
 
1668
        p++;
 
1669
 
 
1670
      if (!*p)
 
1671
        p = NULL;
 
1672
      else
 
1673
        *p = '\0';
 
1674
 
 
1675
      if (!g_file_test (fn, G_FILE_TEST_EXISTS))
 
1676
        {
 
1677
          if (g_mkdir (fn, mode) == -1)
 
1678
            {
 
1679
              int errno_save = errno;
 
1680
              g_free (fn);
 
1681
              errno = errno_save;
 
1682
              return -1;
 
1683
            }
 
1684
        }
 
1685
      else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
 
1686
        {
 
1687
          g_free (fn);
 
1688
          errno = ENOTDIR;
 
1689
          return -1;
 
1690
        }
 
1691
      if (p)
 
1692
        {
 
1693
          *p++ = G_DIR_SEPARATOR;
 
1694
          while (*p && G_IS_DIR_SEPARATOR (*p))
 
1695
            p++;
 
1696
        }
 
1697
    }
 
1698
  while (p);
 
1699
 
 
1700
  g_free (fn);
 
1701
 
 
1702
  return 0;
 
1703
}
 
1704
#endif
 
1705
 
 
1706
 
 
1707
/**
 
1708
 *  Create a directory if it doesn't already exist.
 
1709
 *  Create intermediate parent directories as needed, too.
 
1710
 *
 
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.
 
1713
 *
 
1714
 *  @return 0 if the directory was successfully created, otherwise the @c errno of the
 
1715
 *          failed operation is returned.
 
1716
 **/
 
1717
gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
 
1718
{
 
1719
        gint mode = 0700;
 
1720
        gint result;
 
1721
 
 
1722
        if (path == NULL || strlen(path) == 0)
 
1723
                return EFAULT;
 
1724
 
 
1725
        result = (create_parent_dirs) ? g_mkdir_with_parents(path, mode) : g_mkdir(path, mode);
 
1726
        if (result != 0)
 
1727
                return errno;
 
1728
        return 0;
 
1729
}
 
1730
 
 
1731
 
 
1732
/**
 
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.
 
1736
 *
 
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,
 
1739
 *                unless @a NULL.
 
1740
 *  @param error The is the location for storing a possible error, or @a NULL.
 
1741
 *
 
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.
 
1744
 **/
 
1745
GSList *utils_get_file_list(const gchar *path, guint *length, GError **error)
 
1746
{
 
1747
        GSList *list = NULL;
 
1748
        guint len = 0;
 
1749
        GDir *dir;
 
1750
 
 
1751
        if (error)
 
1752
                *error = NULL;
 
1753
        if (length)
 
1754
                *length = 0;
 
1755
        g_return_val_if_fail(path != NULL, NULL);
 
1756
 
 
1757
        dir = g_dir_open(path, 0, error);
 
1758
        if (dir == NULL)
 
1759
                return NULL;
 
1760
 
 
1761
        while (1)
 
1762
        {
 
1763
                const gchar *filename = g_dir_read_name(dir);
 
1764
                if (filename == NULL) break;
 
1765
 
 
1766
                list = g_slist_insert_sorted(list, g_strdup(filename), (GCompareFunc) strcmp);
 
1767
                len++;
 
1768
        }
 
1769
        g_dir_close(dir);
 
1770
 
 
1771
        if (length)
 
1772
                *length = len;
 
1773
        return list;
 
1774
}
 
1775
 
 
1776
 
 
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)
 
1779
{
 
1780
        gunichar c;
 
1781
 
 
1782
        if (str == NULL || *str == '\0' || ! g_utf8_validate(str, -1, NULL))
 
1783
                return FALSE;
 
1784
 
 
1785
        while (*str != '\0')
 
1786
        {
 
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))
 
1790
                        return TRUE;
 
1791
                /* FIXME don't write a const string */
 
1792
                str = g_utf8_next_char(str);
 
1793
        }
 
1794
        return FALSE;
 
1795
}
 
1796
 
 
1797
 
 
1798
/**
 
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.
 
1802
 *
 
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.
 
1806
 *
 
1807
 *  @return @a TRUE if @c needle was found, else @a FALSE.
 
1808
 **/
 
1809
gboolean utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
 
1810
{
 
1811
        const gchar *c;
 
1812
        gssize pos = -1;
 
1813
 
 
1814
        g_return_val_if_fail(NZV(needle), FALSE);
 
1815
        g_return_val_if_fail(! NZV(replace) || strstr(replace, needle) == NULL, FALSE);
 
1816
 
 
1817
        while (1)
 
1818
        {
 
1819
                c = strstr(haystack->str, needle);
 
1820
                if (c == NULL)
 
1821
                        break;
 
1822
                else
 
1823
                {
 
1824
                        pos = c - haystack->str;
 
1825
                        g_string_erase(haystack, pos, strlen(needle));
 
1826
                        if (replace)
 
1827
                                g_string_insert(haystack, pos, replace);
 
1828
                }
 
1829
        }
 
1830
        return (pos != -1);
 
1831
}
 
1832
 
 
1833
 
 
1834
/* Get project or default startup directory (if set), or NULL. */
 
1835
const gchar *utils_get_default_dir_utf8(void)
 
1836
{
 
1837
        if (app->project && NZV(app->project->base_path))
 
1838
        {
 
1839
                return app->project->base_path;
 
1840
        }
 
1841
 
 
1842
        if (NZV(prefs.default_open_path))
 
1843
        {
 
1844
                return prefs.default_open_path;
 
1845
        }
 
1846
        return NULL;
 
1847
}
 
1848
 
 
1849
 
 
1850
static gboolean check_error(GError **error)
 
1851
{
 
1852
        if (error != NULL && *error != NULL)
 
1853
        {
 
1854
                /* imitate the GLib warning */
 
1855
                g_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 */
 
1861
                return FALSE;
 
1862
        }
 
1863
        return TRUE;
 
1864
}
 
1865
 
 
1866
 
 
1867
/**
 
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.
 
1870
 *
 
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.
 
1881
 *
 
1882
 *  @return @a TRUE on success, @a FALSE if an error was set.
 
1883
 **/
 
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)
 
1887
{
 
1888
        gboolean result;
 
1889
 
 
1890
        if (! check_error(error))
 
1891
                return FALSE;
 
1892
 
 
1893
        if (argv == NULL)
 
1894
        {
 
1895
                *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
 
1896
                return FALSE;
 
1897
        }
 
1898
 
 
1899
        if (std_out)
 
1900
                *std_out = NULL;
 
1901
 
 
1902
        if (std_err)
 
1903
                *std_err = NULL;
 
1904
 
 
1905
#ifdef G_OS_WIN32
 
1906
        result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status, error);
 
1907
#else
 
1908
        result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error);
 
1909
#endif
 
1910
 
 
1911
        return result;
 
1912
}
 
1913
 
 
1914
 
 
1915
/**
 
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.
 
1918
 *
 
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.
 
1927
 *
 
1928
 *  @return @a TRUE on success, @a FALSE if an error was set.
 
1929
 **/
 
1930
gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
 
1931
                                                   GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid,
 
1932
                                                   GError **error)
 
1933
{
 
1934
        gboolean result;
 
1935
 
 
1936
        if (! check_error(error))
 
1937
                return FALSE;
 
1938
 
 
1939
        if (argv == NULL)
 
1940
        {
 
1941
                *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL");
 
1942
                return FALSE;
 
1943
        }
 
1944
 
 
1945
#ifdef G_OS_WIN32
 
1946
        result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL, error);
 
1947
#else
 
1948
        result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error);
 
1949
#endif
 
1950
        return result;
 
1951
}