~ubuntu-branches/ubuntu/vivid/winefish/vivid

« back to all changes in this revision

Viewing changes to src/bf_lib.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Leidert (dale)
  • Date: 2006-04-14 14:41:11 UTC
  • Revision ID: james.westby@ubuntu.com-20060414144111-wi90w6pr70ifwxtw
Tags: upstream-1.3.3
ImportĀ upstreamĀ versionĀ 1.3.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: bf_lib.c 2177 2006-03-17 15:46:27Z kyanh $ */
 
2
/* Winefish LaTeX Editor (based on Bluefish HTML Editor)
 
3
 * bf_lib.c - non-GUI general functions
 
4
 *
 
5
 * Copyright (C) 2000-2004 Olivier Sessink
 
6
 * Modified for Winefish (C) 2005 Ky Anh <kyanh@o2.pl>
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
21
 */
 
22
/* #define DEBUG */
 
23
 
 
24
#include <gtk/gtk.h>
 
25
#include <unistd.h> /* chdir(), getpid() */
 
26
#include <stdio.h> /* fopen() */
 
27
#include <ctype.h> /* toupper */
 
28
#include <string.h> /* strrchr strncmp memmove strncat*/
 
29
#include <sys/stat.h> /* S_IFDIR */
 
30
#include <errno.h>      /* errno */
 
31
 
 
32
#include "bluefish.h"  /* for DEBUG_MSG and stuff like that */
 
33
#include "bf_lib.h"  /* myself */
 
34
 
 
35
#ifdef WIN32
 
36
#define DIRSTR "\\"
 
37
#define DIRCHR 92
 
38
#else
 
39
#define DIRSTR "/"
 
40
#define DIRCHR '/'
 
41
#endif
 
42
/**
 
43
 * get_filename_on_disk_encoding
 
44
 *
 
45
 * if gnome_vfs is defined, this function will also escape local paths
 
46
 * to make sure we can open files with a # in their name
 
47
 */
 
48
gchar *get_filename_on_disk_encoding(const gchar *utf8filename) {
 
49
        if (utf8filename) {
 
50
                GError *gerror=NULL;
 
51
                gsize b_written;
 
52
                gchar *ondiskencoding = g_filename_from_utf8(utf8filename,-1, NULL,&b_written,&gerror);
 
53
                if (gerror) {
 
54
                        g_print(_("Winefish has trouble reading the filenames. Try to set the environment variable G_BROKEN_FILENAMES=1\n"));
 
55
                        ondiskencoding = g_strdup(utf8filename);
 
56
                }
 
57
                return ondiskencoding;
 
58
        }
 
59
        return NULL;
 
60
}
 
61
 
 
62
gchar *get_utf8filename_from_on_disk_encoding(const gchar *encodedname) {
 
63
        if (encodedname) {
 
64
                GError *gerror=NULL;
 
65
                gsize b_written;
 
66
                gchar *ondiskencoding = g_filename_to_utf8(encodedname,-1, NULL,&b_written,&gerror);
 
67
                if (gerror) {
 
68
                        g_print(_("Winefish has trouble reading the filenames. Try to set the environment variable G_BROKEN_FILENAMES=1\n"));
 
69
                        ondiskencoding = g_strdup(encodedname);
 
70
                }
 
71
                DEBUG_MSG("get_utf8filename_from_on_disk_encoding, utf8filename=%s\n",ondiskencoding);
 
72
                return ondiskencoding;
 
73
        }
 
74
        return NULL;
 
75
}
 
76
 
 
77
gboolean string_is_color(const gchar *color) {
 
78
        GdkColor gcolor;
 
79
        return gdk_color_parse(color, &gcolor);
 
80
}
 
81
 
 
82
static void fill_rwx(short unsigned int bits, char *chars) {
 
83
        chars[0] = (bits & S_IRUSR) ? 'r' : '-';
 
84
        chars[1] = (bits & S_IWUSR) ? 'w' : '-';
 
85
        chars[2] = (bits & S_IXUSR) ? 'x' : '-';
 
86
}
 
87
static void fill_setid(short unsigned int bits, char *chars) {
 
88
#ifdef S_ISUID
 
89
        if (bits & S_ISUID) {
 
90
                /* Set-uid, but not executable by owner.  */
 
91
                if (chars[3] != 'x') chars[3] = 'S';
 
92
                else chars[3] = 's';
 
93
        }
 
94
#endif
 
95
#ifdef S_ISGID
 
96
        if (bits & S_ISGID) {
 
97
                /* Set-gid, but not executable by group.  */
 
98
                if (chars[6] != 'x') chars[6] = 'S';
 
99
                else chars[6] = 's';
 
100
        }
 
101
#endif
 
102
#ifdef S_ISVTX
 
103
        if (bits & S_ISVTX) {
 
104
                /* Sticky, but not executable by others.  */
 
105
                if (chars[9] != 'x') chars[9] = 'T';
 
106
                else chars[9] = 't';
 
107
        }
 
108
#endif
 
109
}
 
110
gchar *filemode_to_string(mode_t statmode) {
 
111
        gchar *str = g_malloc0(10);
 
112
        /* following code "adapted" from GNU filemode.c program */
 
113
        fill_rwx((statmode & 0700) << 0, &str[0]);
 
114
        fill_rwx((statmode & 0070) << 3, &str[3]);
 
115
        fill_rwx((statmode & 0007) << 6, &str[6]);
 
116
        fill_setid(statmode, str);
 
117
        return str;
 
118
}
 
119
 
 
120
 
 
121
/**
 
122
 * return_root_with_protocol:
 
123
 * @url: #const gchar* with the url 
 
124
 *
 
125
 * returns the root of the url, including its trailing slash
 
126
 * this might be in the form
 
127
 * - "protocol://server:port/"
 
128
 * - "/"
 
129
 * - NULL, if the url contains no url, nor does it start with a / character
 
130
 *
 
131
 * if there is no trailing slash, this function will return the root WITH a
 
132
 * trailing slash appended!!
 
133
 *
 
134
 * Return value: #gchar* newly allocated, or NULL
 
135
 */
 
136
gchar *return_root_with_protocol(const gchar *url) {
 
137
        gchar *q;
 
138
        if (!url) return NULL;
 
139
        q = strchr(url,':');
 
140
        if (q && *(q+1)=='/' && *(q+2)=='/' && *(q+3)!='\0') {
 
141
                /* we have a protocol */
 
142
                gchar *root = strchr(q+3, '/');
 
143
                if (root) return g_strndup(url, root - url + 1);
 
144
                /* if there is no third slash character, we probably
 
145
                have an url like http://someserver so we will append 
 
146
                the slash ourselves */
 
147
                return g_strconcat(url, "/",NULL);
 
148
        } else if (url[0] == '/') {
 
149
                /* no protocol, return / */
 
150
                return g_strdup("/");
 
151
        }
 
152
        /* no root known */
 
153
        return NULL;
 
154
}
 
155
 
 
156
/**
 
157
 * pointer_switch_addresses:
 
158
 * a: #gpointer;
 
159
 * b: #gpointer
 
160
 *
 
161
 * after this call, a will contain the address previously in a
 
162
 * and b will contain the address previously in b
 
163
 *
 
164
 * Return value: void
 
165
 */
 
166
#ifdef __GNUC__
 
167
__inline__ 
 
168
#endif
 
169
void pointer_switch_addresses(gpointer *a, gpointer *b) {
 
170
        gpointer c;
 
171
        DEBUG_MSG("pointer_switch_addresses, before, a=%p, b=%p\n",a,b);
 
172
        c = *a;
 
173
        *a = *b;
 
174
        *b = c;
 
175
        DEBUG_MSG("pointer_switch_addresses, after, a=%p, b=%p\n",a,b);
 
176
}
 
177
 
 
178
/**
 
179
 * list_switch_order:
 
180
 * @first: a #GList * item
 
181
 * @second: a #GList * item
 
182
 * 
 
183
 * this function will switch place of these two list items
 
184
 * actually not the items themselves, but the data they are 
 
185
 * pointer to is switched
 
186
 * 
 
187
 * Return value: void
 
188
 **/
 
189
void list_switch_order(GList *first, GList *second) {
 
190
        gpointer tmp;
 
191
        tmp = first->data;
 
192
        first->data = second->data;
 
193
        second->data = tmp;
 
194
}
 
195
/**
 
196
 * file_copy:
 
197
 * @source: a #gchar * containing the source filename
 
198
 * @dest: a #gchar * containing the destination filename
 
199
 * 
 
200
 * copies the contents of the file source to dest
 
201
 * this function is Gnome-VFS aware, so it will work on URI's
 
202
 * 
 
203
 * Return value: gboolean, TRUE if the function succeeds
 
204
 **/
 
205
gboolean file_copy(gchar *source, gchar *dest) {
 
206
#ifdef DEVELOPMENT
 
207
        g_assert(source);
 
208
        g_assert(dest);
 
209
#endif
 
210
        int c;
 
211
        FILE *in, *out;
 
212
        gchar *OnDiEn_source, *OnDiEn_dest;
 
213
        OnDiEn_source = get_filename_on_disk_encoding(source);
 
214
        OnDiEn_dest = get_filename_on_disk_encoding(dest);
 
215
 
 
216
        in = fopen(OnDiEn_source, "r");
 
217
        g_free(OnDiEn_source);
 
218
        if (!in) {
 
219
                return FALSE;
 
220
        }
 
221
        out = fopen(OnDiEn_dest, "w");
 
222
        g_free(OnDiEn_dest);
 
223
        if (!out) {
 
224
                fclose(in);
 
225
                return FALSE;
 
226
        }
 
227
        while((c=fgetc(in)) != EOF) {
 
228
                fputc(c,out);
 
229
        }
 
230
        fclose(in);
 
231
        fclose(out);
 
232
        return TRUE;
 
233
}
 
234
 
 
235
static gint length_common_prefix(gchar *first, gchar *second) {
 
236
        gint i=0;
 
237
        while (first[i] == second[i] && first[i] != '\0') {
 
238
                i++;
 
239
        }
 
240
        return i;
 
241
}
 
242
/**
 
243
 * find_common_prefix_in_stringlist:
 
244
 * @stringlist: a #GList* with strings
 
245
 * 
 
246
 * tests every string in stringlist, and returns the length of the 
 
247
 * common prefix all these strings have
 
248
 *
 
249
 * This is for example useful to find out if a list of filenames
 
250
 * share the same base directory
 
251
 * 
 
252
 * Return value: #gint with number of common characters
 
253
 **/
 
254
gint find_common_prefixlen_in_stringlist(GList *stringlist) {
 
255
        gchar *firststring;
 
256
        gint commonlen;
 
257
        GList *tmplist;
 
258
        tmplist = g_list_first(stringlist);
 
259
        firststring = (gchar *)tmplist->data;
 
260
        commonlen = strlen(firststring);
 
261
        tmplist = g_list_next(tmplist);
 
262
        while(tmplist){
 
263
                gint testlen;
 
264
                gchar *secondstring = (gchar *)tmplist->data;
 
265
                testlen = length_common_prefix(firststring, secondstring);
 
266
                if (testlen < commonlen) {
 
267
                        commonlen = testlen;
 
268
                }
 
269
                tmplist = g_list_next(tmplist);
 
270
        }
 
271
        return commonlen;
 
272
}
 
273
/**
 
274
 * append_string_to_file:
 
275
 * @filename: a #gchar * containing the destination filename
 
276
 * @string: a #gchar * containing the string to append
 
277
 * 
 
278
 * opens the file filename in append mode, and appends the string
 
279
 * no newline or anything else is appended, just the string
 
280
 *
 
281
 * DOES NOT YET SUPPORT GNOME_VFS !!!
 
282
 * 
 
283
 * Return value: gboolean, TRUE if the function succeeds
 
284
 **/
 
285
gboolean append_string_to_file(gchar *filename, gchar *string) {
 
286
        FILE *out;
 
287
        gchar *ondiskencoding = get_filename_on_disk_encoding(filename);
 
288
        out = fopen(ondiskencoding, "a");
 
289
        g_free(ondiskencoding);
 
290
        if (!out) {
 
291
                DEBUG_MSG("append_to_file, could not open file %s for append\n", filename);
 
292
                return FALSE;
 
293
        }
 
294
        fputs(string, out);
 
295
        fclose(out);
 
296
        return TRUE;
 
297
}
 
298
/**
 
299
 * countchars:
 
300
 * @string: a gchar * to count the chars in
 
301
 * @chars: a gchar * with the characters you are interested in
 
302
 *
 
303
 * this function will count every character in string that is also in chars
 
304
 * 
 
305
 * Return value: guint with the number of characters found
 
306
 **/
 
307
guint countchars(const gchar *string, const gchar *chars) {
 
308
        guint count=0;
 
309
        gchar *newstr = strpbrk(string, chars);
 
310
        while(newstr) {
 
311
                count++;
 
312
                newstr = strpbrk(++newstr, chars);
 
313
        }
 
314
        DEBUG_MSG("countchars, returning %d\n",count);
 
315
        return count;
 
316
}
 
317
static gint table_convert_char2int_backend(Tconvert_table *table, const gchar *my_char
 
318
                , Ttcc2i_mode mode, int (*func)(const gchar *arg1, const gchar *arg2) ) {
 
319
        Tconvert_table *entry;
 
320
        entry = table;
 
321
        while (entry->my_char) {
 
322
                if (func(my_char,entry->my_char)==0) {
 
323
                        return entry->my_int;
 
324
                }
 
325
                entry++;
 
326
        }
 
327
        return -1;
 
328
}
 
329
static int strfirstchar(const gchar *mychar, const gchar *tablechar) {
 
330
        return mychar[0] - tablechar[0];
 
331
}
 
332
static int strmycharlen(const gchar *mychar, const gchar *tablechar) {
 
333
        return strncmp(mychar,tablechar,strlen(mychar));
 
334
}
 
335
static int strfull_match_gettext(const gchar *mychar, const gchar *tablechar) {
 
336
        return strcmp(mychar,_(tablechar));
 
337
}
 
338
/**
 
339
 * table_convert_char2int:
 
340
 * @table: a #tconvert_table * with strings and integers
 
341
 * @my_char: a #gchar * containing the string to convert
 
342
 * @mode: #Ttcc2i_mode
 
343
 * 
 
344
 * this function can be used to translate a string from some set (in table)
 
345
 * to an integer
 
346
 * 
 
347
 * Return value: gint, found in table, or -1 if not found
 
348
 **/
 
349
gint table_convert_char2int(Tconvert_table *table, const gchar *my_char, Ttcc2i_mode mode) {
 
350
        switch (mode) {
 
351
        case tcc2i_firstchar:
 
352
                return table_convert_char2int_backend(table,my_char,mode,strfirstchar);
 
353
        case tcc2i_mycharlen:
 
354
                return table_convert_char2int_backend(table,my_char,mode,strmycharlen);
 
355
        case tcc2i_full_match:
 
356
                return table_convert_char2int_backend(table,my_char,mode,strcmp);
 
357
        case tcc2i_full_match_gettext:
 
358
                return table_convert_char2int_backend(table,my_char,mode,strfull_match_gettext);
 
359
        default:
 
360
                DEBUG_MSG("bug in call to table_convert_char2int()\n");
 
361
                return -1;
 
362
        }
 
363
}
 
364
/**
 
365
 * table_convert_int2char:
 
366
 * @table: a #tconvert_table * with strings and integers
 
367
 * @my_int: a #gint containing the integer to convert
 
368
 * 
 
369
 * this function can be used to translate an integer from some set (in table)
 
370
 * to a string
 
371
 * WARNING: This function will return a pointer into table, it will 
 
372
 * NOT allocate new memory
 
373
 * 
 
374
 * Return value: gchar * found in table, else NULL
 
375
 **/
 
376
gchar *table_convert_int2char(Tconvert_table *table, gint my_int) {
 
377
        Tconvert_table *entry;
 
378
        entry = table;
 
379
        while (entry->my_char) {
 
380
                if (my_int == entry->my_int) {
 
381
                        return entry->my_char;
 
382
                }
 
383
                entry++;
 
384
        }
 
385
        return NULL;
 
386
}
 
387
/**
 
388
 * expand_string:
 
389
 * @string: a formatstring #gchar * to convert
 
390
 * @specialchar: a const char to use as 'delimited' or 'special character'
 
391
 * @table: a #Tconvert_table * array to use for conversion
 
392
 * 
 
393
 * this function can convert a format string with %0, %1, or \n, \t 
 
394
 * into the final string, where each %number or \char entry is replaced 
 
395
 * with the string found in table
 
396
 *
 
397
 * so this function is the backend for unescape_string() and 
 
398
 * for replace_string_printflike()
 
399
 *
 
400
 * table is an array with last entry {0, NULL}
 
401
 * 
 
402
 * Return value: a newly allocated gchar * with the resulting string
 
403
 **/
 
404
gchar *expand_string(const gchar *string, const char specialchar, Tconvert_table *table) {
 
405
        gchar *p, *prev, *stringdup;
 
406
        gchar *tmp, *dest = g_strdup("");
 
407
 
 
408
        stringdup = g_strdup(string); /* we make a copy so we can set some \0 chars in the string */
 
409
        prev = stringdup;
 
410
        DEBUG_MSG("expand_string, string='%s'\n", string);
 
411
        p = strchr(prev, specialchar);
 
412
        while (p) {
 
413
                gchar *converted;
 
414
                tmp = dest;
 
415
                *p = '\0'; /* set a \0 at this point, the pointer prev now contains everything up to the current % */
 
416
                DEBUG_MSG("expand_string, prev='%s'\n", prev);
 
417
                p++;
 
418
                converted = table_convert_int2char(table, *p);
 
419
                DEBUG_MSG("expand_string, converted='%s'\n", converted);
 
420
                dest = g_strconcat(dest, prev, converted, NULL);
 
421
                g_free(tmp);
 
422
                prev = ++p;
 
423
                p = strchr(p, specialchar);
 
424
        }
 
425
        tmp = dest;
 
426
        dest = g_strconcat(dest, prev, NULL); /* append the end to the current string */
 
427
        g_free(tmp);
 
428
        
 
429
        g_free(stringdup);
 
430
        DEBUG_MSG("expand_string, dest='%s'\n", dest);
 
431
        return dest;
 
432
}
 
433
gchar *replace_string_printflike(const gchar *string, Tconvert_table *table) {
 
434
        return expand_string(string,'%',table);
 
435
}
 
436
 
 
437
static gint tablesize(Tconvert_table *table) {
 
438
        Tconvert_table *tmpentry = table;
 
439
        while (tmpentry->my_char) tmpentry++;
 
440
        return (tmpentry - table);
 
441
}
 
442
/* for now this function can only unexpand strings with tables that contain only
 
443
single character strings like "\n", "\t" etc. */
 
444
gchar *unexpand_string(const gchar *original, const char specialchar, Tconvert_table *table) {
 
445
        gchar *tmp, *tosearchfor, *retval, *prev, *dest, *orig;
 
446
        Tconvert_table *tmpentry;
 
447
        
 
448
        orig = g_strdup(original);
 
449
        DEBUG_MSG("original='%s', strlen()=%d\n",original,strlen(original));
 
450
        tosearchfor = g_malloc(tablesize(table)+1);
 
451
        DEBUG_MSG("tablesize(table)=%d, alloc'ed %d bytes for tosearchfor\n",tablesize(table), tablesize(table)+1);
 
452
        tmp = tosearchfor;
 
453
        tmpentry = table;
 
454
        while(tmpentry->my_char != NULL) {
 
455
                *tmp = tmpentry->my_char[0]; /* we fill the search string with the first character */
 
456
                tmpentry++;
 
457
                tmp++;
 
458
        }
 
459
        *tmp = '\0';
 
460
        DEBUG_MSG("unexpand_string, tosearchfor='%s'\n",tosearchfor);
 
461
        DEBUG_MSG("alloc'ing %d bytes\n", (countchars(original, tosearchfor) + strlen(original) + 1));
 
462
        retval = g_malloc((countchars(original, tosearchfor) + strlen(original) + 1) * sizeof(gchar));
 
463
        dest = retval;
 
464
        prev = orig;
 
465
        /* now we go trough the original till we hit specialchar */
 
466
        tmp = strpbrk(prev, tosearchfor);
 
467
        while (tmp) {
 
468
                gint len = tmp - prev;
 
469
                gint mychar = table_convert_char2int(table, tmp, tcc2i_firstchar);
 
470
                DEBUG_MSG("unexpand_string, tmp='%s', prev='%s'\n",tmp, prev);
 
471
                if (mychar == -1) mychar = *tmp;
 
472
                DEBUG_MSG("unexpand_string, copy %d bytes and advancing dest\n",len);
 
473
                memcpy(dest, prev, len);
 
474
                dest += len;
 
475
                *dest = specialchar;
 
476
                dest++;
 
477
                *dest = mychar;
 
478
                dest++;
 
479
                prev=tmp+1;
 
480
                DEBUG_MSG("prev now is '%s'\n",prev);
 
481
                tmp = strpbrk(prev, tosearchfor);
 
482
        }
 
483
        DEBUG_MSG("unexpand_string, copy the rest (%s) to dest\n",prev);
 
484
        memcpy(dest,prev,strlen(prev)+1); /* this will also make sure there is a \0 at the end */
 
485
        DEBUG_MSG("unexpand_string, retval='%s'\n",retval);
 
486
        g_free(orig);
 
487
        g_free(tosearchfor);
 
488
        return retval;
 
489
}
 
490
/* if you change this table, please change escape_string() and unescape_string() and new_convert_table() in the same way */
 
491
static Tconvert_table standardescapetable [] = {
 
492
        {'n', "\n"},
 
493
        {'t', "\t"},
 
494
        {'\\', "\\"},
 
495
        {'f', "\f"},
 
496
        {'r', "\r"},
 
497
        {'a', "\a"},
 
498
        {'b', "\b"},
 
499
        {'v', "\v"},
 
500
        {'n', "\n"},
 
501
        {':', ":"}, /* this double entry is there to make unescape_string and escape_string work efficient */
 
502
        {0, NULL}
 
503
};
 
504
gchar *unescape_string(const gchar *original, gboolean escape_colon) {
 
505
        gchar *string, *tmp=NULL;
 
506
        DEBUG_MSG("unescape_string, started\n");
 
507
        if (!escape_colon) {
 
508
                tmp = standardescapetable[9].my_char;
 
509
                standardescapetable[9].my_char = NULL;
 
510
        }
 
511
        string = expand_string(original,'\\',standardescapetable);
 
512
        if (!escape_colon) {
 
513
                standardescapetable[9].my_char = tmp;
 
514
        }
 
515
        return string;
 
516
}
 
517
gchar *escape_string(const gchar *original, gboolean escape_colon) {
 
518
        gchar *string, *tmp=NULL;
 
519
        DEBUG_MSG("escape_string, started\n");
 
520
        if (!escape_colon) {
 
521
                tmp = standardescapetable[9].my_char;
 
522
                standardescapetable[9].my_char = NULL;
 
523
        }
 
524
        string = unexpand_string(original,'\\',standardescapetable);
 
525
        if (!escape_colon) {
 
526
                standardescapetable[9].my_char = tmp;
 
527
        }
 
528
        return string;
 
529
}
 
530
Tconvert_table *new_convert_table(gint size, gboolean fill_standardescape) {
 
531
        gint realsize = (fill_standardescape) ? size + 10 : size;
 
532
        Tconvert_table * tct = g_new(Tconvert_table, realsize+1);
 
533
        DEBUG_MSG("new_convert_table, size=%d, realsize=%d,alloced=%d\n",size,realsize,realsize+1);
 
534
        if (fill_standardescape) {
 
535
                gint i;
 
536
                for (i=size;i<realsize;i++) {
 
537
                        tct[i].my_int = standardescapetable[i-size].my_int;
 
538
                        tct[i].my_char = g_strdup(standardescapetable[i-size].my_char);
 
539
                }
 
540
                DEBUG_MSG("new_convert_table, setting tct[%d] (i) to NULL\n",i);
 
541
                tct[i].my_char = NULL;
 
542
        } else {
 
543
                DEBUG_MSG("new_convert_table, setting tct[%d] (size) to NULL\n",size);
 
544
                tct[size].my_char = NULL;
 
545
        }
 
546
        return tct;
 
547
}
 
548
void free_convert_table(Tconvert_table *tct) {
 
549
        Tconvert_table *tmp = tct;
 
550
        while (tmp->my_char) {
 
551
                DEBUG_MSG("free_convert_table, my_char=%s\n",tmp->my_char);
 
552
                g_free(tmp->my_char);
 
553
                tmp++;
 
554
        }
 
555
        DEBUG_MSG("free_convert_table, free table %p\n",tct);
 
556
        g_free(tct);
 
557
}
 
558
 
 
559
/* kyanh, added, 20050223 */
 
560
gchar *convert_command(Tbfwin *bfwin, const gchar *command) {
 
561
        
 
562
        if (! bfwin->current_document) {
 
563
                return g_strdup(command);
 
564
        }
 
565
 
 
566
        gchar *result;
 
567
        gboolean
 
568
                need_D, need_B, need_d, need_b,
 
569
                need_f, need_l, need_p
 
570
                ;
 
571
        gint num_needs;
 
572
 
 
573
        need_D = (strstr(command, "%D") != NULL);
 
574
        need_B = (strstr(command, "%B") != NULL);
 
575
        need_d = (strstr(command, "%d") != NULL);
 
576
        need_b = (strstr(command, "%b") != NULL);
 
577
        need_f = (strstr(command, "%f") != NULL);
 
578
        need_l = (strstr(command, "%l") != NULL);
 
579
        need_p = (strstr(command,"%%") != NULL);
 
580
        /*need_e = (strstr(command, "%e") != NULL);*/
 
581
        num_needs = need_D + need_B + need_d + need_b + need_f + need_l +need_p;
 
582
 
 
583
        if (num_needs) {
 
584
                Tconvert_table *table, *tmpt;
 
585
                table = tmpt = g_new(Tconvert_table, num_needs +1);
 
586
                if (need_p) {
 
587
                        tmpt->my_int = '%';
 
588
                        tmpt->my_char = g_strdup("%");
 
589
                        tmpt++;
 
590
                }
 
591
                if (need_D) {
 
592
                        tmpt->my_int = 'D';
 
593
                        if ( bfwin->project && (bfwin->project->view_bars & MODE_PROJECT) &&  g_file_test(bfwin->project->basedir, G_FILE_TEST_IS_DIR)) {
 
594
                                tmpt->my_char = g_strdup(bfwin->project->basedir);
 
595
                        }else{
 
596
                                if (bfwin->current_document->filename) {
 
597
                                        tmpt->my_char = g_path_get_dirname(bfwin->current_document->filename);
 
598
                                }else{
 
599
                                        tmpt->my_char = g_strdup("?D");
 
600
                                }
 
601
                        }
 
602
                        tmpt++;
 
603
                }
 
604
                if (need_B) {
 
605
                        tmpt->my_int = 'B';
 
606
                        {
 
607
                                gchar *tmpstring=NULL;
 
608
                                if (bfwin->project && (bfwin->project->view_bars & MODE_PROJECT) && g_file_test(bfwin->project->basedir,G_FILE_TEST_IS_DIR) ) {
 
609
                                        tmpstring = g_strconcat(bfwin->project->basedir,"/",bfwin->project->basefile,NULL); 
 
610
                                        if ( g_file_test(tmpstring,G_FILE_TEST_EXISTS) ) {
 
611
                                                g_free(tmpstring);
 
612
                                                tmpstring = g_strdup(bfwin->project->basefile);
 
613
                                        }else{
 
614
                                                g_free(tmpstring);
 
615
                                                if (bfwin->current_document->filename) {
 
616
                                                        tmpstring = g_path_get_basename(bfwin->current_document->filename);
 
617
                                                }else{
 
618
                                                        tmpstring = NULL;
 
619
                                                }
 
620
                                        }
 
621
                                }else{
 
622
                                        if (bfwin->current_document->filename) {
 
623
                                                tmpstring = g_path_get_basename(bfwin->current_document->filename);
 
624
                                        }
 
625
                                }
 
626
                                if (tmpstring) {
 
627
                                        /* remove extension */
 
628
                                        gchar *ext = g_strrstr(tmpstring,".");
 
629
                                        if (ext) {
 
630
                                                tmpstring = g_strndup(tmpstring,strlen(tmpstring)-strlen(ext));
 
631
                                        }
 
632
                                        tmpt->my_char = g_strdup(tmpstring);
 
633
                                        g_free(tmpstring);
 
634
                                }else{
 
635
                                        tmpt->my_char = g_strdup("?B");
 
636
                                }
 
637
                                /* kyanh, 200550301,
 
638
                                We should not free *ext -- the result of g_strrstr().
 
639
                                See glib/string documentation.
 
640
                                */
 
641
                                /* if (ext) {
 
642
                                        g_free(ext);
 
643
                                }
 
644
                                */
 
645
                        }
 
646
                        tmpt++;
 
647
                }
 
648
                if (need_d) {
 
649
                        tmpt->my_int = 'd';
 
650
                        tmpt->my_char = bfwin->current_document->filename ? g_path_get_dirname(bfwin->current_document->filename) : g_strdup("?d");
 
651
                        tmpt++;
 
652
                }
 
653
                
 
654
                if (need_f) {
 
655
                        tmpt->my_int = 'f';
 
656
                        /* should we check for filename ? */
 
657
                        tmpt->my_char = bfwin->current_document->filename ? g_strdup(bfwin->current_document->filename) : g_strdup("?f");
 
658
                        tmpt++;
 
659
                }
 
660
                
 
661
                if (need_b) {
 
662
                        tmpt->my_int = 'b';
 
663
                        if (bfwin->current_document->filename) {
 
664
                                gchar *tmpstring;
 
665
                                tmpstring = g_path_get_basename(bfwin->current_document->filename);
 
666
                                gchar *ext = g_strrstr(tmpstring,".");
 
667
                                if (ext) {
 
668
                                        tmpstring = g_strndup(tmpstring,strlen(tmpstring)-strlen(ext));
 
669
                                }
 
670
                                tmpt->my_char = g_strdup(tmpstring);
 
671
                                g_free(tmpstring);
 
672
                        }else{
 
673
                                tmpt->my_char = g_strdup("?b");
 
674
                        }
 
675
                        tmpt++;
 
676
                }
 
677
 
 
678
                if (need_l) {
 
679
                        tmpt->my_int = 'l';
 
680
                        {       
 
681
                                gint linenumber;
 
682
                                GtkTextIter iter;
 
683
                                gtk_text_buffer_get_iter_at_mark(bfwin->current_document->buffer,&iter,gtk_text_buffer_get_insert(bfwin->current_document->buffer));
 
684
                                linenumber = gtk_text_iter_get_line(&iter);
 
685
                                linenumber++; /* gkt_text_iter_get_line start numbering from 0 */
 
686
                                tmpt->my_char = g_strdup_printf("%d",linenumber);
 
687
                        }
 
688
                        tmpt++;
 
689
                }
 
690
                tmpt->my_char = NULL;
 
691
                result = replace_string_printflike(command, table);
 
692
                free_convert_table(table);
 
693
        }else{
 
694
                result = g_strdup(command);
 
695
        }
 
696
        return result;
 
697
}
 
698
 
 
699
/**************************************************/
 
700
/* byte offset to UTF8 character offset functions */
 
701
/**************************************************/
 
702
 
 
703
/*
 
704
html files usually have enough cache at size 4
 
705
large php files, a cache of 
 
706
        10 resulted in 160% of the buffer to be parsed
 
707
        12 resulted in 152% of the buffer to be parsed
 
708
        14 resulted in 152% of the buffer to be parsed
 
709
        16 resulted in 152% of the buffer to be parsed
 
710
so we keep it at 10 for the moment
 
711
*/
 
712
#define UTF8_OFFSET_CACHE_SIZE 10
 
713
/* #define UTF8_BYTECHARDEBUG */
 
714
 
 
715
typedef struct {
 
716
        gchar *last_buf;
 
717
        /* the two arrays must be grouped and in this order, because they are moved back 
 
718
        one position in ONE memmove() call */
 
719
        guint  last_byteoffset[UTF8_OFFSET_CACHE_SIZE];
 
720
        guint  last_charoffset[UTF8_OFFSET_CACHE_SIZE];
 
721
#ifdef UTF8_BYTECHARDEBUG
 
722
        guint numcalls_since_reset;
 
723
        unsigned long long int numbytes_parsed;
 
724
        guint numcalls_cached_since_reset;
 
725
        unsigned long long int numbytes_cached_parsed;
 
726
#endif
 
727
} Tutf8_offset_cache;
 
728
 
 
729
static Tutf8_offset_cache utf8_offset_cache;
 
730
 
 
731
/**
 
732
 * utf8_offset_cache_reset:
 
733
 * 
 
734
 * this function will reset the utf8 offset cache used by 
 
735
 * utf8_byteoffset_to_charsoffset_cached()
 
736
 *
 
737
 * normally this is done automatically if utf8_byteoffset_to_charsoffset_cached() 
 
738
 * is called with a new buffer. But if you ever call that function for 
 
739
 * the same buffer but the buffer is changed in the meantime you have 
 
740
 * to reset it manually using utf8_offset_cache_reset()
 
741
 * 
 
742
 * Return value: void
 
743
 **/
 
744
#ifdef __GNUC__
 
745
__inline__ 
 
746
#endif
 
747
void utf8_offset_cache_reset() {
 
748
#ifdef UTF8_BYTECHARDEBUG
 
749
        g_print("UTF8_BYTECHARDEBUG: called %d times for total %llu bytes\n",utf8_offset_cache.numcalls_since_reset,utf8_offset_cache.numbytes_parsed);
 
750
        g_print("UTF8_BYTECHARDEBUG: cache HIT %d times, reduced to %llu bytes, cache size %d\n",utf8_offset_cache.numcalls_cached_since_reset,utf8_offset_cache.numbytes_cached_parsed,UTF8_OFFSET_CACHE_SIZE);
 
751
#endif
 
752
        memset(&utf8_offset_cache, 0, sizeof(Tutf8_offset_cache));
 
753
}
 
754
/**
 
755
 * utf8_byteoffset_to_charsoffset_cached:
 
756
 * @string: the gchar * you want to count
 
757
 * @byteoffset: glong with the byteoffset you want the charoffset for
 
758
 * 
 
759
 * this function calculates the UTF-8 character offset in a string for
 
760
 * a given byte offset
 
761
 * It uses caching to speedup multiple calls for the same buffer, the cache
 
762
 * is emptied if you change to another buffer. If you use the same buffer but 
 
763
 * change it inbetween calls, you have to reset it yourself using
 
764
 * the utf8_offset_cache_reset() function
 
765
 * 
 
766
 * Return value: guint with character offset
 
767
 **/
 
768
guint utf8_byteoffset_to_charsoffset_cached(gchar *string, glong byteoffset) {
 
769
        guint retval;
 
770
        gint i = UTF8_OFFSET_CACHE_SIZE-1;
 
771
 
 
772
        if (string != utf8_offset_cache.last_buf) {
 
773
                utf8_offset_cache_reset();
 
774
                utf8_offset_cache.last_buf = string;
 
775
        }
 
776
#ifdef DEBUG
 
777
        DEBUG_MSG("utf8_byteoffset_to_charsoffset_cached, string %p has strlen %d\n", string, strlen(string));
 
778
#endif
 
779
 
 
780
        while (i > 0 && utf8_offset_cache.last_byteoffset[i] > byteoffset) {
 
781
                i--;
 
782
        }
 
783
        
 
784
        if (i > 0) {
 
785
                retval = g_utf8_pointer_to_offset(string+utf8_offset_cache.last_byteoffset[i], string+byteoffset)+utf8_offset_cache.last_charoffset[i];
 
786
#ifdef UTF8_BYTECHARDEBUG
 
787
                utf8_offset_cache.numbytes_parsed += (byteoffset - utf8_offset_cache.last_byteoffset[i]);
 
788
                utf8_offset_cache.numbytes_cached_parsed += (byteoffset - utf8_offset_cache.last_byteoffset[i]);
 
789
                utf8_offset_cache.numcalls_cached_since_reset++;
 
790
#endif
 
791
        } else {
 
792
                retval = g_utf8_pointer_to_offset(string, string+byteoffset);
 
793
#ifdef UTF8_BYTECHARDEBUG
 
794
                utf8_offset_cache.numbytes_parsed += byteoffset;
 
795
#endif
 
796
        }
 
797
        if (i == (UTF8_OFFSET_CACHE_SIZE-1)) {
 
798
                /* add this new calculation to the cache */
 
799
                /* this is a nasty trick to move all guint entries one back in the array, so we can add the new one */
 
800
                memmove(&utf8_offset_cache.last_byteoffset[0], &utf8_offset_cache.last_byteoffset[1], (UTF8_OFFSET_CACHE_SIZE+UTF8_OFFSET_CACHE_SIZE-1)*sizeof(guint));
 
801
 
 
802
                utf8_offset_cache.last_byteoffset[UTF8_OFFSET_CACHE_SIZE-1] = byteoffset;
 
803
                utf8_offset_cache.last_charoffset[UTF8_OFFSET_CACHE_SIZE-1] = retval;
 
804
        }
 
805
#ifdef UTF8_BYTECHARDEBUG
 
806
        utf8_offset_cache.numcalls_since_reset++;
 
807
#endif
 
808
        return retval;
 
809
}
 
810
 
 
811
/**
 
812
 * escapestring:
 
813
 * @original: a gchar * to escape
 
814
 * @delimiter: a gchar that needs escaping, use '\0' if you don't need one
 
815
 *
 
816
 * this function will backslash escape \n, \t, and \ characters, and if 
 
817
 * there is a delimiter it will also be escaped
 
818
 * 
 
819
 * Return value: a newly allocated gchar * that is escaped
 
820
 **/
 
821
/*gchar *old_escapestring(gchar *original, gchar delimiter)
 
822
{
 
823
        gchar *tmp, *newstring, *escapedchars;
 
824
        guint newsize, pos=0;
 
825
 
 
826
        * count the size of the new string *
 
827
        escapedchars = g_strdup_printf("\n\t\\%c", delimiter);
 
828
        DEBUG_MSG("escapestring, escapedchars=%s, extra length=%d\n", escapedchars, countchars(original, escapedchars));
 
829
        newsize = countchars(original, escapedchars) + strlen(original) + 1;
 
830
        newstring = g_malloc0(newsize * sizeof(gchar));
 
831
        g_free(escapedchars);
 
832
        DEBUG_MSG("escapestring, original=%s, newsize=%d\n", original, newsize);
 
833
 
 
834
        tmp = original;
 
835
        while (*tmp != '\0') {
 
836
                switch (*tmp) {
 
837
                case '\\':
 
838
                        strcat(newstring, "\\\\");
 
839
                        pos +=2;
 
840
                break;
 
841
                case '\n':
 
842
                        strcat(newstring, "\\n");
 
843
                        pos +=2;
 
844
                break;
 
845
                case '\t':
 
846
                        strcat(newstring, "\\t");
 
847
                        pos +=2;
 
848
                break;
 
849
                default:
 
850
                        if (*tmp == delimiter) {
 
851
                                newstring[pos] = '\\';
 
852
                                newstring[pos+1] = delimiter;
 
853
                                pos +=2;                        
 
854
                        } else {
 
855
                                newstring[pos] = *tmp;
 
856
                                pos++;
 
857
                        }
 
858
                break;
 
859
                }
 
860
                newstring[pos] = '\0';
 
861
                tmp++;
 
862
        }
 
863
        DEBUG_MSG("escapestring, newstring = %s\n", newstring);
 
864
        return newstring;
 
865
}*/
 
866
 
 
867
/**
 
868
 * unescapestring:
 
869
 * @original: a gchar * to unescape
 
870
 *
 
871
 * this function will backslash unescape \n, \t, and \\ sequences to 
 
872
 * their characters, and if there is any other escaped character 
 
873
 * it will be replaced without the backslash
 
874
 * 
 
875
 * Return value: a newly allocated gchar * that is unescaped
 
876
 **/
 
877
/*gchar *old_unescapestring(gchar *original)
 
878
{
 
879
        gchar *tmp1, *tmp2, *newstring;
 
880
        guint newsize;
 
881
        gint escaped;
 
882
 
 
883
        newsize = strlen(original) + 1;
 
884
        newstring = g_malloc0(newsize * sizeof(gchar));
 
885
        DEBUG_MSG("unescapestring, original=%s, newsize = %d\n", original, newsize);
 
886
 
 
887
        tmp1 = original;
 
888
        tmp2 = newstring;
 
889
        escaped = 0;
 
890
        while (*tmp1 != '\0') {
 
891
                if (escaped) {
 
892
                        switch (*tmp1) {
 
893
                        case '\\':
 
894
                                *tmp2++ = '\\';
 
895
                        break;
 
896
                        case 'n':
 
897
                                *tmp2++ = '\n';
 
898
                        break;
 
899
                        case 't':
 
900
                                *tmp2++ = '\t';
 
901
                        break;
 
902
                        default:
 
903
                                *tmp2++ = *tmp1;
 
904
                        break;
 
905
                        }
 
906
 
 
907
                        escaped = 0;
 
908
                } else {
 
909
                        if (*tmp1 == '\\')
 
910
                                escaped = 1;
 
911
                        else
 
912
                                *tmp2++ = *tmp1;
 
913
                }
 
914
                tmp1++;
 
915
        }
 
916
        DEBUG_MSG("unescapestring, newstring = %s\n", newstring);
 
917
        return newstring;
 
918
}*/
 
919
 
 
920
/**
 
921
 * strip_any_whitespace:
 
922
 * @string: a gchar * to strip
 
923
 *
 
924
 * strips any double chars defined by isspace() from the string, 
 
925
 * only single spaces are returned
 
926
 * the same string is returned, no memory is allocated in this function
 
927
 * 
 
928
 * Return value: the same gchar * as passed to the function
 
929
 **/
 
930
gchar *strip_any_whitespace(gchar *string) {
 
931
        gint count=0, len;
 
932
 
 
933
#ifdef DEVELOPMENT
 
934
        g_assert(string);
 
935
#endif
 
936
 
 
937
        DEBUG_MSG("strip_any_whitespace, starting string='%s'\n", string);
 
938
        len = strlen(string);
 
939
        while(string[count]) {
 
940
                if (isspace((char)string[count])) {
 
941
                        string[count] = ' ';
 
942
                        if (string[count+1] && isspace((char)string[count+1])) {
 
943
                                gint fcount = count;
 
944
                                while (string[fcount] && isspace((char)string[fcount])) {
 
945
                                        fcount++;
 
946
                                }
 
947
                                DEBUG_MSG("strip_any_whitespace, found %d spaces\n", fcount - count);
 
948
                                memmove(&string[count+1], &string[fcount], len - (count+1));
 
949
                                string[len- (fcount-count)+1] = '\0';
 
950
                                DEBUG_MSG("strip_any_whitespace, after memmove, string='%s'\n", string);
 
951
                        }
 
952
                }
 
953
                count++;
 
954
        }
 
955
        g_strstrip(string);
 
956
        DEBUG_MSG("strip_any_whitespace, returning string='%s'\n", string);
 
957
        return string;
 
958
}
 
959
/**
 
960
 * trunc_on_char:
 
961
 * @string: a #gchar * to truncate
 
962
 * @which_char: a #gchar with the char to truncate on
 
963
 *
 
964
 * Returns a pointer to the same string which is truncated at the first
 
965
 * occurence of which_char
 
966
 * 
 
967
 * Return value: the same gchar * as passed to the function
 
968
 **/
 
969
gchar *trunc_on_char(gchar * string, gchar which_char)
 
970
{
 
971
        gchar *tmpchar = string;
 
972
        while(*tmpchar) {
 
973
                if (*tmpchar == which_char) {
 
974
                        *tmpchar = '\0';
 
975
                        return string;
 
976
                }
 
977
                tmpchar++;
 
978
        }
 
979
        return string;
 
980
}
 
981
/* gchar *strip_common_path(char *image_fn, char *html_fn)
 
982
 * returns a newly allocated string containing the the to_filename
 
983
 * but all the common path with from_filename is removed 
 
984
 *
 
985
 * IS THIS IN USE ?? OBVIOUSLY NOT BECAUSE I CAN REMOVE IT */ 
 
986
/*gchar *strip_common_path(char *to_filename, char *from_filename)
 
987
{
 
988
        gchar *tempstr;
 
989
        int count, count2, dir_length;
 
990
 
 
991
        count = 0;
 
992
        tempstr = strrchr(to_filename, DIRCHR);
 
993
        dir_length = strlen(to_filename) - strlen(tempstr);
 
994
        dir_length += 1;
 
995
        DEBUG_MSG("strip_common_path, dir_lenght=%d\n", dir_length);
 
996
        while ((strncmp(to_filename, from_filename, count + 1)) == 0) {
 
997
                count++;
 
998
                if (count > dir_length) {
 
999
                        count = dir_length;
 
1000
                        break;
 
1001
                }
 
1002
        }
 
1003
        while (to_filename[count - 1] != DIRCHR)
 
1004
                count--;
 
1005
        DEBUG_MSG("strip_common_path, equal count = %d\n", count);
 
1006
        count2 = strlen(to_filename);
 
1007
        tempstr = g_malloc(count2 - count + 2);
 
1008
        memcpy(tempstr, &to_filename[count], count2 - count + 2);
 
1009
        DEBUG_MSG("strip_common_path, tempstr= %s, should be %d long\n", tempstr, count2 - count);
 
1010
        return tempstr;
 
1011
} */
 
1012
 
 
1013
/**
 
1014
 * most_efficient_filename:
 
1015
 * @filename: a gchar * with a possibly inefficient filename like /hello/../tmp/../myfile
 
1016
 *
 
1017
 * tries to eliminate any dir/../ combinations in filename
 
1018
 * this function could do evern better, it should also remove /./ entries
 
1019
 * 
 
1020
 * Return value: the same gchar * as passed to the function
 
1021
 **/
 
1022
gchar *most_efficient_filename(gchar *filename) {
 
1023
        gint i,j, len;
 
1024
 
 
1025
        DEBUG_MSG("most_efficient_filename, 1 filename=%s\n", filename);
 
1026
        len = strlen(filename);
 
1027
        for (i=0; i < len-4; i++) {
 
1028
/*              DEBUG_MSG("most_efficient_filename, i=%d\n", i); */
 
1029
                if (strncmp(&filename[i], "/../", 4) == 0) {
 
1030
                        for (j=1; j < i; j++) {
 
1031
                                if ((filename[i - j] == DIRCHR) || (i==j)) {
 
1032
                                        DEBUG_MSG("most_efficient_filename, &filename[%d-%d]=%s, &filename[%d+3]=%s, %d-%d-4=%d\n", i,j,&filename[i-j],i, &filename[i+3], len, j, len-j-4);
 
1033
                                        memmove(&filename[i-j], &filename[i+3], len-i);
 
1034
                                        j=i;
 
1035
                                        i--;
 
1036
                                }
 
1037
                        }
 
1038
                }
 
1039
        }
 
1040
        DEBUG_MSG("most_efficient_filename, 3 filename=%s\n", filename);
 
1041
        return filename;
 
1042
}
 
1043
 
 
1044
/**
 
1045
 * create_relative_link_to:
 
1046
 * @current_filepath: a #gchar * with the current filename
 
1047
 * @link_to_filepath: a #gchar * with a file to link to
 
1048
 *
 
1049
 * creates a newly allocated relative link from current_filepath 
 
1050
 * to link_to_filepath
 
1051
 *
 
1052
 * if current_filepath == NULL it returns the most efficient filepath 
 
1053
 * for link_to_filepath
 
1054
 *
 
1055
 * if link_to_filepath == NULL it will return NULL
 
1056
 *
 
1057
 * Return value: a newly allocated gchar * with the relative link
 
1058
 **/
 
1059
gchar *create_relative_link_to(gchar * current_filepath, gchar * link_to_filepath)
 
1060
{
 
1061
        gchar *returnstring = NULL;
 
1062
        gchar *eff_current_filepath, *eff_link_to_filepath;
 
1063
        gint common_lenght, maxcommonlen;
 
1064
        gint current_filename_length, link_to_filename_length, current_dirname_length, link_to_dirname_length;
 
1065
        gint count, deeper_dirs;
 
1066
 
 
1067
        if (!current_filepath){
 
1068
                returnstring = most_efficient_filename(g_strdup(link_to_filepath));
 
1069
                return returnstring;
 
1070
        }
 
1071
        if (!link_to_filepath) {
 
1072
                return NULL;
 
1073
        }
 
1074
        eff_current_filepath = most_efficient_filename(g_strdup(current_filepath));
 
1075
        eff_link_to_filepath = most_efficient_filename(g_strdup(link_to_filepath));
 
1076
        DEBUG_MSG("eff_current: '%s'\n",eff_current_filepath);
 
1077
        DEBUG_MSG("eff_link_to: '%s'\n",eff_link_to_filepath);
 
1078
        /* get the size of the directory of the current_filename */
 
1079
        current_filename_length = strlen(strrchr(eff_current_filepath, DIRCHR))-1;
 
1080
        link_to_filename_length = strlen(strrchr(eff_link_to_filepath, DIRCHR))-1;
 
1081
        DEBUG_MSG("create_relative_link_to, filenames: current: %d, link_to:%d\n", current_filename_length,link_to_filename_length); 
 
1082
        current_dirname_length = strlen(eff_current_filepath) - current_filename_length;
 
1083
        link_to_dirname_length = strlen(eff_link_to_filepath) - link_to_filename_length;
 
1084
        DEBUG_MSG("create_relative_link_to, dir's: current: %d, link_to:%d\n", current_dirname_length, link_to_dirname_length); 
 
1085
 
 
1086
        if (current_dirname_length < link_to_dirname_length) {
 
1087
                maxcommonlen = current_dirname_length;
 
1088
        } else {
 
1089
                maxcommonlen = link_to_dirname_length;
 
1090
        }
 
1091
        
 
1092
        /* first lets get the common basedir for both dir+file  by comparing the
 
1093
           common path in the directories */
 
1094
        common_lenght = 0;
 
1095
        while ((strncmp(eff_current_filepath, eff_link_to_filepath, common_lenght + 1)) == 0) {
 
1096
                common_lenght++;
 
1097
                if (common_lenght >= maxcommonlen) {
 
1098
                        common_lenght = maxcommonlen;
 
1099
                        break;
 
1100
                }
 
1101
        }
 
1102
        DEBUG_MSG("create_relative_link_to, common_lenght=%d (not checked for directory)\n", common_lenght);
 
1103
        /* this is the common length, but we need the common directories */
 
1104
        if (eff_current_filepath[common_lenght] != DIRCHR) {
 
1105
                gchar *ltmp = &eff_current_filepath[common_lenght];
 
1106
                while ((*ltmp != DIRCHR) && (common_lenght > 0)) {
 
1107
                        common_lenght--;
 
1108
                        ltmp--;
 
1109
                }
 
1110
        }
 
1111
        DEBUG_MSG("create_relative_link_to, common_lenght=%d (checked for directory)\n", common_lenght);
 
1112
 
 
1113
        /* now we need to count how much deeper (in directories) the current_filename
 
1114
           is compared to the link_to_file, that is the amount of ../ we need to add */
 
1115
        deeper_dirs = 0;
 
1116
        for (count = common_lenght+1; count <= current_dirname_length; count++) {
 
1117
                if (eff_current_filepath[count] == DIRCHR) {
 
1118
                        deeper_dirs++;
 
1119
                        DEBUG_MSG("create_relative_link_to, on count=%d, deeper_dirs=%d\n", count, deeper_dirs);
 
1120
                }
 
1121
        }
 
1122
        DEBUG_MSG("create_relative_link_to, deeper_dirs=%d\n", deeper_dirs);
 
1123
 
 
1124
        /* now we know everything we need to know we can create the relative link */
 
1125
        returnstring = g_malloc0((strlen(link_to_filepath) - common_lenght + 3 * deeper_dirs + 1) * sizeof(gchar *));
 
1126
        count = deeper_dirs;
 
1127
        while (count) {
 
1128
                strcat(returnstring, "../");
 
1129
                count--;
 
1130
        }
 
1131
        strcat(returnstring, &eff_link_to_filepath[common_lenght+1]);
 
1132
        DEBUG_MSG("create_relative_link_to, returnstring=%s\n", returnstring);
 
1133
        g_free(eff_current_filepath);
 
1134
        g_free(eff_link_to_filepath);
 
1135
        return returnstring;
 
1136
}
 
1137
#ifdef HAVE_ATLEAST_GTK_2_4
 
1138
#define STRIP_FILE_URI
 
1139
#endif
 
1140
/**
 
1141
 * create_full_path:
 
1142
 * @filename: a gchar * with the (relative or not) filename
 
1143
 * @basedir: a gchar * with a basedir or NULL for current dir
 
1144
 *
 
1145
 * if filename is already absolute, it returns it
 
1146
 * else it will use basedir if available, else the current dir
 
1147
 * to add to the filename to form the full path
 
1148
 *
 
1149
 * for URL's it will simply return a strdup(), except for file:// URL's, 
 
1150
 * there the file:// bit is stripped and 
 
1151
 * IF YOU HAVE GNOME_VFS any %XX sequenves are converted
 
1152
 * so if you DON'T have gnome_vfs, you should not feed file:// uri's!!
 
1153
 *
 
1154
 * it does use most_efficient_filename() to remote unwanted dir/../ entries
 
1155
 *
 
1156
 * Return value: a newly allocated gchar * with the full path
 
1157
 **/
 
1158
gchar *create_full_path(const gchar * filename, const gchar *basedir) {
 
1159
        gchar *absolute_filename;
 
1160
        gchar *tmpcdir;
 
1161
 
 
1162
        if (!filename) return NULL;
 
1163
        DEBUG_MSG("create_full_path, filename=%s, basedir=%s\n", filename, basedir);
 
1164
#ifdef STRIP_FILE_URI
 
1165
        if (strchr(filename, ':') != NULL) { /* it is an URI!! */
 
1166
                DEBUG_MSG("create_full_path, %s is an URI\n",filename);
 
1167
                if (strncmp(filename, "file://", 7)==0) {
 
1168
                        /* THIS IS A BUG, IF YOU DON'T HAVE GNOME_VFS BUT YOU DO HAVE
 
1169
                        GTK-2.4 A %21 OR SOMETHING LIKE THAT IS NOW NOT CONVERTED !!!!!!!!! */
 
1170
                        return g_strdup(filename+7); /* file:// URI's are never relative paths */
 
1171
                }
 
1172
                return g_strdup(filename); /* cannot do this on remote paths */
 
1173
        }
 
1174
#endif
 
1175
        if (g_path_is_absolute(filename)) {
 
1176
                absolute_filename = g_strdup(filename);
 
1177
        } else {
 
1178
                if (basedir) {
 
1179
                        tmpcdir = ending_slash(basedir);
 
1180
                } else {
 
1181
                        gchar *curdir = g_get_current_dir();
 
1182
                        tmpcdir = ending_slash(curdir);
 
1183
                        g_free(curdir);
 
1184
                }
 
1185
                absolute_filename = g_strconcat(tmpcdir, filename, NULL);
 
1186
                g_free(tmpcdir);
 
1187
        }
 
1188
        absolute_filename = most_efficient_filename(absolute_filename);
 
1189
        return absolute_filename;
 
1190
}
 
1191
 
 
1192
/**
 
1193
 * ending_slash:
 
1194
 * @dirname: a #const gchar * with a diretory name
 
1195
 *
 
1196
 * makes sure the last character of the newly allocated 
 
1197
 * string it returns is a '/'
 
1198
 *
 
1199
 * Return value: a newly allocated gchar * dirname that does end on a '/'
 
1200
 **/
 
1201
gchar *ending_slash(const gchar *dirname) {
 
1202
        if (!dirname) {
 
1203
                return g_strdup("");
 
1204
        }
 
1205
 
 
1206
        if (dirname[strlen(dirname)-1] == DIRCHR) {
 
1207
                return g_strdup(dirname);
 
1208
        } else {
 
1209
                return g_strconcat(dirname, DIRSTR, NULL);
 
1210
        }
 
1211
}
 
1212
/**
 
1213
 * path_get_dirname_with_ending_slash:
 
1214
 * @filename: a #const gchar * with a file path
 
1215
 *
 
1216
 * returns a newly allocated string, containing everything up to 
 
1217
 * the last '/' character, including that character itself.
 
1218
 *
 
1219
 * if no '/' character is found it returns NULL
 
1220
 *
 
1221
 * Return value: a newly allocated gchar * dirname that does end on a '/', or NULL on failure
 
1222
 **/
 
1223
gchar *path_get_dirname_with_ending_slash(const gchar *filename) {
 
1224
        gchar *tmp = strrchr(filename, DIRCHR);
 
1225
        if (tmp) {
 
1226
                return g_strndup(filename, (tmp - filename + 1));
 
1227
        } else {
 
1228
                return NULL;
 
1229
        }
 
1230
}
 
1231
 
 
1232
/**
 
1233
 * file_exists_and_readable:
 
1234
 * @filename: a #const gchar * with a file path
 
1235
 *
 
1236
 * tests if the file exists, and  if it is readable, the last
 
1237
 * check is not reliable, it does not check all the groups you are
 
1238
 * in, so change this function before you rely on that check!
 
1239
 *
 
1240
 * this function is Gnome-VFS aware, so it will work on URI's
 
1241
 *
 
1242
 * Return value: gboolean, TRUE if readable, else FALSE
 
1243
 **/
 
1244
gboolean file_exists_and_readable(const gchar * filename) {
 
1245
        gchar *ondiskencoding;
 
1246
        gboolean retval=TRUE;
 
1247
#ifdef DEVELOPMENT
 
1248
        g_assert(filename);
 
1249
#endif
 
1250
        if (!filename || strlen(filename) < 2) {
 
1251
                DEBUG_MSG("file_exists_and_readable, strlen(filename) < 2 or no filename!!!!\n");
 
1252
                return FALSE;
 
1253
        }
 
1254
        DEBUG_MSG("file_exists_and_readable, filename(%p)=\"%s\", strlen(filename)=%d\n", filename, filename, strlen(filename));
 
1255
 
 
1256
        /* BUG#98 */
 
1257
        if ( g_file_test(filename, G_FILE_TEST_IS_DIR) ) {
 
1258
                return FALSE;
 
1259
        }
 
1260
#ifndef WIN32
 
1261
        ondiskencoding = get_filename_on_disk_encoding(filename);
 
1262
        DEBUG_MSG("file_exists_and_readable, ondiskencoding='%s'\n",ondiskencoding);
 
1263
        {
 
1264
                struct stat naamstat;
 
1265
                errno = 0;
 
1266
                retval = ((stat(ondiskencoding, &naamstat) == 0) && (errno == 0));
 
1267
                DEBUG_MSG("file_exists_and_readable, retval=%d (ernno=%d) for %s\n",retval,errno,ondiskencoding);
 
1268
        }
 
1269
        g_free(ondiskencoding);
 
1270
#endif /* WIN32 */
 
1271
        return retval;
 
1272
}
 
1273
/**
 
1274
 * return_first_existing_filename:
 
1275
 * @filename: a #const gchar * with a filename
 
1276
 * @...: more filenames
 
1277
 *
 
1278
 * you can pass multiple filenames to this function, and it will return
 
1279
 * the first filename that really exists according to file_exists_and_readable()
 
1280
 *
 
1281
 * Return value: gchar * with the first filename found
 
1282
 **/
 
1283
gchar *return_first_existing_filename(const gchar *filename, ...) {
 
1284
        va_list args;
 
1285
        gchar *retval=NULL;
 
1286
 
 
1287
        va_start(args, filename);
 
1288
        while (filename) {
 
1289
                if (file_exists_and_readable(filename)) {
 
1290
                        retval = g_strdup(filename);
 
1291
                        break;
 
1292
                }
 
1293
                filename = va_arg(args, gchar*);
 
1294
        }
 
1295
        va_end(args);
 
1296
        return retval;
 
1297
}
 
1298
 
 
1299
/**
 
1300
 * filename_test_extensions:
 
1301
 * @extensions: a #gchar ** NULL terminated arrau of strings
 
1302
 * @filename: a #const gchar * with a filename
 
1303
 *
 
1304
 * tests if the filename matches one of the extensions passed in the NULL terminated array
 
1305
 * of strings
 
1306
 *
 
1307
 * Return value: gboolean, TRUE if the file has one of the extensions in the array
 
1308
 **/
 
1309
gboolean filename_test_extensions(gchar **extensions, gchar *filename) {
 
1310
        if (!extensions) {
 
1311
                return FALSE;
 
1312
        }
 
1313
        while (*extensions) {
 
1314
                if (strncmp(&filename[strlen(filename)-strlen(*extensions)], *extensions, strlen(*extensions)) == 0 ) {
 
1315
                        /* kyanh added then removed, g_print("File of type %s\n",*extensions); */
 
1316
                        return TRUE;
 
1317
                }
 
1318
                extensions++;
 
1319
        }
 
1320
        return FALSE;
 
1321
}
 
1322
 
 
1323
/**
 
1324
 * bf_str_repeat:
 
1325
 * @str: a #const gchar * 
 
1326
 * @number_of: a #gint
 
1327
 *
 
1328
 * returns a newly allocated string, 
 
1329
 * containing str repeated number_of times
 
1330
 *
 
1331
 * Return value: the newly allocated #gchar *
 
1332
 **/
 
1333
gchar *bf_str_repeat(const gchar * str, gint number_of) {
 
1334
        gchar *retstr;
 
1335
        gint len = strlen(str) * number_of;
 
1336
        retstr = g_malloc(len + 1);
 
1337
        retstr[0] = '\0';
 
1338
        while (number_of) {
 
1339
                strncat(retstr, str, len);
 
1340
                number_of--;
 
1341
        };
 
1342
        return retstr;
 
1343
}
 
1344
 
 
1345
/**
 
1346
 * get_int_from_string:
 
1347
 * @string: a #const gchar * 
 
1348
 *
 
1349
 * tries to find a positive integer value in the string and returns it
 
1350
 * the string does not have to start or end with the integer
 
1351
 * it returns -1 if no integer was found somewhere in the string
 
1352
 *
 
1353
 * Return value: the found #gint, -1 on failure
 
1354
 **/
 
1355
gint get_int_from_string(gchar *string) {
 
1356
        if (string) {
 
1357
                gint faktor = 1, result=-1;
 
1358
                gint i,len = strlen(string);
 
1359
                for (i=len-1;i>=0;i--) {
 
1360
                        if ((string[i] < 58) && (string[i] > 47)) {
 
1361
                                if (result == -1) {
 
1362
                                        result = 0;
 
1363
                                } else {
 
1364
                                        faktor *= 10;
 
1365
                                }
 
1366
                                result += (((gint)string[i])-48)*faktor;
 
1367
                                DEBUG_MSG("get_int_from_string, set result to %d\n", result);
 
1368
                        } else {
 
1369
                                if (result !=-1) {
 
1370
                                        return result;
 
1371
                                }
 
1372
                        }
 
1373
                }
 
1374
                return result;
 
1375
        }
 
1376
        return -1;
 
1377
}
 
1378
 
 
1379
/**
 
1380
 * create_secure_dir_return_filename:
 
1381
 *
 
1382
 * this function uses mkdir(name) to create a dir with permissions rwx------
 
1383
 * mkdir will fail if name already exists or is a symlink or something
 
1384
 * the name is chosen by tempnam() so the chance that mkdir() fails in
 
1385
 * a normal situation is minimal, it almost must be a hacking attempt
 
1386
 *
 
1387
 * the filename generated can safely be used for output of an external 
 
1388
 * script because the dir has rwx------ permissions
 
1389
 *
 
1390
 * Return value: a newly allocated #gchar * containing a temporary filename in a secure dir
 
1391
 **/
 
1392
/* kyanh, 20050301, used by outputbox.c and menu.c
 
1393
 rewrite, use `mkstemp' instead of `tempnam' -- See man tempnam(3)
 
1394
*/
 
1395
gchar *create_secure_dir_return_filename() {
 
1396
        /* gchar *name, *name2;  */
 
1397
        /*
 
1398
        DEBUG_MSG("create_secure_dir_return_filename,g_get_tmp_dir()=%s\n", g_get_tmp_dir());
 
1399
        */
 
1400
        /* it is SAFE to use tempnam() here, because we don't open a file with that name,
 
1401
         * we create a directory with that name. mkdir() will FAIL if this name is a hardlink
 
1402
         * or a symlink, so we DO NOT overwrite any file the link is pointing to
 
1403
         */
 
1404
        /* kyanh, removed, 20050301, removed */
 
1405
        /*
 
1406
        name = tempnam(g_get_tmp_dir(), NULL);
 
1407
        DEBUG_MSG("create_secure_dir_return_filename, name=%s\n", name);
 
1408
        if (!name) {
 
1409
                return NULL;
 
1410
        }
 
1411
 
 
1412
        if (mkdir(name, 0700) != 0) {
 
1413
                g_free(name);
 
1414
                return NULL;
 
1415
        }
 
1416
        name2 = tempnam(name, NULL);
 
1417
        DEBUG_MSG("create_secure_dir_return_filename, name2=%s\n", name2);
 
1418
        g_free(name);
 
1419
        return name2;
 
1420
        */
 
1421
        /* kyanh, 20050220 */
 
1422
        /* kyanh, uncommented-rewrote, 20050301 */
 
1423
        gchar *tmpstr;
 
1424
        /*
 
1425
        gchar *tmpdir = g_strdup(g_getenv("TMPDIR"));
 
1426
        if (!tmpdir) {
 
1427
                tmpdir = g_strdup(P_tmpdir);
 
1428
        }
 
1429
        */
 
1430
        /* g_get_tmp_dir(): The return value is never NULL. */
 
1431
        tmpstr = g_strdup_printf("%s/winefish-XXXXXX",g_get_tmp_dir());
 
1432
        /* g_free(tmpdir); */
 
1433
        if (mkstemp(tmpstr)) {
 
1434
                unlink(tmpstr); /* for mkfifo() */
 
1435
                g_print("create_secure_dir_return_filename: return [%s]\n",tmpstr);
 
1436
                /* g_print("P_tmpdir=%s\n", P_tmpdir); */
 
1437
                return tmpstr;
 
1438
        }else{
 
1439
                g_free(tmpstr);
 
1440
                return NULL;
 
1441
        }
 
1442
}
 
1443
/**
 
1444
 * remove_secure_dir_and_filename:
 
1445
 * @filename: the #gchar * filename to remove
 
1446
 *
 
1447
 * this function will remove a the filename created 
 
1448
 * by create_secure_dir_return_filename(), and the safe
 
1449
 * directory the file was created in
 
1450
 *
 
1451
 * Return value: void
 
1452
 **/
 
1453
void remove_secure_dir_and_filename(gchar *filename) {
 
1454
        /*gchar *dirname = g_path_get_dirname(filename); */
 
1455
        unlink(filename);
 
1456
        /*rmdir(dirname);
 
1457
        g_free(dirname);*/
 
1458
        g_free(filename);
 
1459
}
 
1460
 
 
1461
/* gchar *buf_replace_char(gchar *buf, gint len, gchar srcchar, gchar destchar) {
 
1462
        gint curlen=0;
 
1463
        gchar *tmpbuf=buf;
 
1464
        while(tmpbuf[curlen] != '\0' && curlen < len) {
 
1465
                if (tmpbuf[curlen] == srcchar) {
 
1466
                        tmpbuf[curlen] = destchar;
 
1467
                }
 
1468
                curlen++;
 
1469
        }
 
1470
        return buf;
 
1471
}*/
 
1472
 
 
1473
/**
 
1474
 * wordcount:
 
1475
 * @text: A #gchar* to examine.
 
1476
 * @chars: #guint*, will contain no. chars in text.
 
1477
 * @lines: #guint*, will contain no. lines in text.
 
1478
 * @words: #guint*, will contain no. words in text.
 
1479
 *
 
1480
 * Returns number of characters, lines and words in the supplied #gchar*.
 
1481
 * Handles UTF-8 correctly. Input must be properly encoded UTF-8.
 
1482
 * Words are defined as any characters grouped, separated with spaces.
 
1483
 * I.e., this is a word: "12a42a,.". This is two words: ". ."
 
1484
 *
 
1485
 * This function contains a switch() with inspiration from the GNU wc utility.
 
1486
 * Rewritten for glib UTF-8 handling by Christian Tellefsen, chris@tellefsen.net.
 
1487
 *
 
1488
 * Note that gchar == char, so that gives us no problems here.
 
1489
 *
 
1490
 * Return value: void
 
1491
 **/
 
1492
void wordcount(gchar *text, guint *chars, guint *lines, guint *words)
 
1493
{
 
1494
        guint in_word = 0;
 
1495
        gunichar utext;
 
1496
        
 
1497
        if(!text) return; /* politely refuse to operate on NULL .. */
 
1498
                
 
1499
        *chars = *words = *lines = 0;
 
1500
        while (*text != '\0') {
 
1501
                (*chars)++;
 
1502
                
 
1503
                switch (*text) {
 
1504
                        case '\n':
 
1505
                                (*lines)++;
 
1506
                                /* Fall through. */
 
1507
                        case '\r':
 
1508
                        case '\f':
 
1509
                        case '\t':
 
1510
                        case ' ':
 
1511
                        case '\v':
 
1512
                                mb_word_separator:
 
1513
                                if (in_word) {
 
1514
                                        in_word = 0;
 
1515
                                        (*words)++;
 
1516
                                }
 
1517
                                break;
 
1518
                        default:
 
1519
                                utext = g_utf8_get_char_validated(text, 2); /* This might be an utf-8 char..*/
 
1520
                                if (g_unichar_isspace (utext)) /* Unicode encoded space? */
 
1521
                                        goto mb_word_separator;
 
1522
                                if (g_unichar_isgraph (utext)) /* Is this something printable? */
 
1523
                                        in_word = 1;
 
1524
                                break;
 
1525
                } /* switch */
 
1526
                
 
1527
                text = g_utf8_next_char(text); /* Even if the current char is 2 bytes, this will iterate correctly. */
 
1528
        }
 
1529
        
 
1530
        /* Capture last word, if there's no whitespace at the end of the file. */
 
1531
        if(in_word) (*words)++;
 
1532
        /* We start counting line numbers from 1 */
 
1533
        if(*chars > 0) (*lines)++;
 
1534
}
 
1535
 
 
1536
GSList *gslist_from_glist(GList *src) {
 
1537
        GSList *target=NULL;
 
1538
        GList *tmplist = g_list_first(src);
 
1539
        while (tmplist) {
 
1540
                target = g_slist_append(target, tmplist->data);
 
1541
                tmplist = g_list_next(tmplist);
 
1542
        }
 
1543
        return target;
 
1544
}
 
1545
 
 
1546
GList *glist_from_gslist(GSList *src) {
 
1547
        GList *target=NULL;
 
1548
        GSList *tmplist = src;
 
1549
        while (tmplist) {
 
1550
                target = g_list_append(target, tmplist->data);
 
1551
                tmplist = g_slist_next(tmplist);
 
1552
        }
 
1553
        return target;
 
1554
}