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
5
* Copyright (C) 2000-2004 Olivier Sessink
6
* Modified for Winefish (C) 2005 Ky Anh <kyanh@o2.pl>
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.
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.
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
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 */
32
#include "bluefish.h" /* for DEBUG_MSG and stuff like that */
33
#include "bf_lib.h" /* myself */
43
* get_filename_on_disk_encoding
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
48
gchar *get_filename_on_disk_encoding(const gchar *utf8filename) {
52
gchar *ondiskencoding = g_filename_from_utf8(utf8filename,-1, NULL,&b_written,&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);
57
return ondiskencoding;
62
gchar *get_utf8filename_from_on_disk_encoding(const gchar *encodedname) {
66
gchar *ondiskencoding = g_filename_to_utf8(encodedname,-1, NULL,&b_written,&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);
71
DEBUG_MSG("get_utf8filename_from_on_disk_encoding, utf8filename=%s\n",ondiskencoding);
72
return ondiskencoding;
77
gboolean string_is_color(const gchar *color) {
79
return gdk_color_parse(color, &gcolor);
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' : '-';
87
static void fill_setid(short unsigned int bits, char *chars) {
90
/* Set-uid, but not executable by owner. */
91
if (chars[3] != 'x') chars[3] = 'S';
97
/* Set-gid, but not executable by group. */
98
if (chars[6] != 'x') chars[6] = 'S';
103
if (bits & S_ISVTX) {
104
/* Sticky, but not executable by others. */
105
if (chars[9] != 'x') chars[9] = 'T';
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);
122
* return_root_with_protocol:
123
* @url: #const gchar* with the url
125
* returns the root of the url, including its trailing slash
126
* this might be in the form
127
* - "protocol://server:port/"
129
* - NULL, if the url contains no url, nor does it start with a / character
131
* if there is no trailing slash, this function will return the root WITH a
132
* trailing slash appended!!
134
* Return value: #gchar* newly allocated, or NULL
136
gchar *return_root_with_protocol(const gchar *url) {
138
if (!url) return NULL;
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("/");
157
* pointer_switch_addresses:
161
* after this call, a will contain the address previously in a
162
* and b will contain the address previously in b
169
void pointer_switch_addresses(gpointer *a, gpointer *b) {
171
DEBUG_MSG("pointer_switch_addresses, before, a=%p, b=%p\n",a,b);
175
DEBUG_MSG("pointer_switch_addresses, after, a=%p, b=%p\n",a,b);
180
* @first: a #GList * item
181
* @second: a #GList * item
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
189
void list_switch_order(GList *first, GList *second) {
192
first->data = second->data;
197
* @source: a #gchar * containing the source filename
198
* @dest: a #gchar * containing the destination filename
200
* copies the contents of the file source to dest
201
* this function is Gnome-VFS aware, so it will work on URI's
203
* Return value: gboolean, TRUE if the function succeeds
205
gboolean file_copy(gchar *source, gchar *dest) {
212
gchar *OnDiEn_source, *OnDiEn_dest;
213
OnDiEn_source = get_filename_on_disk_encoding(source);
214
OnDiEn_dest = get_filename_on_disk_encoding(dest);
216
in = fopen(OnDiEn_source, "r");
217
g_free(OnDiEn_source);
221
out = fopen(OnDiEn_dest, "w");
227
while((c=fgetc(in)) != EOF) {
235
static gint length_common_prefix(gchar *first, gchar *second) {
237
while (first[i] == second[i] && first[i] != '\0') {
243
* find_common_prefix_in_stringlist:
244
* @stringlist: a #GList* with strings
246
* tests every string in stringlist, and returns the length of the
247
* common prefix all these strings have
249
* This is for example useful to find out if a list of filenames
250
* share the same base directory
252
* Return value: #gint with number of common characters
254
gint find_common_prefixlen_in_stringlist(GList *stringlist) {
258
tmplist = g_list_first(stringlist);
259
firststring = (gchar *)tmplist->data;
260
commonlen = strlen(firststring);
261
tmplist = g_list_next(tmplist);
264
gchar *secondstring = (gchar *)tmplist->data;
265
testlen = length_common_prefix(firststring, secondstring);
266
if (testlen < commonlen) {
269
tmplist = g_list_next(tmplist);
274
* append_string_to_file:
275
* @filename: a #gchar * containing the destination filename
276
* @string: a #gchar * containing the string to append
278
* opens the file filename in append mode, and appends the string
279
* no newline or anything else is appended, just the string
281
* DOES NOT YET SUPPORT GNOME_VFS !!!
283
* Return value: gboolean, TRUE if the function succeeds
285
gboolean append_string_to_file(gchar *filename, gchar *string) {
287
gchar *ondiskencoding = get_filename_on_disk_encoding(filename);
288
out = fopen(ondiskencoding, "a");
289
g_free(ondiskencoding);
291
DEBUG_MSG("append_to_file, could not open file %s for append\n", filename);
300
* @string: a gchar * to count the chars in
301
* @chars: a gchar * with the characters you are interested in
303
* this function will count every character in string that is also in chars
305
* Return value: guint with the number of characters found
307
guint countchars(const gchar *string, const gchar *chars) {
309
gchar *newstr = strpbrk(string, chars);
312
newstr = strpbrk(++newstr, chars);
314
DEBUG_MSG("countchars, returning %d\n",count);
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;
321
while (entry->my_char) {
322
if (func(my_char,entry->my_char)==0) {
323
return entry->my_int;
329
static int strfirstchar(const gchar *mychar, const gchar *tablechar) {
330
return mychar[0] - tablechar[0];
332
static int strmycharlen(const gchar *mychar, const gchar *tablechar) {
333
return strncmp(mychar,tablechar,strlen(mychar));
335
static int strfull_match_gettext(const gchar *mychar, const gchar *tablechar) {
336
return strcmp(mychar,_(tablechar));
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
344
* this function can be used to translate a string from some set (in table)
347
* Return value: gint, found in table, or -1 if not found
349
gint table_convert_char2int(Tconvert_table *table, const gchar *my_char, Ttcc2i_mode 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);
360
DEBUG_MSG("bug in call to table_convert_char2int()\n");
365
* table_convert_int2char:
366
* @table: a #tconvert_table * with strings and integers
367
* @my_int: a #gint containing the integer to convert
369
* this function can be used to translate an integer from some set (in table)
371
* WARNING: This function will return a pointer into table, it will
372
* NOT allocate new memory
374
* Return value: gchar * found in table, else NULL
376
gchar *table_convert_int2char(Tconvert_table *table, gint my_int) {
377
Tconvert_table *entry;
379
while (entry->my_char) {
380
if (my_int == entry->my_int) {
381
return entry->my_char;
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
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
397
* so this function is the backend for unescape_string() and
398
* for replace_string_printflike()
400
* table is an array with last entry {0, NULL}
402
* Return value: a newly allocated gchar * with the resulting string
404
gchar *expand_string(const gchar *string, const char specialchar, Tconvert_table *table) {
405
gchar *p, *prev, *stringdup;
406
gchar *tmp, *dest = g_strdup("");
408
stringdup = g_strdup(string); /* we make a copy so we can set some \0 chars in the string */
410
DEBUG_MSG("expand_string, string='%s'\n", string);
411
p = strchr(prev, specialchar);
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);
418
converted = table_convert_int2char(table, *p);
419
DEBUG_MSG("expand_string, converted='%s'\n", converted);
420
dest = g_strconcat(dest, prev, converted, NULL);
423
p = strchr(p, specialchar);
426
dest = g_strconcat(dest, prev, NULL); /* append the end to the current string */
430
DEBUG_MSG("expand_string, dest='%s'\n", dest);
433
gchar *replace_string_printflike(const gchar *string, Tconvert_table *table) {
434
return expand_string(string,'%',table);
437
static gint tablesize(Tconvert_table *table) {
438
Tconvert_table *tmpentry = table;
439
while (tmpentry->my_char) tmpentry++;
440
return (tmpentry - table);
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;
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);
454
while(tmpentry->my_char != NULL) {
455
*tmp = tmpentry->my_char[0]; /* we fill the search string with the first character */
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));
465
/* now we go trough the original till we hit specialchar */
466
tmp = strpbrk(prev, tosearchfor);
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);
480
DEBUG_MSG("prev now is '%s'\n",prev);
481
tmp = strpbrk(prev, tosearchfor);
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);
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 [] = {
501
{':', ":"}, /* this double entry is there to make unescape_string and escape_string work efficient */
504
gchar *unescape_string(const gchar *original, gboolean escape_colon) {
505
gchar *string, *tmp=NULL;
506
DEBUG_MSG("unescape_string, started\n");
508
tmp = standardescapetable[9].my_char;
509
standardescapetable[9].my_char = NULL;
511
string = expand_string(original,'\\',standardescapetable);
513
standardescapetable[9].my_char = tmp;
517
gchar *escape_string(const gchar *original, gboolean escape_colon) {
518
gchar *string, *tmp=NULL;
519
DEBUG_MSG("escape_string, started\n");
521
tmp = standardescapetable[9].my_char;
522
standardescapetable[9].my_char = NULL;
524
string = unexpand_string(original,'\\',standardescapetable);
526
standardescapetable[9].my_char = tmp;
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) {
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);
540
DEBUG_MSG("new_convert_table, setting tct[%d] (i) to NULL\n",i);
541
tct[i].my_char = NULL;
543
DEBUG_MSG("new_convert_table, setting tct[%d] (size) to NULL\n",size);
544
tct[size].my_char = NULL;
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);
555
DEBUG_MSG("free_convert_table, free table %p\n",tct);
559
/* kyanh, added, 20050223 */
560
gchar *convert_command(Tbfwin *bfwin, const gchar *command) {
562
if (! bfwin->current_document) {
563
return g_strdup(command);
568
need_D, need_B, need_d, need_b,
569
need_f, need_l, need_p
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;
584
Tconvert_table *table, *tmpt;
585
table = tmpt = g_new(Tconvert_table, num_needs +1);
588
tmpt->my_char = g_strdup("%");
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);
596
if (bfwin->current_document->filename) {
597
tmpt->my_char = g_path_get_dirname(bfwin->current_document->filename);
599
tmpt->my_char = g_strdup("?D");
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) ) {
612
tmpstring = g_strdup(bfwin->project->basefile);
615
if (bfwin->current_document->filename) {
616
tmpstring = g_path_get_basename(bfwin->current_document->filename);
622
if (bfwin->current_document->filename) {
623
tmpstring = g_path_get_basename(bfwin->current_document->filename);
627
/* remove extension */
628
gchar *ext = g_strrstr(tmpstring,".");
630
tmpstring = g_strndup(tmpstring,strlen(tmpstring)-strlen(ext));
632
tmpt->my_char = g_strdup(tmpstring);
635
tmpt->my_char = g_strdup("?B");
638
We should not free *ext -- the result of g_strrstr().
639
See glib/string documentation.
650
tmpt->my_char = bfwin->current_document->filename ? g_path_get_dirname(bfwin->current_document->filename) : g_strdup("?d");
656
/* should we check for filename ? */
657
tmpt->my_char = bfwin->current_document->filename ? g_strdup(bfwin->current_document->filename) : g_strdup("?f");
663
if (bfwin->current_document->filename) {
665
tmpstring = g_path_get_basename(bfwin->current_document->filename);
666
gchar *ext = g_strrstr(tmpstring,".");
668
tmpstring = g_strndup(tmpstring,strlen(tmpstring)-strlen(ext));
670
tmpt->my_char = g_strdup(tmpstring);
673
tmpt->my_char = g_strdup("?b");
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);
690
tmpt->my_char = NULL;
691
result = replace_string_printflike(command, table);
692
free_convert_table(table);
694
result = g_strdup(command);
699
/**************************************************/
700
/* byte offset to UTF8 character offset functions */
701
/**************************************************/
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
712
#define UTF8_OFFSET_CACHE_SIZE 10
713
/* #define UTF8_BYTECHARDEBUG */
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;
727
} Tutf8_offset_cache;
729
static Tutf8_offset_cache utf8_offset_cache;
732
* utf8_offset_cache_reset:
734
* this function will reset the utf8 offset cache used by
735
* utf8_byteoffset_to_charsoffset_cached()
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()
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);
752
memset(&utf8_offset_cache, 0, sizeof(Tutf8_offset_cache));
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
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
766
* Return value: guint with character offset
768
guint utf8_byteoffset_to_charsoffset_cached(gchar *string, glong byteoffset) {
770
gint i = UTF8_OFFSET_CACHE_SIZE-1;
772
if (string != utf8_offset_cache.last_buf) {
773
utf8_offset_cache_reset();
774
utf8_offset_cache.last_buf = string;
777
DEBUG_MSG("utf8_byteoffset_to_charsoffset_cached, string %p has strlen %d\n", string, strlen(string));
780
while (i > 0 && utf8_offset_cache.last_byteoffset[i] > byteoffset) {
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++;
792
retval = g_utf8_pointer_to_offset(string, string+byteoffset);
793
#ifdef UTF8_BYTECHARDEBUG
794
utf8_offset_cache.numbytes_parsed += byteoffset;
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));
802
utf8_offset_cache.last_byteoffset[UTF8_OFFSET_CACHE_SIZE-1] = byteoffset;
803
utf8_offset_cache.last_charoffset[UTF8_OFFSET_CACHE_SIZE-1] = retval;
805
#ifdef UTF8_BYTECHARDEBUG
806
utf8_offset_cache.numcalls_since_reset++;
813
* @original: a gchar * to escape
814
* @delimiter: a gchar that needs escaping, use '\0' if you don't need one
816
* this function will backslash escape \n, \t, and \ characters, and if
817
* there is a delimiter it will also be escaped
819
* Return value: a newly allocated gchar * that is escaped
821
/*gchar *old_escapestring(gchar *original, gchar delimiter)
823
gchar *tmp, *newstring, *escapedchars;
824
guint newsize, pos=0;
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);
835
while (*tmp != '\0') {
838
strcat(newstring, "\\\\");
842
strcat(newstring, "\\n");
846
strcat(newstring, "\\t");
850
if (*tmp == delimiter) {
851
newstring[pos] = '\\';
852
newstring[pos+1] = delimiter;
855
newstring[pos] = *tmp;
860
newstring[pos] = '\0';
863
DEBUG_MSG("escapestring, newstring = %s\n", newstring);
869
* @original: a gchar * to unescape
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
875
* Return value: a newly allocated gchar * that is unescaped
877
/*gchar *old_unescapestring(gchar *original)
879
gchar *tmp1, *tmp2, *newstring;
883
newsize = strlen(original) + 1;
884
newstring = g_malloc0(newsize * sizeof(gchar));
885
DEBUG_MSG("unescapestring, original=%s, newsize = %d\n", original, newsize);
890
while (*tmp1 != '\0') {
916
DEBUG_MSG("unescapestring, newstring = %s\n", newstring);
921
* strip_any_whitespace:
922
* @string: a gchar * to strip
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
928
* Return value: the same gchar * as passed to the function
930
gchar *strip_any_whitespace(gchar *string) {
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])) {
942
if (string[count+1] && isspace((char)string[count+1])) {
944
while (string[fcount] && isspace((char)string[fcount])) {
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);
956
DEBUG_MSG("strip_any_whitespace, returning string='%s'\n", string);
961
* @string: a #gchar * to truncate
962
* @which_char: a #gchar with the char to truncate on
964
* Returns a pointer to the same string which is truncated at the first
965
* occurence of which_char
967
* Return value: the same gchar * as passed to the function
969
gchar *trunc_on_char(gchar * string, gchar which_char)
971
gchar *tmpchar = string;
973
if (*tmpchar == which_char) {
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
985
* IS THIS IN USE ?? OBVIOUSLY NOT BECAUSE I CAN REMOVE IT */
986
/*gchar *strip_common_path(char *to_filename, char *from_filename)
989
int count, count2, dir_length;
992
tempstr = strrchr(to_filename, DIRCHR);
993
dir_length = strlen(to_filename) - strlen(tempstr);
995
DEBUG_MSG("strip_common_path, dir_lenght=%d\n", dir_length);
996
while ((strncmp(to_filename, from_filename, count + 1)) == 0) {
998
if (count > dir_length) {
1003
while (to_filename[count - 1] != DIRCHR)
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);
1014
* most_efficient_filename:
1015
* @filename: a gchar * with a possibly inefficient filename like /hello/../tmp/../myfile
1017
* tries to eliminate any dir/../ combinations in filename
1018
* this function could do evern better, it should also remove /./ entries
1020
* Return value: the same gchar * as passed to the function
1022
gchar *most_efficient_filename(gchar *filename) {
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);
1040
DEBUG_MSG("most_efficient_filename, 3 filename=%s\n", filename);
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
1049
* creates a newly allocated relative link from current_filepath
1050
* to link_to_filepath
1052
* if current_filepath == NULL it returns the most efficient filepath
1053
* for link_to_filepath
1055
* if link_to_filepath == NULL it will return NULL
1057
* Return value: a newly allocated gchar * with the relative link
1059
gchar *create_relative_link_to(gchar * current_filepath, gchar * link_to_filepath)
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;
1067
if (!current_filepath){
1068
returnstring = most_efficient_filename(g_strdup(link_to_filepath));
1069
return returnstring;
1071
if (!link_to_filepath) {
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);
1086
if (current_dirname_length < link_to_dirname_length) {
1087
maxcommonlen = current_dirname_length;
1089
maxcommonlen = link_to_dirname_length;
1092
/* first lets get the common basedir for both dir+file by comparing the
1093
common path in the directories */
1095
while ((strncmp(eff_current_filepath, eff_link_to_filepath, common_lenght + 1)) == 0) {
1097
if (common_lenght >= maxcommonlen) {
1098
common_lenght = maxcommonlen;
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)) {
1111
DEBUG_MSG("create_relative_link_to, common_lenght=%d (checked for directory)\n", common_lenght);
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 */
1116
for (count = common_lenght+1; count <= current_dirname_length; count++) {
1117
if (eff_current_filepath[count] == DIRCHR) {
1119
DEBUG_MSG("create_relative_link_to, on count=%d, deeper_dirs=%d\n", count, deeper_dirs);
1122
DEBUG_MSG("create_relative_link_to, deeper_dirs=%d\n", deeper_dirs);
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;
1128
strcat(returnstring, "../");
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;
1137
#ifdef HAVE_ATLEAST_GTK_2_4
1138
#define STRIP_FILE_URI
1142
* @filename: a gchar * with the (relative or not) filename
1143
* @basedir: a gchar * with a basedir or NULL for current dir
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
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!!
1154
* it does use most_efficient_filename() to remote unwanted dir/../ entries
1156
* Return value: a newly allocated gchar * with the full path
1158
gchar *create_full_path(const gchar * filename, const gchar *basedir) {
1159
gchar *absolute_filename;
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 */
1172
return g_strdup(filename); /* cannot do this on remote paths */
1175
if (g_path_is_absolute(filename)) {
1176
absolute_filename = g_strdup(filename);
1179
tmpcdir = ending_slash(basedir);
1181
gchar *curdir = g_get_current_dir();
1182
tmpcdir = ending_slash(curdir);
1185
absolute_filename = g_strconcat(tmpcdir, filename, NULL);
1188
absolute_filename = most_efficient_filename(absolute_filename);
1189
return absolute_filename;
1194
* @dirname: a #const gchar * with a diretory name
1196
* makes sure the last character of the newly allocated
1197
* string it returns is a '/'
1199
* Return value: a newly allocated gchar * dirname that does end on a '/'
1201
gchar *ending_slash(const gchar *dirname) {
1203
return g_strdup("");
1206
if (dirname[strlen(dirname)-1] == DIRCHR) {
1207
return g_strdup(dirname);
1209
return g_strconcat(dirname, DIRSTR, NULL);
1213
* path_get_dirname_with_ending_slash:
1214
* @filename: a #const gchar * with a file path
1216
* returns a newly allocated string, containing everything up to
1217
* the last '/' character, including that character itself.
1219
* if no '/' character is found it returns NULL
1221
* Return value: a newly allocated gchar * dirname that does end on a '/', or NULL on failure
1223
gchar *path_get_dirname_with_ending_slash(const gchar *filename) {
1224
gchar *tmp = strrchr(filename, DIRCHR);
1226
return g_strndup(filename, (tmp - filename + 1));
1233
* file_exists_and_readable:
1234
* @filename: a #const gchar * with a file path
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!
1240
* this function is Gnome-VFS aware, so it will work on URI's
1242
* Return value: gboolean, TRUE if readable, else FALSE
1244
gboolean file_exists_and_readable(const gchar * filename) {
1245
gchar *ondiskencoding;
1246
gboolean retval=TRUE;
1250
if (!filename || strlen(filename) < 2) {
1251
DEBUG_MSG("file_exists_and_readable, strlen(filename) < 2 or no filename!!!!\n");
1254
DEBUG_MSG("file_exists_and_readable, filename(%p)=\"%s\", strlen(filename)=%d\n", filename, filename, strlen(filename));
1257
if ( g_file_test(filename, G_FILE_TEST_IS_DIR) ) {
1261
ondiskencoding = get_filename_on_disk_encoding(filename);
1262
DEBUG_MSG("file_exists_and_readable, ondiskencoding='%s'\n",ondiskencoding);
1264
struct stat naamstat;
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);
1269
g_free(ondiskencoding);
1274
* return_first_existing_filename:
1275
* @filename: a #const gchar * with a filename
1276
* @...: more filenames
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()
1281
* Return value: gchar * with the first filename found
1283
gchar *return_first_existing_filename(const gchar *filename, ...) {
1287
va_start(args, filename);
1289
if (file_exists_and_readable(filename)) {
1290
retval = g_strdup(filename);
1293
filename = va_arg(args, gchar*);
1300
* filename_test_extensions:
1301
* @extensions: a #gchar ** NULL terminated arrau of strings
1302
* @filename: a #const gchar * with a filename
1304
* tests if the filename matches one of the extensions passed in the NULL terminated array
1307
* Return value: gboolean, TRUE if the file has one of the extensions in the array
1309
gboolean filename_test_extensions(gchar **extensions, gchar *filename) {
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); */
1325
* @str: a #const gchar *
1326
* @number_of: a #gint
1328
* returns a newly allocated string,
1329
* containing str repeated number_of times
1331
* Return value: the newly allocated #gchar *
1333
gchar *bf_str_repeat(const gchar * str, gint number_of) {
1335
gint len = strlen(str) * number_of;
1336
retstr = g_malloc(len + 1);
1339
strncat(retstr, str, len);
1346
* get_int_from_string:
1347
* @string: a #const gchar *
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
1353
* Return value: the found #gint, -1 on failure
1355
gint get_int_from_string(gchar *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)) {
1366
result += (((gint)string[i])-48)*faktor;
1367
DEBUG_MSG("get_int_from_string, set result to %d\n", result);
1380
* create_secure_dir_return_filename:
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
1387
* the filename generated can safely be used for output of an external
1388
* script because the dir has rwx------ permissions
1390
* Return value: a newly allocated #gchar * containing a temporary filename in a secure dir
1392
/* kyanh, 20050301, used by outputbox.c and menu.c
1393
rewrite, use `mkstemp' instead of `tempnam' -- See man tempnam(3)
1395
gchar *create_secure_dir_return_filename() {
1396
/* gchar *name, *name2; */
1398
DEBUG_MSG("create_secure_dir_return_filename,g_get_tmp_dir()=%s\n", g_get_tmp_dir());
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
1404
/* kyanh, removed, 20050301, removed */
1406
name = tempnam(g_get_tmp_dir(), NULL);
1407
DEBUG_MSG("create_secure_dir_return_filename, name=%s\n", name);
1412
if (mkdir(name, 0700) != 0) {
1416
name2 = tempnam(name, NULL);
1417
DEBUG_MSG("create_secure_dir_return_filename, name2=%s\n", name2);
1421
/* kyanh, 20050220 */
1422
/* kyanh, uncommented-rewrote, 20050301 */
1425
gchar *tmpdir = g_strdup(g_getenv("TMPDIR"));
1427
tmpdir = g_strdup(P_tmpdir);
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); */
1444
* remove_secure_dir_and_filename:
1445
* @filename: the #gchar * filename to remove
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
1451
* Return value: void
1453
void remove_secure_dir_and_filename(gchar *filename) {
1454
/*gchar *dirname = g_path_get_dirname(filename); */
1461
/* gchar *buf_replace_char(gchar *buf, gint len, gchar srcchar, gchar destchar) {
1464
while(tmpbuf[curlen] != '\0' && curlen < len) {
1465
if (tmpbuf[curlen] == srcchar) {
1466
tmpbuf[curlen] = destchar;
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.
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: ". ."
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.
1488
* Note that gchar == char, so that gives us no problems here.
1490
* Return value: void
1492
void wordcount(gchar *text, guint *chars, guint *lines, guint *words)
1497
if(!text) return; /* politely refuse to operate on NULL .. */
1499
*chars = *words = *lines = 0;
1500
while (*text != '\0') {
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? */
1527
text = g_utf8_next_char(text); /* Even if the current char is 2 bytes, this will iterate correctly. */
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)++;
1536
GSList *gslist_from_glist(GList *src) {
1537
GSList *target=NULL;
1538
GList *tmplist = g_list_first(src);
1540
target = g_slist_append(target, tmplist->data);
1541
tmplist = g_list_next(tmplist);
1546
GList *glist_from_gslist(GSList *src) {
1548
GSList *tmplist = src;
1550
target = g_list_append(target, tmplist->data);
1551
tmplist = g_slist_next(tmplist);