2006-08-04 [colin] 2.4.0cvs27
[claws.git] / src / common / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #ifdef ENABLE_NLS
28 #include <glib/gi18n.h>
29 #else
30 #define _(a) (a)
31 #define N_(a) (a)
32 #endif
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <errno.h>
37
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
39 #  include <wchar.h>
40 #  include <wctype.h>
41 #endif
42 #include <stdlib.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <stdarg.h>
46 #include <sys/types.h>
47 #if HAVE_SYS_WAIT_H
48 #  include <sys/wait.h>
49 #endif
50 #include <dirent.h>
51 #include <time.h>
52 #include <regex.h>
53
54 #ifdef G_OS_UNIX
55 #include <sys/utsname.h>
56 #endif
57
58 #ifdef G_OS_WIN32
59 #  include <direct.h>
60 #  include <io.h>
61 #  include <fcntl.h>
62 #endif
63
64 #include "utils.h"
65 #include "socket.h"
66 #include "../codeconv.h"
67
68 #define BUFFSIZE        8192
69
70 static gboolean debug_mode = FALSE;
71 #ifdef G_OS_WIN32
72 static GSList *tempfiles=NULL;
73 #endif
74
75
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
78 {
79 #ifdef G_OS_WIN32
80         if (G_WIN32_HAVE_WIDECHAR_API()) {
81                 wchar_t *wpath;
82                 gint retval;
83                 gint save_errno;
84
85                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
86                 if (wpath == NULL) {
87                         errno = EINVAL;
88                         return -1;
89                 }
90
91                 retval = _wchdir(wpath);
92                 save_errno = errno;
93
94                 g_free(wpath);
95
96                 errno = save_errno;
97                 return retval;
98         } else {
99                 gchar *cp_path;
100                 gint retval;
101                 gint save_errno;
102
103                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104                 if (cp_path == NULL) {
105                         errno = EINVAL;
106                         return -1;
107                 }
108
109                 retval = chdir(cp_path);
110                 save_errno = errno;
111
112                 g_free(cp_path);
113
114                 errno = save_errno;
115                 return retval;
116         }
117 #else
118         return chdir(path);
119 #endif
120 }
121
122 gint g_chmod(const gchar *path, gint mode)
123 {
124 #ifdef G_OS_WIN32
125         if (G_WIN32_HAVE_WIDECHAR_API()) {
126                 wchar_t *wpath;
127                 gint retval;
128                 gint save_errno;
129
130                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
131                 if (wpath == NULL) {
132                         errno = EINVAL;
133                         return -1;
134                 }
135
136                 retval = _wchmod(wpath, mode);
137                 save_errno = errno;
138
139                 g_free(wpath);
140
141                 errno = save_errno;
142                 return retval;
143         } else {
144                 gchar *cp_path;
145                 gint retval;
146                 gint save_errno;
147
148                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149                 if (cp_path == NULL) {
150                         errno = EINVAL;
151                         return -1;
152                 }
153
154                 retval = chmod(cp_path, mode);
155                 save_errno = errno;
156
157                 g_free(cp_path);
158
159                 errno = save_errno;
160                 return retval;
161         }
162 #else
163         return chmod(path, mode);
164 #endif
165 }
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
167
168
169 #ifdef G_OS_WIN32
170 gint mkstemp_name(const gchar *template, gchar **name_used)
171 {
172         static gulong count=0; /* W32-_mktemp only supports up to 27
173                                   tempfiles... */
174         int tmpfd;
175
176         *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177         tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
178                                     (S_IRUSR | S_IWUSR));
179
180         tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
181         if (tmpfd<0) {
182                 perror(g_strdup_printf("cant create %s",*name_used));
183                 return -1;
184         }
185         else
186                 return tmpfd;
187 }
188 #endif /* G_OS_WIN32 */
189
190 #ifdef G_OS_WIN32
191 gint mkstemp(const gchar *template)
192 {
193         gchar *dummyname;
194         gint res = mkstemp_name(template, &dummyname);
195         g_free(dummyname);
196         return res;
197 }
198 #endif /* G_OS_WIN32 */
199
200 void list_free_strings(GList *list)
201 {
202         list = g_list_first(list);
203
204         while (list != NULL) {
205                 g_free(list->data);
206                 list = list->next;
207         }
208 }
209
210 void slist_free_strings(GSList *list)
211 {
212         while (list != NULL) {
213                 g_free(list->data);
214                 list = list->next;
215         }
216 }
217
218 GSList *slist_concat_unique (GSList *first, GSList *second)
219 {
220         GSList *tmp, *ret;
221         if (first == NULL) {
222                 if (second == NULL)
223                         return NULL;
224                 else
225                         return second;
226         } else if (second == NULL)
227                 return first;
228         ret = first;
229         for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230                 if (g_slist_find(ret, tmp->data) == NULL)
231                         ret = g_slist_prepend(ret, tmp->data);
232         }
233         return ret;
234 }
235
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
237 {
238         g_free(key);
239 }
240
241 void hash_free_strings(GHashTable *table)
242 {
243         g_hash_table_foreach(table, hash_free_strings_func, NULL);
244 }
245
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
247                                      gpointer data)
248 {
249         g_free(value);
250 }
251
252 void hash_free_value_mem(GHashTable *table)
253 {
254         g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
255 }
256
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
258 {
259         return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
260 }
261
262 guint str_case_hash(gconstpointer key)
263 {
264         const gchar *p = key;
265         guint h = *p;
266
267         if (h) {
268                 h = g_ascii_tolower(h);
269                 for (p += 1; *p != '\0'; p++)
270                         h = (h << 5) - h + g_ascii_tolower(*p);
271         }
272
273         return h;
274 }
275
276 void ptr_array_free_strings(GPtrArray *array)
277 {
278         gint i;
279         gchar *str;
280
281         g_return_if_fail(array != NULL);
282
283         for (i = 0; i < array->len; i++) {
284                 str = g_ptr_array_index(array, i);
285                 g_free(str);
286         }
287 }
288
289 gboolean str_find(const gchar *haystack, const gchar *needle)
290 {
291         return strstr(haystack, needle) != NULL ? TRUE : FALSE;
292 }
293
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
295 {
296         return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
297 }
298
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
300 {
301         return strcmp(haystack, needle) == 0;
302 }
303
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
305 {
306         return g_ascii_strcasecmp(haystack, needle) == 0;
307 }
308
309 gint to_number(const gchar *nstr)
310 {
311         register const gchar *p;
312
313         if (*nstr == '\0') return -1;
314
315         for (p = nstr; *p != '\0'; p++)
316                 if (!g_ascii_isdigit(*p)) return -1;
317
318         return atoi(nstr);
319 }
320
321 /* convert integer into string,
322    nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
324 {
325         g_snprintf(nstr, 11, "%d", n);
326         return nstr;
327 }
328
329 /* convert integer into string */
330 gchar *itos(gint n)
331 {
332         static gchar nstr[11];
333
334         return itos_buf(nstr, n);
335 }
336
337 gchar *to_human_readable(off_t size)
338 {
339         static gchar str[14];
340
341         if (size < 1024)
342                 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
343         else if (size >> 10 < 1024)
344                 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
345         else if (size >> 20 < 1024)
346                 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
347         else
348                 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
349
350         return str;
351 }
352
353 /* strcmp with NULL-checking */
354 gint strcmp2(const gchar *s1, const gchar *s2)
355 {
356         if (s1 == NULL || s2 == NULL)
357                 return -1;
358         else
359                 return strcmp(s1, s2);
360 }
361 /* strstr with NULL-checking */
362 gchar *strstr2(const gchar *s1, const gchar *s2)
363 {
364         if (s1 == NULL || s2 == NULL)
365                 return NULL;
366         else
367                 return strstr(s1, s2);
368 }
369 /* compare paths */
370 gint path_cmp(const gchar *s1, const gchar *s2)
371 {
372         gint len1, len2;
373         int rc;
374 #ifdef G_OS_WIN32
375         gchar *s1buf, *s2buf;
376 #endif
377
378         if (s1 == NULL || s2 == NULL) return -1;
379         if (*s1 == '\0' || *s2 == '\0') return -1;
380
381 #ifdef G_OS_WIN32
382         s1buf = g_strdup (s1);
383         s2buf = g_strdup (s2);
384         subst_char (s1buf, '/', G_DIR_SEPARATOR);
385         subst_char (s2buf, '/', G_DIR_SEPARATOR);
386         s1 = s1buf;
387         s2 = s2buf;
388 #endif /* !G_OS_WIN32 */
389
390         len1 = strlen(s1);
391         len2 = strlen(s2);
392
393         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
394         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
395
396         rc = strncmp(s1, s2, MAX(len1, len2));
397 #ifdef G_OS_WIN32
398         g_free (s1buf);
399         g_free (s2buf);
400 #endif /* !G_OS_WIN32 */
401         return rc;
402 }
403
404 /* remove trailing return code */
405 gchar *strretchomp(gchar *str)
406 {
407         register gchar *s;
408
409         if (!*str) return str;
410
411         for (s = str + strlen(str) - 1;
412              s >= str && (*s == '\n' || *s == '\r');
413              s--)
414                 *s = '\0';
415
416         return str;
417 }
418
419 /* remove trailing character */
420 gchar *strtailchomp(gchar *str, gchar tail_char)
421 {
422         register gchar *s;
423
424         if (!*str) return str;
425         if (tail_char == '\0') return str;
426
427         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
428                 *s = '\0';
429
430         return str;
431 }
432
433 /* remove CR (carriage return) */
434 gchar *strcrchomp(gchar *str)
435 {
436         register gchar *s;
437
438         if (!*str) return str;
439
440         s = str + strlen(str) - 1;
441         if (*s == '\n' && s > str && *(s - 1) == '\r') {
442                 *(s - 1) = '\n';
443                 *s = '\0';
444         }
445
446         return str;
447 }
448
449 void file_strip_crs(const gchar *file)
450 {
451         FILE *fp = NULL, *outfp = NULL;
452         gchar buf[4096];
453         gchar *out = get_tmp_file();
454         if (file == NULL)
455                 goto freeout;
456
457         fp = fopen(file, "rb");
458         if (!fp)
459                 goto freeout;
460
461         outfp = fopen(out, "wb");
462         if (!outfp) {
463                 fclose(fp);
464                 goto freeout;
465         }
466
467         while (fgets(buf, sizeof (buf), fp) != NULL) {
468                 strcrchomp(buf);
469                 fputs(buf, outfp);
470         }
471
472         fclose(fp);
473         fclose(outfp);
474         rename_force(out, file);
475 freeout:
476         g_free(out);
477 }
478
479 /* Similar to `strstr' but this function ignores the case of both strings.  */
480 gchar *strcasestr(const gchar *haystack, const gchar *needle)
481 {
482         register size_t haystack_len, needle_len;
483
484         haystack_len = strlen(haystack);
485         needle_len   = strlen(needle);
486
487         if (haystack_len < needle_len || needle_len == 0)
488                 return NULL;
489
490         while (haystack_len >= needle_len) {
491                 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
492                         return (gchar *)haystack;
493                 else {
494                         haystack++;
495                         haystack_len--;
496                 }
497         }
498
499         return NULL;
500 }
501
502 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
503                    gconstpointer needle, size_t needlelen)
504 {
505         const gchar *haystack_ = (const gchar *)haystack;
506         const gchar *needle_ = (const gchar *)needle;
507         const gchar *haystack_cur = (const gchar *)haystack;
508
509         if (needlelen == 1)
510                 return memchr(haystack_, *needle_, haystacklen);
511
512         while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
513                != NULL) {
514                 if (haystacklen - (haystack_cur - haystack_) < needlelen)
515                         break;
516                 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
517                         return (gpointer)haystack_cur;
518                 else
519                         haystack_cur++;
520         }
521
522         return NULL;
523 }
524
525 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
526 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
527 {
528         register const gchar *s = src;
529         register gchar *d = dest;
530
531         while (--n && *s)
532                 *d++ = *s++;
533         *d = '\0';
534
535         return dest;
536 }
537
538 #if !HAVE_ISWALNUM
539 int iswalnum(wint_t wc)
540 {
541         return g_ascii_isalnum((int)wc);
542 }
543 #endif
544
545 #if !HAVE_ISWSPACE
546 int iswspace(wint_t wc)
547 {
548         return g_ascii_isspace((int)wc);
549 }
550 #endif
551
552 #if !HAVE_TOWLOWER
553 wint_t towlower(wint_t wc)
554 {
555         if (wc >= L'A' && wc <= L'Z')
556                 return wc + L'a' - L'A';
557
558         return wc;
559 }
560 #endif
561
562 #if !HAVE_WCSLEN
563 size_t wcslen(const wchar_t *s)
564 {
565         size_t len = 0;
566
567         while (*s != L'\0')
568                 ++len, ++s;
569
570         return len;
571 }
572 #endif
573
574 #if !HAVE_WCSCPY
575 /* Copy SRC to DEST.  */
576 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
577 {
578         wint_t c;
579         wchar_t *s = dest;
580
581         do {
582                 c = *src++;
583                 *dest++ = c;
584         } while (c != L'\0');
585
586         return s;
587 }
588 #endif
589
590 #if !HAVE_WCSNCPY
591 /* Copy no more than N wide-characters of SRC to DEST.  */
592 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
593 {
594         wint_t c;
595         wchar_t *s = dest;
596
597         do {
598                 c = *src++;
599                 *dest++ = c;
600                 if (--n == 0)
601                         return s;
602         } while (c != L'\0');
603
604         /* zero fill */
605         do
606                 *dest++ = L'\0';
607         while (--n > 0);
608
609         return s;
610 }
611 #endif
612
613 /* Duplicate S, returning an identical malloc'd string. */
614 wchar_t *wcsdup(const wchar_t *s)
615 {
616         wchar_t *new_str;
617
618         if (s) {
619                 new_str = g_new(wchar_t, wcslen(s) + 1);
620                 wcscpy(new_str, s);
621         } else
622                 new_str = NULL;
623
624         return new_str;
625 }
626
627 /* Duplicate no more than N wide-characters of S,
628    returning an identical malloc'd string. */
629 wchar_t *wcsndup(const wchar_t *s, size_t n)
630 {
631         wchar_t *new_str;
632
633         if (s) {
634                 new_str = g_new(wchar_t, n + 1);
635                 wcsncpy(new_str, s, n);
636                 new_str[n] = (wchar_t)0;
637         } else
638                 new_str = NULL;
639
640         return new_str;
641 }
642
643 wchar_t *strdup_mbstowcs(const gchar *s)
644 {
645         wchar_t *new_str;
646
647         if (s) {
648                 new_str = g_new(wchar_t, strlen(s) + 1);
649                 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
650                         g_free(new_str);
651                         new_str = NULL;
652                 } else
653                         new_str = g_realloc(new_str,
654                                             sizeof(wchar_t) * (wcslen(new_str) + 1));
655         } else
656                 new_str = NULL;
657
658         return new_str;
659 }
660
661 gchar *strdup_wcstombs(const wchar_t *s)
662 {
663         gchar *new_str;
664         size_t len;
665
666         if (s) {
667                 len = wcslen(s) * MB_CUR_MAX + 1;
668                 new_str = g_new(gchar, len);
669                 if (wcstombs(new_str, s, len) < 0) {
670                         g_free(new_str);
671                         new_str = NULL;
672                 } else
673                         new_str = g_realloc(new_str, strlen(new_str) + 1);
674         } else
675                 new_str = NULL;
676
677         return new_str;
678 }
679
680 /* Compare S1 and S2, ignoring case.  */
681 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
682 {
683         wint_t c1;
684         wint_t c2;
685
686         while (n--) {
687                 c1 = towlower(*s1++);
688                 c2 = towlower(*s2++);
689                 if (c1 != c2)
690                         return c1 - c2;
691                 else if (c1 == 0 && c2 == 0)
692                         break;
693         }
694
695         return 0;
696 }
697
698 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
699 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
700 {
701         register size_t haystack_len, needle_len;
702
703         haystack_len = wcslen(haystack);
704         needle_len   = wcslen(needle);
705
706         if (haystack_len < needle_len || needle_len == 0)
707                 return NULL;
708
709         while (haystack_len >= needle_len) {
710                 if (!wcsncasecmp(haystack, needle, needle_len))
711                         return (wchar_t *)haystack;
712                 else {
713                         haystack++;
714                         haystack_len--;
715                 }
716         }
717
718         return NULL;
719 }
720
721 gint get_mbs_len(const gchar *s)
722 {
723         const gchar *p = s;
724         gint mb_len;
725         gint len = 0;
726
727         if (!p)
728                 return -1;
729
730         while (*p != '\0') {
731                 mb_len = g_utf8_skip[*(guchar *)p];
732                 if (mb_len == 0)
733                         break;
734                 else
735                         len++;
736
737                 p += mb_len;
738         }
739
740         return len;
741 }
742
743 /* Examine if next block is non-ASCII string */
744 gboolean is_next_nonascii(const gchar *s)
745 {
746         const gchar *p;
747
748         /* skip head space */
749         for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
750                 ;
751         for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
752                 if (*(guchar *)p > 127 || *(guchar *)p < 32)
753                         return TRUE;
754         }
755
756         return FALSE;
757 }
758
759 gint get_next_word_len(const gchar *s)
760 {
761         gint len = 0;
762
763         for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
764                 ;
765
766         return len;
767 }
768
769 /* compare subjects */
770 gint subject_compare(const gchar *s1, const gchar *s2)
771 {
772         gchar *str1, *str2;
773
774         if (!s1 || !s2) return -1;
775         if (!*s1 || !*s2) return -1;
776
777         Xstrdup_a(str1, s1, return -1);
778         Xstrdup_a(str2, s2, return -1);
779
780         trim_subject_for_compare(str1);
781         trim_subject_for_compare(str2);
782
783         if (!*str1 || !*str2) return -1;
784
785         return strcmp(str1, str2);
786 }
787
788 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
789 {
790         gchar *str1, *str2;
791
792         if (!s1 || !s2) return -1;
793
794         Xstrdup_a(str1, s1, return -1);
795         Xstrdup_a(str2, s2, return -1);
796
797         trim_subject_for_sort(str1);
798         trim_subject_for_sort(str2);
799
800         return g_utf8_collate(str1, str2);
801 }
802
803 void trim_subject_for_compare(gchar *str)
804 {
805         gchar *srcp;
806
807         eliminate_parenthesis(str, '[', ']');
808         eliminate_parenthesis(str, '(', ')');
809         g_strstrip(str);
810
811         srcp = str + subject_get_prefix_length(str);
812         if (srcp != str)
813                 memmove(str, srcp, strlen(srcp) + 1);
814 }
815
816 void trim_subject_for_sort(gchar *str)
817 {
818         gchar *srcp;
819
820         g_strstrip(str);
821
822         srcp = str + subject_get_prefix_length(str);
823         if (srcp != str)
824                 memmove(str, srcp, strlen(srcp) + 1);
825 }
826
827 void trim_subject(gchar *str)
828 {
829         register gchar *srcp;
830         gchar op, cl;
831         gint in_brace;
832
833         g_strstrip(str);
834
835         srcp = str + subject_get_prefix_length(str);
836
837         if (*srcp == '[') {
838                 op = '[';
839                 cl = ']';
840         } else if (*srcp == '(') {
841                 op = '(';
842                 cl = ')';
843         } else
844                 op = 0;
845
846         if (op) {
847                 ++srcp;
848                 in_brace = 1;
849                 while (*srcp) {
850                         if (*srcp == op)
851                                 in_brace++;
852                         else if (*srcp == cl)
853                                 in_brace--;
854                         srcp++;
855                         if (in_brace == 0)
856                                 break;
857                 }
858         }
859         while (g_ascii_isspace(*srcp)) srcp++;
860         memmove(str, srcp, strlen(srcp) + 1);
861 }
862
863 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
864 {
865         register gchar *srcp, *destp;
866         gint in_brace;
867
868         srcp = destp = str;
869
870         while ((destp = strchr(destp, op))) {
871                 in_brace = 1;
872                 srcp = destp + 1;
873                 while (*srcp) {
874                         if (*srcp == op)
875                                 in_brace++;
876                         else if (*srcp == cl)
877                                 in_brace--;
878                         srcp++;
879                         if (in_brace == 0)
880                                 break;
881                 }
882                 while (g_ascii_isspace(*srcp)) srcp++;
883                 memmove(destp, srcp, strlen(srcp) + 1);
884         }
885 }
886
887 void extract_parenthesis(gchar *str, gchar op, gchar cl)
888 {
889         register gchar *srcp, *destp;
890         gint in_brace;
891
892         srcp = destp = str;
893
894         while ((srcp = strchr(destp, op))) {
895                 if (destp > str)
896                         *destp++ = ' ';
897                 memmove(destp, srcp + 1, strlen(srcp));
898                 in_brace = 1;
899                 while(*destp) {
900                         if (*destp == op)
901                                 in_brace++;
902                         else if (*destp == cl)
903                                 in_brace--;
904
905                         if (in_brace == 0)
906                                 break;
907
908                         destp++;
909                 }
910         }
911         *destp = '\0';
912 }
913
914 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
915                                          gchar op, gchar cl)
916 {
917         register gchar *srcp, *destp;
918         gint in_brace;
919         gboolean in_quote = FALSE;
920
921         srcp = destp = str;
922
923         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
924                 if (destp > str)
925                         *destp++ = ' ';
926                 memmove(destp, srcp + 1, strlen(srcp));
927                 in_brace = 1;
928                 while(*destp) {
929                         if (*destp == op && !in_quote)
930                                 in_brace++;
931                         else if (*destp == cl && !in_quote)
932                                 in_brace--;
933                         else if (*destp == quote_chr)
934                                 in_quote ^= TRUE;
935
936                         if (in_brace == 0)
937                                 break;
938
939                         destp++;
940                 }
941         }
942         *destp = '\0';
943 }
944
945 void eliminate_quote(gchar *str, gchar quote_chr)
946 {
947         register gchar *srcp, *destp;
948
949         srcp = destp = str;
950
951         while ((destp = strchr(destp, quote_chr))) {
952                 if ((srcp = strchr(destp + 1, quote_chr))) {
953                         srcp++;
954                         while (g_ascii_isspace(*srcp)) srcp++;
955                         memmove(destp, srcp, strlen(srcp) + 1);
956                 } else {
957                         *destp = '\0';
958                         break;
959                 }
960         }
961 }
962
963 void extract_quote(gchar *str, gchar quote_chr)
964 {
965         register gchar *p;
966
967         if ((str = strchr(str, quote_chr))) {
968                 p = str;
969                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
970                         memmove(p - 1, p, strlen(p) + 1);
971                         p--;
972                 }
973                 if(p) {
974                         *p = '\0';
975                         memmove(str, str + 1, p - str);
976                 }
977         }
978 }
979
980 void eliminate_address_comment(gchar *str)
981 {
982         register gchar *srcp, *destp;
983         gint in_brace;
984
985         srcp = destp = str;
986
987         while ((destp = strchr(destp, '"'))) {
988                 if ((srcp = strchr(destp + 1, '"'))) {
989                         srcp++;
990                         if (*srcp == '@') {
991                                 destp = srcp + 1;
992                         } else {
993                                 while (g_ascii_isspace(*srcp)) srcp++;
994                                 memmove(destp, srcp, strlen(srcp) + 1);
995                         }
996                 } else {
997                         *destp = '\0';
998                         break;
999                 }
1000         }
1001
1002         srcp = destp = str;
1003
1004         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
1005                 in_brace = 1;
1006                 srcp = destp + 1;
1007                 while (*srcp) {
1008                         if (*srcp == '(')
1009                                 in_brace++;
1010                         else if (*srcp == ')')
1011                                 in_brace--;
1012                         srcp++;
1013                         if (in_brace == 0)
1014                                 break;
1015                 }
1016                 while (g_ascii_isspace(*srcp)) srcp++;
1017                 memmove(destp, srcp, strlen(srcp) + 1);
1018         }
1019 }
1020
1021 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1022 {
1023         gboolean in_quote = FALSE;
1024
1025         while (*str) {
1026                 if (*str == c && !in_quote)
1027                         return (gchar *)str;
1028                 if (*str == quote_chr)
1029                         in_quote ^= TRUE;
1030                 str++;
1031         }
1032
1033         return NULL;
1034 }
1035
1036 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1037 {
1038         gboolean in_quote = FALSE;
1039         const gchar *p;
1040
1041         p = str + strlen(str) - 1;
1042         while (p >= str) {
1043                 if (*p == c && !in_quote)
1044                         return (gchar *)p;
1045                 if (*p == quote_chr)
1046                         in_quote ^= TRUE;
1047                 p--;
1048         }
1049
1050         return NULL;
1051 }
1052
1053 void extract_address(gchar *str)
1054 {
1055         eliminate_address_comment(str);
1056         if (strchr_with_skip_quote(str, '"', '<'))
1057                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1058         g_strstrip(str);
1059 }
1060
1061 void extract_list_id_str(gchar *str)
1062 {
1063         if (strchr_with_skip_quote(str, '"', '<'))
1064                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1065         g_strstrip(str);
1066 }
1067
1068 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1069 {
1070         gchar *work;
1071         gchar *workp;
1072
1073         if (!str) return addr_list;
1074
1075         Xstrdup_a(work, str, return addr_list);
1076
1077         if (removecomments)
1078                 eliminate_address_comment(work);
1079         workp = work;
1080
1081         while (workp && *workp) {
1082                 gchar *p, *next;
1083
1084                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1085                         *p = '\0';
1086                         next = p + 1;
1087                 } else
1088                         next = NULL;
1089
1090                 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1091                         extract_parenthesis_with_skip_quote
1092                                 (workp, '"', '<', '>');
1093
1094                 g_strstrip(workp);
1095                 if (*workp)
1096                         addr_list = g_slist_append(addr_list, g_strdup(workp));
1097
1098                 workp = next;
1099         }
1100
1101         return addr_list;
1102 }
1103
1104 GSList *address_list_append(GSList *addr_list, const gchar *str)
1105 {
1106         return address_list_append_real(addr_list, str, TRUE);
1107 }
1108
1109 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1110 {
1111         return address_list_append_real(addr_list, str, FALSE);
1112 }
1113
1114 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1115 {
1116         const gchar *strp;
1117
1118         if (!str) return msgid_list;
1119         strp = str;
1120
1121         while (strp && *strp) {
1122                 const gchar *start, *end;
1123                 gchar *msgid;
1124
1125                 if ((start = strchr(strp, '<')) != NULL) {
1126                         end = strchr(start + 1, '>');
1127                         if (!end) break;
1128                 } else
1129                         break;
1130
1131                 msgid = g_strndup(start + 1, end - start - 1);
1132                 g_strstrip(msgid);
1133                 if (*msgid)
1134                         msgid_list = g_slist_prepend(msgid_list, msgid);
1135                 else
1136                         g_free(msgid);
1137
1138                 strp = end + 1;
1139         }
1140
1141         return msgid_list;
1142 }
1143
1144 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1145 {
1146         GSList *list;
1147
1148         list = references_list_prepend(NULL, str);
1149         list = g_slist_reverse(list);
1150         msgid_list = g_slist_concat(msgid_list, list);
1151
1152         return msgid_list;
1153 }
1154
1155 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1156 {
1157         gchar *work;
1158         gchar *workp;
1159
1160         if (!str) return group_list;
1161
1162         Xstrdup_a(work, str, return group_list);
1163
1164         workp = work;
1165
1166         while (workp && *workp) {
1167                 gchar *p, *next;
1168
1169                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1170                         *p = '\0';
1171                         next = p + 1;
1172                 } else
1173                         next = NULL;
1174
1175                 g_strstrip(workp);
1176                 if (*workp)
1177                         group_list = g_slist_append(group_list,
1178                                                     g_strdup(workp));
1179
1180                 workp = next;
1181         }
1182
1183         return group_list;
1184 }
1185
1186 GList *add_history(GList *list, const gchar *str)
1187 {
1188         GList *old;
1189
1190         g_return_val_if_fail(str != NULL, list);
1191
1192         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1193         if (old) {
1194                 g_free(old->data);
1195                 list = g_list_remove(list, old->data);
1196         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1197                 GList *last;
1198
1199                 last = g_list_last(list);
1200                 if (last) {
1201                         g_free(last->data);
1202                         list = g_list_remove(list, last->data);
1203                 }
1204         }
1205
1206         list = g_list_prepend(list, g_strdup(str));
1207
1208         return list;
1209 }
1210
1211 void remove_return(gchar *str)
1212 {
1213         register gchar *p = str;
1214
1215         while (*p) {
1216                 if (*p == '\n' || *p == '\r')
1217                         memmove(p, p + 1, strlen(p));
1218                 else
1219                         p++;
1220         }
1221 }
1222
1223 void remove_space(gchar *str)
1224 {
1225         register gchar *p = str;
1226         register gint spc;
1227
1228         while (*p) {
1229                 spc = 0;
1230                 while (g_ascii_isspace(*(p + spc)))
1231                         spc++;
1232                 if (spc)
1233                         memmove(p, p + spc, strlen(p + spc) + 1);
1234                 else
1235                         p++;
1236         }
1237 }
1238
1239 void unfold_line(gchar *str)
1240 {
1241         register gchar *p = str;
1242         register gint spc;
1243
1244         while (*p) {
1245                 if (*p == '\n' || *p == '\r') {
1246                         *p++ = ' ';
1247                         spc = 0;
1248                         while (g_ascii_isspace(*(p + spc)))
1249                                 spc++;
1250                         if (spc)
1251                                 memmove(p, p + spc, strlen(p + spc) + 1);
1252                 } else
1253                         p++;
1254         }
1255 }
1256
1257 void subst_char(gchar *str, gchar orig, gchar subst)
1258 {
1259         register gchar *p = str;
1260
1261         while (*p) {
1262                 if (*p == orig)
1263                         *p = subst;
1264                 p++;
1265         }
1266 }
1267
1268 void subst_chars(gchar *str, gchar *orig, gchar subst)
1269 {
1270         register gchar *p = str;
1271
1272         while (*p) {
1273                 if (strchr(orig, *p) != NULL)
1274                         *p = subst;
1275                 p++;
1276         }
1277 }
1278
1279 void subst_for_filename(gchar *str)
1280 {
1281         if (!str)
1282                 return;
1283 #ifdef G_OS_WIN32
1284         subst_chars(str, "\t\r\n\\/*:", '_');
1285 #else
1286         subst_chars(str, "\t\r\n\\/*", '_');
1287 #endif
1288 }
1289
1290 void subst_for_shellsafe_filename(gchar *str)
1291 {
1292         if (!str)
1293                 return;
1294         subst_for_filename(str);
1295         subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1296 }
1297
1298 gboolean is_header_line(const gchar *str)
1299 {
1300         if (str[0] == ':') return FALSE;
1301
1302         while (*str != '\0' && *str != ' ') {
1303                 if (*str == ':')
1304                         return TRUE;
1305                 str++;
1306         }
1307
1308         return FALSE;
1309 }
1310
1311 gboolean is_ascii_str(const gchar *str)
1312 {
1313         const guchar *p = (const guchar *)str;
1314
1315         while (*p != '\0') {
1316                 if (*p != '\t' && *p != ' ' &&
1317                     *p != '\r' && *p != '\n' &&
1318                     (*p < 32 || *p >= 127))
1319                         return FALSE;
1320                 p++;
1321         }
1322
1323         return TRUE;
1324 }
1325
1326 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1327 {
1328         const gchar *first_pos;
1329         const gchar *last_pos;
1330         const gchar *p = str;
1331         gint quote_level = -1;
1332
1333         /* speed up line processing by only searching to the last '>' */
1334         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1335                 /* skip a line if it contains a '<' before the initial '>' */
1336                 if (memchr(str, '<', first_pos - str) != NULL)
1337                         return -1;
1338                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1339         } else
1340                 return -1;
1341
1342         while (p <= last_pos) {
1343                 while (p < last_pos) {
1344                         if (g_ascii_isspace(*p))
1345                                 p++;
1346                         else
1347                                 break;
1348                 }
1349
1350                 if (strchr(quote_chars, *p))
1351                         quote_level++;
1352                 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1353                         /* any characters are allowed except '-' and space */
1354                         while (*p != '-'
1355                                && !strchr(quote_chars, *p)
1356                                && !g_ascii_isspace(*p)
1357                                && p < last_pos)
1358                                 p++;
1359                         if (strchr(quote_chars, *p))
1360                                 quote_level++;
1361                         else
1362                                 break;
1363                 }
1364
1365                 p++;
1366         }
1367
1368         return quote_level;
1369 }
1370
1371 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1372 {
1373         const gchar *p = str, *q;
1374         gint cur_line = 0, len;
1375
1376         while ((q = strchr(p, '\n')) != NULL) {
1377                 len = q - p + 1;
1378                 if (len > max_chars) {
1379                         if (line)
1380                                 *line = cur_line;
1381                         return -1;
1382                 }
1383                 p = q + 1;
1384                 ++cur_line;
1385         }
1386
1387         len = strlen(p);
1388         if (len > max_chars) {
1389                 if (line)
1390                         *line = cur_line;
1391                 return -1;
1392         }
1393
1394         return 0;
1395 }
1396
1397 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1398 {
1399         gchar * position = NULL;
1400         gchar * tmp_pos = NULL;
1401         int i;
1402
1403         if (quote_chars == NULL)
1404                 return FALSE;
1405
1406         for (i = 0; i < strlen(quote_chars); i++) {
1407                 tmp_pos = strchr (str,  quote_chars[i]);
1408                 if(position == NULL
1409                    || (tmp_pos != NULL && position >= tmp_pos) )
1410                         position = tmp_pos;
1411         }
1412         return position;
1413 }
1414
1415 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1416 {
1417         gchar * position = NULL;
1418         gchar * tmp_pos = NULL;
1419         int i;
1420
1421         if (quote_chars == NULL)
1422                 return FALSE;
1423
1424         for (i = 0; i < strlen(quote_chars); i++) {
1425                 tmp_pos = strrchr (str, quote_chars[i]);
1426                 if(position == NULL
1427                    || (tmp_pos != NULL && position <= tmp_pos) )
1428                         position = tmp_pos;
1429         }
1430         return position;
1431 }
1432
1433 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1434 {
1435         register guint haystack_len, needle_len;
1436         gboolean in_squote = FALSE, in_dquote = FALSE;
1437
1438         haystack_len = strlen(haystack);
1439         needle_len   = strlen(needle);
1440
1441         if (haystack_len < needle_len || needle_len == 0)
1442                 return NULL;
1443
1444         while (haystack_len >= needle_len) {
1445                 if (!in_squote && !in_dquote &&
1446                     !strncmp(haystack, needle, needle_len))
1447                         return (gchar *)haystack;
1448
1449                 /* 'foo"bar"' -> foo"bar"
1450                    "foo'bar'" -> foo'bar' */
1451                 if (*haystack == '\'') {
1452                         if (in_squote)
1453                                 in_squote = FALSE;
1454                         else if (!in_dquote)
1455                                 in_squote = TRUE;
1456                 } else if (*haystack == '\"') {
1457                         if (in_dquote)
1458                                 in_dquote = FALSE;
1459                         else if (!in_squote)
1460                                 in_dquote = TRUE;
1461                 }
1462
1463                 haystack++;
1464                 haystack_len--;
1465         }
1466
1467         return NULL;
1468 }
1469
1470 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1471 {
1472         const gchar *p;
1473         gchar quote_chr = '"';
1474         gint in_brace;
1475         gboolean in_quote = FALSE;
1476
1477         p = str;
1478
1479         if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1480                 p++;
1481                 in_brace = 1;
1482                 while (*p) {
1483                         if (*p == op && !in_quote)
1484                                 in_brace++;
1485                         else if (*p == cl && !in_quote)
1486                                 in_brace--;
1487                         else if (*p == quote_chr)
1488                                 in_quote ^= TRUE;
1489
1490                         if (in_brace == 0)
1491                                 return (gchar *)p;
1492
1493                         p++;
1494                 }
1495         }
1496
1497         return NULL;
1498 }
1499
1500 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1501                              gint max_tokens)
1502 {
1503         GSList *string_list = NULL, *slist;
1504         gchar **str_array;
1505         const gchar *s_op, *s_cl;
1506         guint i, n = 1;
1507
1508         g_return_val_if_fail(str != NULL, NULL);
1509
1510         if (max_tokens < 1)
1511                 max_tokens = G_MAXINT;
1512
1513         s_op = strchr_with_skip_quote(str, '"', op);
1514         if (!s_op) return NULL;
1515         str = s_op;
1516         s_cl = strchr_parenthesis_close(str, op, cl);
1517         if (s_cl) {
1518                 do {
1519                         guint len;
1520                         gchar *new_string;
1521
1522                         str++;
1523                         len = s_cl - str;
1524                         new_string = g_new(gchar, len + 1);
1525                         strncpy(new_string, str, len);
1526                         new_string[len] = 0;
1527                         string_list = g_slist_prepend(string_list, new_string);
1528                         n++;
1529                         str = s_cl + 1;
1530
1531                         while (*str && g_ascii_isspace(*str)) str++;
1532                         if (*str != op) {
1533                                 string_list = g_slist_prepend(string_list,
1534                                                               g_strdup(""));
1535                                 n++;
1536                                 s_op = strchr_with_skip_quote(str, '"', op);
1537                                 if (!--max_tokens || !s_op) break;
1538                                 str = s_op;
1539                         } else
1540                                 s_op = str;
1541                         s_cl = strchr_parenthesis_close(str, op, cl);
1542                 } while (--max_tokens && s_cl);
1543         }
1544
1545         str_array = g_new(gchar*, n);
1546
1547         i = n - 1;
1548
1549         str_array[i--] = NULL;
1550         for (slist = string_list; slist; slist = slist->next)
1551                 str_array[i--] = slist->data;
1552
1553         g_slist_free(string_list);
1554
1555         return str_array;
1556 }
1557
1558 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1559                             gint max_tokens)
1560 {
1561         GSList *string_list = NULL, *slist;
1562         gchar **str_array, *s, *new_str;
1563         guint i, n = 1, len;
1564
1565         g_return_val_if_fail(str != NULL, NULL);
1566         g_return_val_if_fail(delim != NULL, NULL);
1567
1568         if (max_tokens < 1)
1569                 max_tokens = G_MAXINT;
1570
1571         s = strstr_with_skip_quote(str, delim);
1572         if (s) {
1573                 guint delimiter_len = strlen(delim);
1574
1575                 do {
1576                         len = s - str;
1577                         new_str = g_strndup(str, len);
1578
1579                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1580                                 if (new_str[len - 1] == new_str[0]) {
1581                                         new_str[len - 1] = '\0';
1582                                         memmove(new_str, new_str + 1, len - 1);
1583                                 }
1584                         }
1585                         string_list = g_slist_prepend(string_list, new_str);
1586                         n++;
1587                         str = s + delimiter_len;
1588                         s = strstr_with_skip_quote(str, delim);
1589                 } while (--max_tokens && s);
1590         }
1591
1592         if (*str) {
1593                 new_str = g_strdup(str);
1594                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1595                         len = strlen(str);
1596                         if (new_str[len - 1] == new_str[0]) {
1597                                 new_str[len - 1] = '\0';
1598                                 memmove(new_str, new_str + 1, len - 1);
1599                         }
1600                 }
1601                 string_list = g_slist_prepend(string_list, new_str);
1602                 n++;
1603         }
1604
1605         str_array = g_new(gchar*, n);
1606
1607         i = n - 1;
1608
1609         str_array[i--] = NULL;
1610         for (slist = string_list; slist; slist = slist->next)
1611                 str_array[i--] = slist->data;
1612
1613         g_slist_free(string_list);
1614
1615         return str_array;
1616 }
1617
1618 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1619 {
1620         gchar *abbrev_group;
1621         gchar *ap;
1622         const gchar *p = group;
1623         const gchar *last;
1624
1625         g_return_val_if_fail(group != NULL, NULL);
1626
1627         last = group + strlen(group);
1628         abbrev_group = ap = g_malloc(strlen(group) + 1);
1629
1630         while (*p) {
1631                 while (*p == '.')
1632                         *ap++ = *p++;
1633                 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1634                         *ap++ = *p++;
1635                         while (*p != '.') p++;
1636                 } else {
1637                         strcpy(ap, p);
1638                         return abbrev_group;
1639                 }
1640         }
1641
1642         *ap = '\0';
1643         return abbrev_group;
1644 }
1645
1646 gchar *trim_string(const gchar *str, gint len)
1647 {
1648         const gchar *p = str;
1649         gint mb_len;
1650         gchar *new_str;
1651         gint new_len = 0;
1652
1653         if (!str) return NULL;
1654         if (strlen(str) <= len)
1655                 return g_strdup(str);
1656         if (g_utf8_validate(str, -1, NULL) == FALSE)
1657                 return g_strdup(str);
1658
1659         while (*p != '\0') {
1660                 mb_len = g_utf8_skip[*(guchar *)p];
1661                 if (mb_len == 0)
1662                         break;
1663                 else if (new_len + mb_len > len)
1664                         break;
1665
1666                 new_len += mb_len;
1667                 p += mb_len;
1668         }
1669
1670         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1671         return g_strconcat(new_str, "...", NULL);
1672 }
1673
1674 GList *uri_list_extract_filenames(const gchar *uri_list)
1675 {
1676         GList *result = NULL;
1677         const gchar *p, *q;
1678         gchar *escaped_utf8uri;
1679
1680         p = uri_list;
1681
1682         while (p) {
1683                 if (*p != '#') {
1684                         while (g_ascii_isspace(*p)) p++;
1685                         if (!strncmp(p, "file:", 5)) {
1686                                 q = p;
1687                                 q += 5;
1688                                 while (*q && *q != '\n' && *q != '\r') q++;
1689
1690                                 if (q > p) {
1691                                         gchar *file, *locale_file = NULL;
1692                                         q--;
1693                                         while (q > p && g_ascii_isspace(*q))
1694                                                 q--;
1695                                         Xalloca(escaped_utf8uri, q - p + 2,
1696                                                 return result);
1697                                         Xalloca(file, q - p + 2,
1698                                                 return result);
1699                                         *file = '\0';
1700                                         strncpy(escaped_utf8uri, p, q - p + 1);
1701                                         escaped_utf8uri[q - p + 1] = '\0';
1702                                         decode_uri(file, escaped_utf8uri);
1703                     /*
1704                      * g_filename_from_uri() rejects escaped/locale encoded uri
1705                      * string which come from Nautilus.
1706                      */
1707                                         if (g_utf8_validate(file, -1, NULL))
1708                                                 locale_file
1709                                                         = conv_codeset_strdup(
1710                                                                 file + 5,
1711                                                                 CS_UTF_8,
1712                                                                 conv_get_locale_charset_str());
1713                                         if (!locale_file)
1714                                                 locale_file = g_strdup(file + 5);
1715                                         result = g_list_append(result, locale_file);
1716                                 }
1717                         }
1718                 }
1719                 p = strchr(p, '\n');
1720                 if (p) p++;
1721         }
1722
1723         return result;
1724 }
1725
1726 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped
1727  * characters
1728  */
1729 static gint axtoi(const gchar *hexstr)
1730 {
1731         gint hi, lo, result;
1732
1733         hi = hexstr[0];
1734         if ('0' <= hi && hi <= '9') {
1735                 hi -= '0';
1736         } else
1737                 if ('a' <= hi && hi <= 'f') {
1738                         hi -= ('a' - 10);
1739                 } else
1740                         if ('A' <= hi && hi <= 'F') {
1741                                 hi -= ('A' - 10);
1742                         }
1743
1744         lo = hexstr[1];
1745         if ('0' <= lo && lo <= '9') {
1746                 lo -= '0';
1747         } else
1748                 if ('a' <= lo && lo <= 'f') {
1749                         lo -= ('a'-10);
1750                 } else
1751                         if ('A' <= lo && lo <= 'F') {
1752                                 lo -= ('A' - 10);
1753                         }
1754         result = lo + (16 * hi);
1755         return result;
1756 }
1757
1758 gboolean is_uri_string(const gchar *str)
1759 {
1760         while (str && *str && g_ascii_isspace(*str))
1761                 str++;
1762         return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1763                 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1764                 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1765                 g_ascii_strncasecmp(str, "www.", 4) == 0);
1766 }
1767
1768 gchar *get_uri_path(const gchar *uri)
1769 {
1770         while (uri && *uri && g_ascii_isspace(*uri))
1771                 uri++;
1772         if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1773                 return (gchar *)(uri + 7);
1774         else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1775                 return (gchar *)(uri + 8);
1776         else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1777                 return (gchar *)(uri + 6);
1778         else
1779                 return (gchar *)uri;
1780 }
1781
1782 gint get_uri_len(const gchar *str)
1783 {
1784         const gchar *p;
1785
1786         if (is_uri_string(str)) {
1787                 for (p = str; *p != '\0'; p++) {
1788                         if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1789                                 break;
1790                 }
1791                 return p - str;
1792         }
1793
1794         return 0;
1795 }
1796
1797 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1798  * plusses, and escape characters are used)
1799  */
1800 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1801 {
1802         gchar *dec = decoded_uri;
1803         const gchar *enc = encoded_uri;
1804
1805         while (*enc) {
1806                 if (*enc == '%') {
1807                         enc++;
1808                         if (isxdigit((guchar)enc[0]) &&
1809                             isxdigit((guchar)enc[1])) {
1810                                 *dec = axtoi(enc);
1811                                 dec++;
1812                                 enc += 2;
1813                         }
1814                 } else {
1815                         if (*enc == '+')
1816                                 *dec = ' ';
1817                         else
1818                                 *dec = *enc;
1819                         dec++;
1820                         enc++;
1821                 }
1822         }
1823
1824         *dec = '\0';
1825 }
1826
1827 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1828                      gchar **subject, gchar **body)
1829 {
1830         gchar *tmp_mailto;
1831         gchar *p;
1832
1833         Xstrdup_a(tmp_mailto, mailto, return -1);
1834
1835         if (!strncmp(tmp_mailto, "mailto:", 7))
1836                 tmp_mailto += 7;
1837
1838         p = strchr(tmp_mailto, '?');
1839         if (p) {
1840                 *p = '\0';
1841                 p++;
1842         }
1843
1844         if (to && !*to)
1845                 *to = g_strdup(tmp_mailto);
1846
1847         while (p) {
1848                 gchar *field, *value;
1849
1850                 field = p;
1851
1852                 p = strchr(p, '=');
1853                 if (!p) break;
1854                 *p = '\0';
1855                 p++;
1856
1857                 value = p;
1858
1859                 p = strchr(p, '&');
1860                 if (p) {
1861                         *p = '\0';
1862                         p++;
1863                 }
1864
1865                 if (*value == '\0') continue;
1866
1867                 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1868                         *cc = g_strdup(value);
1869                 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1870                         *bcc = g_strdup(value);
1871                 } else if (subject && !*subject &&
1872                            !g_ascii_strcasecmp(field, "subject")) {
1873                         *subject = g_malloc(strlen(value) + 1);
1874                         decode_uri(*subject, value);
1875                 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1876                         *body = g_malloc(strlen(value) + 1);
1877                         decode_uri(*body, value);
1878                 }
1879         }
1880
1881         return 0;
1882 }
1883
1884
1885 #ifdef G_OS_WIN32
1886 #include <windows.h>
1887 #ifndef CSIDL_APPDATA
1888 #define CSIDL_APPDATA 0x001a
1889 #endif
1890 #ifndef CSIDL_LOCAL_APPDATA
1891 #define CSIDL_LOCAL_APPDATA 0x001c
1892 #endif
1893 #ifndef CSIDL_FLAG_CREATE
1894 #define CSIDL_FLAG_CREATE 0x8000
1895 #endif
1896 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1897
1898 #define RTLD_LAZY 0
1899 const char *
1900 w32_strerror (int w32_errno)
1901 {
1902   static char strerr[256];
1903   int ec = (int)GetLastError ();
1904
1905   if (w32_errno == 0)
1906     w32_errno = ec;
1907   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1908                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1909                  strerr, DIM (strerr)-1, NULL);
1910   return strerr;
1911 }
1912
1913 static __inline__ void *
1914 dlopen (const char * name, int flag)
1915 {
1916   void * hd = LoadLibrary (name);
1917   return hd;
1918 }
1919
1920 static __inline__ void *
1921 dlsym (void * hd, const char * sym)
1922 {
1923   if (hd && sym)
1924     {
1925       void * fnc = GetProcAddress (hd, sym);
1926       if (!fnc)
1927         return NULL;
1928       return fnc;
1929     }
1930   return NULL;
1931 }
1932
1933
1934 static __inline__ const char *
1935 dlerror (void)
1936 {
1937   return w32_strerror (0);
1938 }
1939
1940
1941 static __inline__ int
1942 dlclose (void * hd)
1943 {
1944   if (hd)
1945     {
1946       FreeLibrary (hd);
1947       return 0;
1948     }
1949   return -1;
1950 }
1951
1952 static HRESULT
1953 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1954 {
1955   static int initialized;
1956   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1957
1958   if (!initialized)
1959     {
1960       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1961       void *handle;
1962       int i;
1963
1964       initialized = 1;
1965
1966       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1967         {
1968           handle = dlopen (dllnames[i], RTLD_LAZY);
1969           if (handle)
1970             {
1971               func = dlsym (handle, "SHGetFolderPathA");
1972               if (!func)
1973                 {
1974                   dlclose (handle);
1975                   handle = NULL;
1976                 }
1977             }
1978         }
1979     }
1980
1981   if (func)
1982     return func (a,b,c,d,e);
1983   else
1984     return -1;
1985 }
1986
1987 /* Returns a static string with the directroy from which the module
1988    has been loaded.  Returns an empty string on error. */
1989 static char *w32_get_module_dir(void)
1990 {
1991         static char *moddir;
1992
1993         if (!moddir) {
1994                 char name[MAX_PATH+10];
1995                 char *p;
1996
1997                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1998                         *name = 0;
1999                 else {
2000                         p = strrchr (name, '\\');
2001                         if (p)
2002                                 *p = 0;
2003                         else
2004                                 *name = 0;
2005                 }
2006                 moddir = g_strdup (name);
2007         }
2008         return moddir;
2009 }
2010 #endif /* G_OS_WIN32 */
2011
2012 /* Return a static string with the locale dir. */
2013 const gchar *get_locale_dir(void)
2014 {
2015         static gchar *loc_dir;
2016
2017 #ifdef G_OS_WIN32
2018         if (!loc_dir)
2019                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
2020                                       "\\share\\locale", NULL);
2021 #endif
2022         if (!loc_dir)
2023                 loc_dir = LOCALEDIR;
2024         
2025         return loc_dir;
2026 }
2027
2028
2029 const gchar *get_home_dir(void)
2030 {
2031 #ifdef G_OS_WIN32
2032         static char home_dir[MAX_PATH] = "";
2033
2034         if (home_dir[0] == '\0') {
2035                 if (w32_shgetfolderpath
2036                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
2037                              NULL, 0, home_dir) < 0)
2038                                 strcpy (home_dir, "C:\\Sylpheed");
2039         }
2040         return home_dir;
2041 #else
2042         static const gchar *homeenv = NULL;
2043
2044         if (homeenv)
2045                 return homeenv;
2046
2047         if (!homeenv && g_getenv("HOME") != NULL)
2048                 homeenv = g_strdup(g_getenv("HOME"));
2049         if (!homeenv)
2050                 homeenv = g_get_home_dir();
2051
2052         return homeenv;
2053 #endif
2054 }
2055
2056 const gchar *get_rc_dir(void)
2057 {
2058         static gchar *rc_dir = NULL;
2059
2060         if (!rc_dir)
2061                 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2062                                      RC_DIR, NULL);
2063
2064         return rc_dir;
2065 }
2066
2067 const gchar *get_mail_base_dir(void)
2068 {
2069 #ifdef G_OS_WIN32
2070         static gchar *mail_base_dir = NULL;
2071
2072         if (!mail_base_dir)
2073                 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2074                                             "Mailboxes", NULL);
2075
2076         return mail_base_dir;
2077 #else
2078         return get_home_dir();
2079 #endif
2080 }
2081
2082 const gchar *get_news_cache_dir(void)
2083 {
2084         static gchar *news_cache_dir = NULL;
2085
2086         if (!news_cache_dir)
2087                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2088                                              NEWS_CACHE_DIR, NULL);
2089
2090         return news_cache_dir;
2091 }
2092
2093 const gchar *get_imap_cache_dir(void)
2094 {
2095         static gchar *imap_cache_dir = NULL;
2096
2097         if (!imap_cache_dir)
2098                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2099                                              IMAP_CACHE_DIR, NULL);
2100
2101         return imap_cache_dir;
2102 }
2103
2104 const gchar *get_mbox_cache_dir(void)
2105 {
2106         static gchar *mbox_cache_dir = NULL;
2107
2108         if (!mbox_cache_dir)
2109                 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2110                                              MBOX_CACHE_DIR, NULL);
2111
2112         return mbox_cache_dir;
2113 }
2114
2115 const gchar *get_mime_tmp_dir(void)
2116 {
2117         static gchar *mime_tmp_dir = NULL;
2118
2119         if (!mime_tmp_dir)
2120                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2121                                            MIME_TMP_DIR, NULL);
2122
2123         return mime_tmp_dir;
2124 }
2125
2126 const gchar *get_template_dir(void)
2127 {
2128         static gchar *template_dir = NULL;
2129
2130         if (!template_dir)
2131                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2132                                            TEMPLATE_DIR, NULL);
2133
2134         return template_dir;
2135 }
2136
2137 const gchar *get_header_cache_dir(void)
2138 {
2139         static gchar *header_dir = NULL;
2140
2141         if (!header_dir)
2142                 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2143                                          HEADER_CACHE_DIR, NULL);
2144
2145         return header_dir;
2146 }
2147
2148 /* Return the default directory for Plugins. */
2149 const gchar *get_plugin_dir(void)
2150 {
2151 #ifdef G_OS_WIN32
2152         static gchar *plugin_dir = NULL;
2153
2154         if (!plugin_dir)
2155                 plugin_dir = g_strconcat(w32_get_module_dir(),
2156                                          "\\lib\\sylpheed-claws\\plugins\\",
2157                                          NULL);
2158         return plugin_dir;
2159 #else
2160         if (is_dir_exist(PLUGINDIR))
2161                 return PLUGINDIR;
2162         else {
2163                 static gchar *plugin_dir = NULL;
2164                 if (!plugin_dir)
2165                         plugin_dir = g_strconcat(get_rc_dir(), 
2166                                 G_DIR_SEPARATOR_S, "plugins", 
2167                                 G_DIR_SEPARATOR_S, NULL);
2168                 return plugin_dir;                      
2169         }
2170 #endif
2171 }
2172
2173 const gchar *get_tmp_dir(void)
2174 {
2175         static gchar *tmp_dir = NULL;
2176
2177         if (!tmp_dir)
2178                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2179                                       TMP_DIR, NULL);
2180
2181         return tmp_dir;
2182 }
2183
2184 gchar *get_tmp_file(void)
2185 {
2186         gchar *tmp_file;
2187         static guint32 id = 0;
2188
2189         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2190                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
2191
2192         return tmp_file;
2193 }
2194
2195 const gchar *get_domain_name(void)
2196 {
2197 #ifdef G_OS_UNIX
2198         static gchar *domain_name = NULL;
2199
2200         if (!domain_name) {
2201                 struct hostent *hp;
2202                 struct utsname uts;
2203
2204                 if (uname(&uts) < 0) {
2205                         perror("uname");
2206                         domain_name = "unknown";
2207                 } else {
2208                         if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2209                                 perror("gethostbyname");
2210                                 domain_name = g_strdup(uts.nodename);
2211                         } else {
2212                                 domain_name = g_strdup(hp->h_name);
2213                         }
2214                 }
2215
2216                 debug_print("domain name = %s\n", domain_name);
2217         }
2218
2219         return domain_name;
2220 #else
2221         return "unknown";
2222 #endif
2223 }
2224
2225 off_t get_file_size(const gchar *file)
2226 {
2227         struct stat s;
2228
2229         if (g_stat(file, &s) < 0) {
2230                 FILE_OP_ERROR(file, "stat");
2231                 return -1;
2232         }
2233
2234         return s.st_size;
2235 }
2236
2237 off_t get_file_size_as_crlf(const gchar *file)
2238 {
2239         FILE *fp;
2240         off_t size = 0;
2241         gchar buf[BUFFSIZE];
2242
2243         if ((fp = g_fopen(file, "rb")) == NULL) {
2244                 FILE_OP_ERROR(file, "fopen");
2245                 return -1;
2246         }
2247
2248         while (fgets(buf, sizeof(buf), fp) != NULL) {
2249                 strretchomp(buf);
2250                 size += strlen(buf) + 2;
2251         }
2252
2253         if (ferror(fp)) {
2254                 FILE_OP_ERROR(file, "fgets");
2255                 size = -1;
2256         }
2257
2258         fclose(fp);
2259
2260         return size;
2261 }
2262
2263 off_t get_left_file_size(FILE *fp)
2264 {
2265         glong pos;
2266         glong end;
2267         off_t size;
2268
2269         if ((pos = ftell(fp)) < 0) {
2270                 perror("ftell");
2271                 return -1;
2272         }
2273         if (fseek(fp, 0L, SEEK_END) < 0) {
2274                 perror("fseek");
2275                 return -1;
2276         }
2277         if ((end = ftell(fp)) < 0) {
2278                 perror("fseek");
2279                 return -1;
2280         }
2281         size = end - pos;
2282         if (fseek(fp, pos, SEEK_SET) < 0) {
2283                 perror("fseek");
2284                 return -1;
2285         }
2286
2287         return size;
2288 }
2289
2290 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2291 {
2292         struct stat s;
2293
2294         if (file == NULL)
2295                 return FALSE;
2296
2297         if (g_stat(file, &s) < 0) {
2298                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2299                 return FALSE;
2300         }
2301
2302         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2303                 return TRUE;
2304
2305         return FALSE;
2306 }
2307
2308
2309 /* Test on whether FILE is a relative file name. This is
2310  * straightforward for Unix but more complex for Windows. */
2311 gboolean is_relative_filename(const gchar *file)
2312 {
2313         if (!file)
2314                 return TRUE;
2315 #ifdef G_OS_WIN32
2316         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2317                 return FALSE; /* Prefixed with a hostname - this can't
2318                                * be a relative name. */
2319
2320         if ( ((*file >= 'a' && *file <= 'z')
2321               || (*file >= 'A' && *file <= 'Z'))
2322              && file[1] == ':')
2323                 file += 2;  /* Skip drive letter. */
2324
2325         return !(*file == '\\' || *file == '/');
2326 #else
2327         return !(*file == G_DIR_SEPARATOR);
2328 #endif
2329 }
2330
2331
2332 gboolean is_dir_exist(const gchar *dir)
2333 {
2334         if (dir == NULL)
2335                 return FALSE;
2336
2337         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2338 }
2339
2340 gboolean is_file_entry_exist(const gchar *file)
2341 {
2342         if (file == NULL)
2343                 return FALSE;
2344
2345         return g_file_test(file, G_FILE_TEST_EXISTS);
2346 }
2347
2348 gboolean dirent_is_regular_file(struct dirent *d)
2349 {
2350 #ifdef HAVE_DIRENT_D_TYPE
2351         if (d->d_type == DT_REG)
2352                 return TRUE;
2353         else if (d->d_type != DT_UNKNOWN)
2354                 return FALSE;
2355 #endif
2356
2357         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2358 }
2359
2360 gboolean dirent_is_directory(struct dirent *d)
2361 {
2362 #ifdef HAVE_DIRENT_D_TYPE
2363         if (d->d_type == DT_DIR)
2364                 return TRUE;
2365         else if (d->d_type != DT_UNKNOWN)
2366                 return FALSE;
2367 #endif
2368
2369         return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2370 }
2371
2372 gint change_dir(const gchar *dir)
2373 {
2374         gchar *prevdir = NULL;
2375
2376         if (debug_mode)
2377                 prevdir = g_get_current_dir();
2378
2379         if (g_chdir(dir) < 0) {
2380                 FILE_OP_ERROR(dir, "chdir");
2381                 if (debug_mode) g_free(prevdir);
2382                 return -1;
2383         } else if (debug_mode) {
2384                 gchar *cwd;
2385
2386                 cwd = g_get_current_dir();
2387                 if (strcmp(prevdir, cwd) != 0)
2388                         g_print("current dir: %s\n", cwd);
2389                 g_free(cwd);
2390                 g_free(prevdir);
2391         }
2392
2393         return 0;
2394 }
2395
2396 gint make_dir(const gchar *dir)
2397 {
2398         if (g_mkdir(dir, S_IRWXU) < 0) {
2399                 FILE_OP_ERROR(dir, "mkdir");
2400                 return -1;
2401         }
2402         if (g_chmod(dir, S_IRWXU) < 0)
2403                 FILE_OP_ERROR(dir, "chmod");
2404
2405         return 0;
2406 }
2407
2408 gint make_dir_hier(const gchar *dir)
2409 {
2410         gchar *parent_dir;
2411         const gchar *p;
2412
2413         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2414                 parent_dir = g_strndup(dir, p - dir);
2415                 if (*parent_dir != '\0') {
2416                         if (!is_dir_exist(parent_dir)) {
2417                                 if (make_dir(parent_dir) < 0) {
2418                                         g_free(parent_dir);
2419                                         return -1;
2420                                 }
2421                         }
2422                 }
2423                 g_free(parent_dir);
2424         }
2425
2426         if (!is_dir_exist(dir)) {
2427                 if (make_dir(dir) < 0)
2428                         return -1;
2429         }
2430
2431         return 0;
2432 }
2433
2434 gint remove_all_files(const gchar *dir)
2435 {
2436         GDir *dp;
2437         const gchar *dir_name;
2438         gchar *prev_dir;
2439
2440         prev_dir = g_get_current_dir();
2441
2442         if (g_chdir(dir) < 0) {
2443                 FILE_OP_ERROR(dir, "chdir");
2444                 g_free(prev_dir);
2445                 return -1;
2446         }
2447
2448         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2449                 g_warning("failed to open directory: %s\n", dir);
2450                 g_free(prev_dir);
2451                 return -1;
2452         }
2453
2454         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2455                 if (g_unlink(dir_name) < 0)
2456                         FILE_OP_ERROR(dir_name, "unlink");
2457         }
2458
2459         g_dir_close(dp);
2460
2461         if (g_chdir(prev_dir) < 0) {
2462                 FILE_OP_ERROR(prev_dir, "chdir");
2463                 g_free(prev_dir);
2464                 return -1;
2465         }
2466
2467         g_free(prev_dir);
2468
2469         return 0;
2470 }
2471
2472 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2473 {
2474         GDir *dp;
2475         const gchar *dir_name;
2476         gchar *prev_dir;
2477         gint file_no;
2478
2479         prev_dir = g_get_current_dir();
2480
2481         if (g_chdir(dir) < 0) {
2482                 FILE_OP_ERROR(dir, "chdir");
2483                 g_free(prev_dir);
2484                 return -1;
2485         }
2486
2487         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2488                 g_warning("failed to open directory: %s\n", dir);
2489                 g_free(prev_dir);
2490                 return -1;
2491         }
2492
2493         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2494                 file_no = to_number(dir_name);
2495                 if (file_no > 0 && first <= file_no && file_no <= last) {
2496                         if (is_dir_exist(dir_name))
2497                                 continue;
2498                         if (g_unlink(dir_name) < 0)
2499                                 FILE_OP_ERROR(dir_name, "unlink");
2500                 }
2501         }
2502
2503         g_dir_close(dp);
2504
2505         if (g_chdir(prev_dir) < 0) {
2506                 FILE_OP_ERROR(prev_dir, "chdir");
2507                 g_free(prev_dir);
2508                 return -1;
2509         }
2510
2511         g_free(prev_dir);
2512
2513         return 0;
2514 }
2515
2516 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2517 {
2518         GDir *dp;
2519         const gchar *dir_name;
2520         gchar *prev_dir;
2521         gint file_no;
2522
2523         prev_dir = g_get_current_dir();
2524
2525         if (g_chdir(dir) < 0) {
2526                 FILE_OP_ERROR(dir, "chdir");
2527                 g_free(prev_dir);
2528                 return -1;
2529         }
2530
2531         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2532                 FILE_OP_ERROR(dir, "opendir");
2533                 g_free(prev_dir);
2534                 return -1;
2535         }
2536
2537         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2538                 file_no = to_number(dir_name);
2539                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2540                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2541                         if (is_dir_exist(dir_name))
2542                                 continue;
2543                         if (g_unlink(dir_name) < 0)
2544                                 FILE_OP_ERROR(dir_name, "unlink");
2545                 }
2546         }
2547
2548         g_dir_close(dp);
2549
2550         if (g_chdir(prev_dir) < 0) {
2551                 FILE_OP_ERROR(prev_dir, "chdir");
2552                 g_free(prev_dir);
2553                 return -1;
2554         }
2555
2556         g_free(prev_dir);
2557
2558         return 0;
2559 }
2560
2561 gint remove_all_numbered_files(const gchar *dir)
2562 {
2563         return remove_numbered_files(dir, 0, UINT_MAX);
2564 }
2565
2566 gint remove_expired_files(const gchar *dir, guint hours)
2567 {
2568         GDir *dp;
2569         const gchar *dir_name;
2570         struct stat s;
2571         gchar *prev_dir;
2572         gint file_no;
2573         time_t mtime, now, expire_time;
2574
2575         prev_dir = g_get_current_dir();
2576
2577         if (g_chdir(dir) < 0) {
2578                 FILE_OP_ERROR(dir, "chdir");
2579                 g_free(prev_dir);
2580                 return -1;
2581         }
2582
2583         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2584                 g_warning("failed to open directory: %s\n", dir);
2585                 g_free(prev_dir);
2586                 return -1;
2587         }
2588
2589         now = time(NULL);
2590         expire_time = hours * 60 * 60;
2591
2592         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2593                 file_no = to_number(dir_name);
2594                 if (file_no > 0) {
2595                         if (g_stat(dir_name, &s) < 0) {
2596                                 FILE_OP_ERROR(dir_name, "stat");
2597                                 continue;
2598                         }
2599                         if (S_ISDIR(s.st_mode))
2600                                 continue;
2601                         mtime = MAX(s.st_mtime, s.st_atime);
2602                         if (now - mtime > expire_time) {
2603                                 if (g_unlink(dir_name) < 0)
2604                                         FILE_OP_ERROR(dir_name, "unlink");
2605                         }
2606                 }
2607         }
2608
2609         g_dir_close(dp);
2610
2611         if (g_chdir(prev_dir) < 0) {
2612                 FILE_OP_ERROR(prev_dir, "chdir");
2613                 g_free(prev_dir);
2614                 return -1;
2615         }
2616
2617         g_free(prev_dir);
2618
2619         return 0;
2620 }
2621
2622 gint remove_dir_recursive(const gchar *dir)
2623 {
2624         struct stat s;
2625         GDir *dp;
2626         const gchar *dir_name;
2627         gchar *prev_dir;
2628
2629         if (g_stat(dir, &s) < 0) {
2630                 FILE_OP_ERROR(dir, "stat");
2631                 if (ENOENT == errno) return 0;
2632                 return -1;
2633         }
2634
2635         if (!S_ISDIR(s.st_mode)) {
2636                 if (g_unlink(dir) < 0) {
2637                         FILE_OP_ERROR(dir, "unlink");
2638                         return -1;
2639                 }
2640
2641                 return 0;
2642         }
2643
2644         prev_dir = g_get_current_dir();
2645         /* g_print("prev_dir = %s\n", prev_dir); */
2646
2647         if (!path_cmp(prev_dir, dir)) {
2648                 g_free(prev_dir);
2649                 if (g_chdir("..") < 0) {
2650                         FILE_OP_ERROR(dir, "chdir");
2651                         return -1;
2652                 }
2653                 prev_dir = g_get_current_dir();
2654         }
2655
2656         if (g_chdir(dir) < 0) {
2657                 FILE_OP_ERROR(dir, "chdir");
2658                 g_free(prev_dir);
2659                 return -1;
2660         }
2661
2662         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2663                 g_warning("failed to open directory: %s\n", dir);
2664                 g_chdir(prev_dir);
2665                 g_free(prev_dir);
2666                 return -1;
2667         }
2668
2669         /* remove all files in the directory */
2670         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2671                 /* g_print("removing %s\n", dir_name); */
2672
2673                 if (is_dir_exist(dir_name)) {
2674                         if (remove_dir_recursive(dir_name) < 0) {
2675                                 g_warning("can't remove directory\n");
2676                                 return -1;
2677                         }
2678                 } else {
2679                         if (g_unlink(dir_name) < 0)
2680                                 FILE_OP_ERROR(dir_name, "unlink");
2681                 }
2682         }
2683
2684         g_dir_close(dp);
2685
2686         if (g_chdir(prev_dir) < 0) {
2687                 FILE_OP_ERROR(prev_dir, "chdir");
2688                 g_free(prev_dir);
2689                 return -1;
2690         }
2691
2692         g_free(prev_dir);
2693
2694         if (g_rmdir(dir) < 0) {
2695                 FILE_OP_ERROR(dir, "rmdir");
2696                 return -1;
2697         }
2698
2699         return 0;
2700 }
2701
2702 gint rename_force(const gchar *oldpath, const gchar *newpath)
2703 {
2704 #ifndef G_OS_UNIX
2705         if (!is_file_entry_exist(oldpath)) {
2706                 errno = ENOENT;
2707                 return -1;
2708         }
2709         if (is_file_exist(newpath)) {
2710                 if (g_unlink(newpath) < 0)
2711                         FILE_OP_ERROR(newpath, "unlink");
2712         }
2713 #endif
2714         return g_rename(oldpath, newpath);
2715 }
2716
2717 /*
2718  * Append src file body to the tail of dest file.
2719  * Now keep_backup has no effects.
2720  */
2721 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2722 {
2723         FILE *src_fp, *dest_fp;
2724         gint n_read;
2725         gchar buf[BUFSIZ];
2726
2727         gboolean err = FALSE;
2728
2729         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2730                 FILE_OP_ERROR(src, "fopen");
2731                 return -1;
2732         }
2733
2734         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2735                 FILE_OP_ERROR(dest, "fopen");
2736                 fclose(src_fp);
2737                 return -1;
2738         }
2739
2740         if (change_file_mode_rw(dest_fp, dest) < 0) {
2741                 FILE_OP_ERROR(dest, "chmod");
2742                 g_warning("can't change file mode\n");
2743         }
2744
2745         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2746                 if (n_read < sizeof(buf) && ferror(src_fp))
2747                         break;
2748                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2749                         g_warning("writing to %s failed.\n", dest);
2750                         fclose(dest_fp);
2751                         fclose(src_fp);
2752                         g_unlink(dest);
2753                         return -1;
2754                 }
2755         }
2756
2757         if (ferror(src_fp)) {
2758                 FILE_OP_ERROR(src, "fread");
2759                 err = TRUE;
2760         }
2761         fclose(src_fp);
2762         if (fclose(dest_fp) == EOF) {
2763                 FILE_OP_ERROR(dest, "fclose");
2764                 err = TRUE;
2765         }
2766
2767         if (err) {
2768                 g_unlink(dest);
2769                 return -1;
2770         }
2771
2772         return 0;
2773 }
2774
2775 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2776 {
2777         FILE *src_fp, *dest_fp;
2778         gint n_read;
2779         gchar buf[BUFSIZ];
2780         gchar *dest_bak = NULL;
2781         gboolean err = FALSE;
2782
2783         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2784                 FILE_OP_ERROR(src, "fopen");
2785                 return -1;
2786         }
2787         if (is_file_exist(dest)) {
2788                 dest_bak = g_strconcat(dest, ".bak", NULL);
2789                 if (rename_force(dest, dest_bak) < 0) {
2790                         FILE_OP_ERROR(dest, "rename");
2791                         fclose(src_fp);
2792                         g_free(dest_bak);
2793                         return -1;
2794                 }
2795         }
2796
2797         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2798                 FILE_OP_ERROR(dest, "fopen");
2799                 fclose(src_fp);
2800                 if (dest_bak) {
2801                         if (rename_force(dest_bak, dest) < 0)
2802                                 FILE_OP_ERROR(dest_bak, "rename");
2803                         g_free(dest_bak);
2804                 }
2805                 return -1;
2806         }
2807
2808         if (change_file_mode_rw(dest_fp, dest) < 0) {
2809                 FILE_OP_ERROR(dest, "chmod");
2810                 g_warning("can't change file mode\n");
2811         }
2812
2813         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2814                 if (n_read < sizeof(buf) && ferror(src_fp))
2815                         break;
2816                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2817                         g_warning("writing to %s failed.\n", dest);
2818                         fclose(dest_fp);
2819                         fclose(src_fp);
2820                         g_unlink(dest);
2821                         if (dest_bak) {
2822                                 if (rename_force(dest_bak, dest) < 0)
2823                                         FILE_OP_ERROR(dest_bak, "rename");
2824                                 g_free(dest_bak);
2825                         }
2826                         return -1;
2827                 }
2828         }
2829
2830         if (ferror(src_fp)) {
2831                 FILE_OP_ERROR(src, "fread");
2832                 err = TRUE;
2833         }
2834         fclose(src_fp);
2835         if (fclose(dest_fp) == EOF) {
2836                 FILE_OP_ERROR(dest, "fclose");
2837                 err = TRUE;
2838         }
2839
2840         if (err) {
2841                 g_unlink(dest);
2842                 if (dest_bak) {
2843                         if (rename_force(dest_bak, dest) < 0)
2844                                 FILE_OP_ERROR(dest_bak, "rename");
2845                         g_free(dest_bak);
2846                 }
2847                 return -1;
2848         }
2849
2850         if (keep_backup == FALSE && dest_bak)
2851                 g_unlink(dest_bak);
2852
2853         g_free(dest_bak);
2854
2855         return 0;
2856 }
2857
2858 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2859 {
2860         if (overwrite == FALSE && is_file_exist(dest)) {
2861                 g_warning("move_file(): file %s already exists.", dest);
2862                 return -1;
2863         }
2864
2865         if (rename_force(src, dest) == 0) return 0;
2866
2867         if (EXDEV != errno) {
2868                 FILE_OP_ERROR(src, "rename");
2869                 return -1;
2870         }
2871
2872         if (copy_file(src, dest, FALSE) < 0) return -1;
2873
2874         g_unlink(src);
2875
2876         return 0;
2877 }
2878
2879 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2880 {
2881         gint n_read;
2882         gint bytes_left, to_read;
2883         gchar buf[BUFSIZ];
2884
2885         if (fseek(fp, offset, SEEK_SET) < 0) {
2886                 perror("fseek");
2887                 return -1;
2888         }
2889
2890         bytes_left = length;
2891         to_read = MIN(bytes_left, sizeof(buf));
2892
2893         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2894                 if (n_read < to_read && ferror(fp))
2895                         break;
2896                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2897                         return -1;
2898                 }
2899                 bytes_left -= n_read;
2900                 if (bytes_left == 0)
2901                         break;
2902                 to_read = MIN(bytes_left, sizeof(buf));
2903         }
2904
2905         if (ferror(fp)) {
2906                 perror("fread");
2907                 return -1;
2908         }
2909
2910         return 0;
2911 }
2912
2913 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2914 {
2915         FILE *dest_fp;
2916         gboolean err = FALSE;
2917
2918         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2919                 FILE_OP_ERROR(dest, "fopen");
2920                 return -1;
2921         }
2922
2923         if (change_file_mode_rw(dest_fp, dest) < 0) {
2924                 FILE_OP_ERROR(dest, "chmod");
2925                 g_warning("can't change file mode\n");
2926         }
2927
2928         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2929                 err = TRUE;
2930
2931         if (!err && fclose(dest_fp) == EOF) {
2932                 FILE_OP_ERROR(dest, "fclose");
2933                 err = TRUE;
2934         }
2935
2936         if (err) {
2937                 g_warning("writing to %s failed.\n", dest);
2938                 g_unlink(dest);
2939                 return -1;
2940         }
2941
2942         return 0;
2943 }
2944
2945 /* convert line endings into CRLF. If the last line doesn't end with
2946  * linebreak, add it.
2947  */
2948 gchar *canonicalize_str(const gchar *str)
2949 {
2950         const gchar *p;
2951         guint new_len = 0;
2952         gchar *out, *outp;
2953
2954         for (p = str; *p != '\0'; ++p) {
2955                 if (*p != '\r') {
2956                         ++new_len;
2957                         if (*p == '\n')
2958                                 ++new_len;
2959                 }
2960         }
2961         if (p == str || *(p - 1) != '\n')
2962                 new_len += 2;
2963
2964         out = outp = g_malloc(new_len + 1);
2965         for (p = str; *p != '\0'; ++p) {
2966                 if (*p != '\r') {
2967                         if (*p == '\n')
2968                                 *outp++ = '\r';
2969                         *outp++ = *p;
2970                 }
2971         }
2972         if (p == str || *(p - 1) != '\n') {
2973                 *outp++ = '\r';
2974                 *outp++ = '\n';
2975         }
2976         *outp = '\0';
2977
2978         return out;
2979 }
2980
2981 gint canonicalize_file(const gchar *src, const gchar *dest)
2982 {
2983         FILE *src_fp, *dest_fp;
2984         gchar buf[BUFFSIZE];
2985         gint len;
2986         gboolean err = FALSE;
2987         gboolean last_linebreak = FALSE;
2988
2989         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2990                 FILE_OP_ERROR(src, "fopen");
2991                 return -1;
2992         }
2993
2994         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2995                 FILE_OP_ERROR(dest, "fopen");
2996                 fclose(src_fp);
2997                 return -1;
2998         }
2999
3000         if (change_file_mode_rw(dest_fp, dest) < 0) {
3001                 FILE_OP_ERROR(dest, "chmod");
3002                 g_warning("can't change file mode\n");
3003         }
3004
3005         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3006                 gint r = 0;
3007
3008                 len = strlen(buf);
3009                 if (len == 0) break;
3010                 last_linebreak = FALSE;
3011
3012                 if (buf[len - 1] != '\n') {
3013                         last_linebreak = TRUE;
3014                         r = fputs(buf, dest_fp);
3015                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3016                         r = fputs(buf, dest_fp);
3017                 } else {
3018                         if (len > 1) {
3019                                 r = fwrite(buf, 1, len - 1, dest_fp);
3020                                 if (r != (len -1))
3021                                         r = EOF;
3022                         }
3023                         if (r != EOF)
3024                                 r = fputs("\r\n", dest_fp);
3025                 }
3026
3027                 if (r == EOF) {
3028                         g_warning("writing to %s failed.\n", dest);
3029                         fclose(dest_fp);
3030                         fclose(src_fp);
3031                         g_unlink(dest);
3032                         return -1;
3033                 }
3034         }
3035
3036         if (last_linebreak == TRUE) {
3037                 if (fputs("\r\n", dest_fp) == EOF)
3038                         err = TRUE;
3039         }
3040
3041         if (ferror(src_fp)) {
3042                 FILE_OP_ERROR(src, "fgets");
3043                 err = TRUE;
3044         }
3045         fclose(src_fp);
3046         if (fclose(dest_fp) == EOF) {
3047                 FILE_OP_ERROR(dest, "fclose");
3048                 err = TRUE;
3049         }
3050
3051         if (err) {
3052                 g_unlink(dest);
3053                 return -1;
3054         }
3055
3056         return 0;
3057 }
3058
3059 gint canonicalize_file_replace(const gchar *file)
3060 {
3061         gchar *tmp_file;
3062
3063         tmp_file = get_tmp_file();
3064
3065         if (canonicalize_file(file, tmp_file) < 0) {
3066                 g_free(tmp_file);
3067                 return -1;
3068         }
3069
3070         if (move_file(tmp_file, file, TRUE) < 0) {
3071                 g_warning("can't replace %s .\n", file);
3072                 g_unlink(tmp_file);
3073                 g_free(tmp_file);
3074                 return -1;
3075         }
3076
3077         g_free(tmp_file);
3078         return 0;
3079 }
3080
3081 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3082 {
3083         FILE *src_fp, *dest_fp;
3084         gchar buf[BUFFSIZE];
3085         gboolean err = FALSE;
3086
3087         if ((src_fp = g_fopen(src, "rb")) == NULL) {
3088                 FILE_OP_ERROR(src, "fopen");
3089                 return -1;
3090         }
3091
3092         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3093                 FILE_OP_ERROR(dest, "fopen");
3094                 fclose(src_fp);
3095                 return -1;
3096         }
3097
3098         if (change_file_mode_rw(dest_fp, dest) < 0) {
3099                 FILE_OP_ERROR(dest, "chmod");
3100                 g_warning("can't change file mode\n");
3101         }
3102
3103         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3104                 strcrchomp(buf);
3105                 if (fputs(buf, dest_fp) == EOF) {
3106                         g_warning("writing to %s failed.\n", dest);
3107                         fclose(dest_fp);
3108                         fclose(src_fp);
3109                         g_unlink(dest);
3110                         return -1;
3111                 }
3112         }
3113
3114         if (ferror(src_fp)) {
3115                 FILE_OP_ERROR(src, "fgets");
3116                 err = TRUE;
3117         }
3118         fclose(src_fp);
3119         if (fclose(dest_fp) == EOF) {
3120                 FILE_OP_ERROR(dest, "fclose");
3121                 err = TRUE;
3122         }
3123
3124         if (err) {
3125                 g_unlink(dest);
3126                 return -1;
3127         }
3128
3129         return 0;
3130 }
3131
3132 gint uncanonicalize_file_replace(const gchar *file)
3133 {
3134         gchar *tmp_file;
3135
3136         tmp_file = get_tmp_file();
3137
3138         if (uncanonicalize_file(file, tmp_file) < 0) {
3139                 g_free(tmp_file);
3140                 return -1;
3141         }
3142
3143         if (move_file(tmp_file, file, TRUE) < 0) {
3144                 g_warning("can't replace %s .\n", file);
3145                 g_unlink(tmp_file);
3146                 g_free(tmp_file);
3147                 return -1;
3148         }
3149
3150         g_free(tmp_file);
3151         return 0;
3152 }
3153
3154 gchar *normalize_newlines(const gchar *str)
3155 {
3156         const gchar *p = str;
3157         gchar *out, *outp;
3158
3159         out = outp = g_malloc(strlen(str) + 1);
3160         for (p = str; *p != '\0'; ++p) {
3161                 if (*p == '\r') {
3162                         if (*(p + 1) != '\n')
3163                                 *outp++ = '\n';
3164                 } else
3165                         *outp++ = *p;
3166         }
3167
3168         *outp = '\0';
3169
3170         return out;
3171 }
3172
3173 gchar *get_outgoing_rfc2822_str(FILE *fp)
3174 {
3175         gchar buf[BUFFSIZE];
3176         GString *str;
3177         gchar *ret;
3178
3179         str = g_string_new(NULL);
3180
3181         /* output header part */
3182         while (fgets(buf, sizeof(buf), fp) != NULL) {
3183                 strretchomp(buf);
3184                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3185                         gint next;
3186
3187                         for (;;) {
3188                                 next = fgetc(fp);
3189                                 if (next == EOF)
3190                                         break;
3191                                 else if (next != ' ' && next != '\t') {
3192                                         ungetc(next, fp);
3193                                         break;
3194                                 }
3195                                 if (fgets(buf, sizeof(buf), fp) == NULL)
3196                                         break;
3197                         }
3198                 } else {
3199                         g_string_append(str, buf);
3200                         g_string_append(str, "\r\n");
3201                         if (buf[0] == '\0')
3202                                 break;
3203                 }
3204         }
3205
3206         /* output body part */
3207         while (fgets(buf, sizeof(buf), fp) != NULL) {
3208                 strretchomp(buf);
3209                 if (buf[0] == '.')
3210                         g_string_append_c(str, '.');
3211                 g_string_append(str, buf);
3212                 g_string_append(str, "\r\n");
3213         }
3214
3215         ret = str->str;
3216         g_string_free(str, FALSE);
3217
3218         return ret;
3219 }
3220
3221 /*
3222  * Create a new boundary in a way that it is very unlikely that this
3223  * will occur in the following text.  It would be easy to ensure
3224  * uniqueness if everything is either quoted-printable or base64
3225  * encoded (note that conversion is allowed), but because MIME bodies
3226  * may be nested, it may happen that the same boundary has already
3227  * been used.
3228  *
3229  *   boundary := 0*69<bchars> bcharsnospace
3230  *   bchars := bcharsnospace / " "
3231  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3232  *                  "+" / "_" / "," / "-" / "." /
3233  *                  "/" / ":" / "=" / "?"
3234  *
3235  * some special characters removed because of buggy MTAs
3236  */
3237
3238 gchar *generate_mime_boundary(const gchar *prefix)
3239 {
3240         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3241                              "abcdefghijklmnopqrstuvwxyz"
3242                              "1234567890+_./=";
3243         gchar buf_uniq[24];
3244         gint i;
3245
3246         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3247                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3248         buf_uniq[i] = '\0';
3249
3250         return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3251                                buf_uniq);
3252 }
3253
3254 gint change_file_mode_rw(FILE *fp, const gchar *file)
3255 {
3256 #if HAVE_FCHMOD
3257         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3258 #else
3259         return g_chmod(file, S_IRUSR|S_IWUSR);
3260 #endif
3261 }
3262
3263 FILE *my_tmpfile(void)
3264 {
3265 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3266         const gchar suffix[] = ".XXXXXX";
3267         const gchar *tmpdir;
3268         guint tmplen;
3269         const gchar *progname;
3270         guint proglen;
3271         gchar *fname;
3272         gint fd;
3273         FILE *fp;
3274         gchar buf[2]="a";
3275
3276         tmpdir = get_tmp_dir();
3277         tmplen = strlen(tmpdir);
3278         progname = g_get_prgname();
3279         if (progname == NULL)
3280                 progname = "sylpheed-claws";
3281         proglen = strlen(progname);
3282         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3283                 return tmpfile());
3284
3285         memcpy(fname, tmpdir, tmplen);
3286         fname[tmplen] = G_DIR_SEPARATOR;
3287         memcpy(fname + tmplen + 1, progname, proglen);
3288         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3289
3290         fd = mkstemp(fname);
3291         if (fd < 0)
3292                 return tmpfile();
3293
3294 #ifndef G_OS_WIN32
3295         g_unlink(fname);
3296         
3297         /* verify that we can write in the file after unlinking */
3298         if (write(fd, buf, 1) < 0) {
3299                 close(fd);
3300                 return tmpfile();
3301         }
3302         
3303 #endif
3304
3305         fp = fdopen(fd, "w+b");
3306         if (!fp)
3307                 close(fd);
3308         else {
3309                 rewind(fp);
3310                 return fp;
3311         }
3312
3313 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3314
3315         return tmpfile();
3316 }
3317
3318 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3319 {
3320         int fd;
3321 #ifdef G_OS_WIN32
3322         char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3323                                           dir, G_DIR_SEPARATOR);
3324         fd = mkstemp_name(template, filename);
3325         g_free(template);
3326 #else
3327         *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3328         fd = mkstemp(*filename);
3329 #endif
3330         return fdopen(fd, "w+");
3331 }
3332
3333 FILE *str_open_as_stream(const gchar *str)
3334 {
3335         FILE *fp;
3336         size_t len;
3337
3338         g_return_val_if_fail(str != NULL, NULL);
3339
3340         fp = my_tmpfile();
3341         if (!fp) {
3342                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3343                 return NULL;
3344         }
3345
3346         len = strlen(str);
3347         if (len == 0) return fp;
3348
3349         if (fwrite(str, 1, len, fp) != len) {
3350                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3351                 fclose(fp);
3352                 return NULL;
3353         }
3354
3355         rewind(fp);
3356         return fp;
3357 }
3358
3359 gint str_write_to_file(const gchar *str, const gchar *file)
3360 {
3361         FILE *fp;
3362         size_t len;
3363
3364         g_return_val_if_fail(str != NULL, -1);
3365         g_return_val_if_fail(file != NULL, -1);
3366
3367         if ((fp = g_fopen(file, "wb")) == NULL) {
3368                 FILE_OP_ERROR(file, "fopen");
3369                 return -1;
3370         }
3371
3372         len = strlen(str);
3373         if (len == 0) {
3374                 fclose(fp);
3375                 return 0;
3376         }
3377
3378         if (fwrite(str, 1, len, fp) != len) {
3379                 FILE_OP_ERROR(file, "fwrite");
3380                 fclose(fp);
3381                 g_unlink(file);
3382                 return -1;
3383         }
3384
3385         if (fclose(fp) == EOF) {
3386                 FILE_OP_ERROR(file, "fclose");
3387                 g_unlink(file);
3388                 return -1;
3389         }
3390
3391         return 0;
3392 }
3393
3394 gchar *file_read_to_str(const gchar *file)
3395 {
3396         FILE *fp;
3397         gchar *str;
3398
3399         g_return_val_if_fail(file != NULL, NULL);
3400
3401         if ((fp = g_fopen(file, "rb")) == NULL) {
3402                 FILE_OP_ERROR(file, "fopen");
3403                 return NULL;
3404         }
3405
3406         str = file_read_stream_to_str(fp);
3407
3408         fclose(fp);
3409
3410         return str;
3411 }
3412
3413 gchar *file_read_stream_to_str(FILE *fp)
3414 {
3415         GByteArray *array;
3416         guchar buf[BUFSIZ];
3417         gint n_read;
3418         gchar *str;
3419
3420         g_return_val_if_fail(fp != NULL, NULL);
3421
3422         array = g_byte_array_new();
3423
3424         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3425                 if (n_read < sizeof(buf) && ferror(fp))
3426                         break;
3427                 g_byte_array_append(array, buf, n_read);
3428         }
3429
3430         if (ferror(fp)) {
3431                 FILE_OP_ERROR("file stream", "fread");
3432                 g_byte_array_free(array, TRUE);
3433                 return NULL;
3434         }
3435
3436         buf[0] = '\0';
3437         g_byte_array_append(array, buf, 1);
3438         str = (gchar *)array->data;
3439         g_byte_array_free(array, FALSE);
3440
3441         if (!g_utf8_validate(str, -1, NULL)) {
3442                 const gchar *src_codeset, *dest_codeset;
3443                 gchar *tmp = NULL;
3444                 src_codeset = conv_get_locale_charset_str();
3445                 dest_codeset = CS_UTF_8;
3446                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3447                 g_free(str);
3448                 str = tmp;
3449         }
3450
3451         return str;
3452 }
3453
3454 gint execute_async(gchar *const argv[])
3455 {
3456         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3457
3458         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3459                           NULL, NULL, NULL, FALSE) == FALSE) {
3460                 g_warning("Can't execute command: %s\n", argv[0]);
3461                 return -1;
3462         }
3463
3464         return 0;
3465 }
3466
3467 gint execute_sync(gchar *const argv[])
3468 {
3469         gint status;
3470
3471         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3472
3473         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3474                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3475                 g_warning("Can't execute command: %s\n", argv[0]);
3476                 return -1;
3477         }
3478
3479 #ifdef G_OS_UNIX
3480         if (WIFEXITED(status))
3481                 return WEXITSTATUS(status);
3482         else
3483                 return -1;
3484 #else
3485         return status;
3486 #endif
3487 }
3488
3489 gint execute_command_line(const gchar *cmdline, gboolean async)
3490 {
3491         gchar **argv;
3492         gint ret;
3493
3494         debug_print("execute_command_line(): executing: %s\n", cmdline);
3495
3496         argv = strsplit_with_quote(cmdline, " ", 0);
3497
3498         if (async)
3499                 ret = execute_async(argv);
3500         else
3501                 ret = execute_sync(argv);
3502
3503         g_strfreev(argv);
3504
3505         return ret;
3506 }
3507
3508 gchar *get_command_output(const gchar *cmdline)
3509 {
3510         gchar *child_stdout;
3511         gint status;
3512
3513         g_return_val_if_fail(cmdline != NULL, NULL);
3514
3515         debug_print("get_command_output(): executing: %s\n", cmdline);
3516
3517         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3518                                       NULL) == FALSE) {
3519                 g_warning("Can't execute command: %s\n", cmdline);
3520                 return NULL;
3521         }
3522
3523         return child_stdout;
3524 }
3525
3526 static gint is_unchanged_uri_char(char c)
3527 {
3528         switch (c) {
3529                 case '(':
3530                 case ')':
3531                 case ',':
3532                         return 0;
3533                 default:
3534                         return 1;
3535         }
3536 }
3537
3538 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3539 {
3540         int i;
3541         int k;
3542
3543         k = 0;
3544         for(i = 0; i < strlen(uri) ; i++) {
3545                 if (is_unchanged_uri_char(uri[i])) {
3546                         if (k + 2 >= bufsize)
3547                                 break;
3548                         encoded_uri[k++] = uri[i];
3549                 }
3550                 else {
3551                         char * hexa = "0123456789ABCDEF";
3552
3553                         if (k + 4 >= bufsize)
3554                                 break;
3555                         encoded_uri[k++] = '%';
3556                         encoded_uri[k++] = hexa[uri[i] / 16];
3557                         encoded_uri[k++] = hexa[uri[i] % 16];
3558                 }
3559         }
3560         encoded_uri[k] = 0;
3561 }
3562
3563 gint open_uri(const gchar *uri, const gchar *cmdline)
3564 {
3565         gchar buf[BUFFSIZE];
3566         gchar *p;
3567         gchar encoded_uri[BUFFSIZE];
3568
3569         g_return_val_if_fail(uri != NULL, -1);
3570
3571         /* an option to choose whether to use encode_uri or not ? */
3572         encode_uri(encoded_uri, BUFFSIZE, uri);
3573
3574         if (cmdline &&
3575             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3576             !strchr(p + 2, '%'))
3577                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3578         else {
3579                 if (cmdline)
3580                         g_warning("Open URI command line is invalid "
3581                                   "(there must be only one '%%s'): %s",
3582                                   cmdline);
3583                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3584         }
3585
3586         execute_command_line(buf, TRUE);
3587
3588         return 0;
3589 }
3590
3591 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3592 {
3593         gchar buf[BUFFSIZE];
3594         gchar *p;
3595
3596         g_return_val_if_fail(filepath != NULL, -1);
3597
3598         if (cmdline &&
3599             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3600             !strchr(p + 2, '%'))
3601                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3602         else {
3603                 if (cmdline)
3604                         g_warning("Open Text Editor command line is invalid "
3605                                   "(there must be only one '%%s'): %s",
3606                                   cmdline);
3607                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3608         }
3609
3610         execute_command_line(buf, TRUE);
3611
3612         return 0;
3613 }
3614
3615 time_t remote_tzoffset_sec(const gchar *zone)
3616 {
3617         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3618         gchar zone3[4];
3619         gchar *p;
3620         gchar c;
3621         gint iustz;
3622         gint offset;
3623         time_t remoteoffset;
3624
3625         strncpy(zone3, zone, 3);
3626         zone3[3] = '\0';
3627         remoteoffset = 0;
3628
3629         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3630             (c == '+' || c == '-')) {
3631                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3632                 if (c == '-')
3633                         remoteoffset = -remoteoffset;
3634         } else if (!strncmp(zone, "UT" , 2) ||
3635                    !strncmp(zone, "GMT", 2)) {
3636                 remoteoffset = 0;
3637         } else if (strlen(zone3) == 3) {
3638                 for (p = ustzstr; *p != '\0'; p += 3) {
3639                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3640                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3641                                 remoteoffset = iustz * 3600;
3642                                 break;
3643                         }
3644                 }
3645                 if (*p == '\0')
3646                         return -1;
3647         } else if (strlen(zone3) == 1) {
3648                 switch (zone[0]) {
3649                 case 'Z': remoteoffset =   0; break;
3650                 case 'A': remoteoffset =  -1; break;
3651                 case 'B': remoteoffset =  -2; break;
3652                 case 'C': remoteoffset =  -3; break;
3653                 case 'D': remoteoffset =  -4; break;
3654                 case 'E': remoteoffset =  -5; break;
3655                 case 'F': remoteoffset =  -6; break;
3656                 case 'G': remoteoffset =  -7; break;
3657                 case 'H': remoteoffset =  -8; break;
3658                 case 'I': remoteoffset =  -9; break;
3659                 case 'K': remoteoffset = -10; break; /* J is not used */
3660                 case 'L': remoteoffset = -11; break;
3661                 case 'M': remoteoffset = -12; break;
3662                 case 'N': remoteoffset =   1; break;
3663                 case 'O': remoteoffset =   2; break;
3664                 case 'P': remoteoffset =   3; break;
3665                 case 'Q': remoteoffset =   4; break;
3666                 case 'R': remoteoffset =   5; break;
3667                 case 'S': remoteoffset =   6; break;
3668                 case 'T': remoteoffset =   7; break;
3669                 case 'U': remoteoffset =   8; break;
3670                 case 'V': remoteoffset =   9; break;
3671                 case 'W': remoteoffset =  10; break;
3672                 case 'X': remoteoffset =  11; break;
3673                 case 'Y': remoteoffset =  12; break;
3674                 default:  remoteoffset =   0; break;
3675                 }
3676                 remoteoffset = remoteoffset * 3600;
3677         } else
3678                 return -1;
3679
3680         return remoteoffset;
3681 }
3682
3683 time_t tzoffset_sec(time_t *now)
3684 {
3685         struct tm gmt, *lt;
3686         gint off;
3687
3688         gmt = *gmtime(now);
3689         lt = localtime(now);
3690
3691         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3692
3693         if (lt->tm_year < gmt.tm_year)
3694                 off -= 24 * 60;
3695         else if (lt->tm_year > gmt.tm_year)
3696                 off += 24 * 60;
3697         else if (lt->tm_yday < gmt.tm_yday)
3698                 off -= 24 * 60;
3699         else if (lt->tm_yday > gmt.tm_yday)
3700                 off += 24 * 60;
3701
3702         if (off >= 24 * 60)             /* should be impossible */
3703                 off = 23 * 60 + 59;     /* if not, insert silly value */
3704         if (off <= -24 * 60)
3705                 off = -(23 * 60 + 59);
3706
3707         return off * 60;
3708 }
3709
3710 /* calculate timezone offset */
3711 gchar *tzoffset(time_t *now)
3712 {
3713         static gchar offset_string[6];
3714         struct tm gmt, *lt;
3715         gint off;
3716         gchar sign = '+';
3717
3718         gmt = *gmtime(now);
3719         lt = localtime(now);
3720
3721         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3722
3723         if (lt->tm_year < gmt.tm_year)
3724                 off -= 24 * 60;
3725         else if (lt->tm_year > gmt.tm_year)
3726                 off += 24 * 60;
3727         else if (lt->tm_yday < gmt.tm_yday)
3728                 off -= 24 * 60;
3729         else if (lt->tm_yday > gmt.tm_yday)
3730                 off += 24 * 60;
3731
3732         if (off < 0) {
3733                 sign = '-';
3734                 off = -off;
3735         }
3736
3737         if (off >= 24 * 60)             /* should be impossible */
3738                 off = 23 * 60 + 59;     /* if not, insert silly value */
3739
3740         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3741
3742         return offset_string;
3743 }
3744
3745 void get_rfc822_date(gchar *buf, gint len)
3746 {
3747         struct tm *lt;
3748         time_t t;
3749         gchar day[4], mon[4];
3750         gint dd, hh, mm, ss, yyyy;
3751
3752         t = time(NULL);
3753         lt = localtime(&t);
3754
3755         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3756                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3757         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3758                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3759 }
3760
3761 /* just a wrapper to suppress the warning of gcc about %c */
3762 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3763                    const struct tm *tm)
3764 {
3765         return strftime(s, max, format, tm);
3766 }
3767
3768 void debug_set_mode(gboolean mode)
3769 {
3770         debug_mode = mode;
3771 }
3772
3773 gboolean debug_get_mode(void)
3774 {
3775         return debug_mode;
3776 }
3777
3778 void debug_print_real(const gchar *format, ...)
3779 {
3780         va_list args;
3781         gchar buf[BUFFSIZE];
3782
3783         if (!debug_mode) return;
3784
3785         va_start(args, format);
3786         g_vsnprintf(buf, sizeof(buf), format, args);
3787         va_end(args);
3788
3789         g_print("%s", buf);
3790 }
3791
3792
3793 const char * debug_srcname(const char *file)
3794 {
3795         const char *s = strrchr (file, '/');
3796         return s? s+1:file;
3797 }
3798
3799
3800 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3801 {
3802         if (subject == NULL)
3803                 subject = "";
3804         else
3805                 subject += subject_get_prefix_length(subject);
3806
3807         return g_hash_table_lookup(subject_table, subject);
3808 }
3809
3810 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3811                           void * data)
3812 {
3813         if (subject == NULL || *subject == 0)
3814                 return;
3815         subject += subject_get_prefix_length(subject);
3816         g_hash_table_insert(subject_table, subject, data);
3817 }
3818
3819 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3820 {
3821         if (subject == NULL)
3822                 return;
3823
3824         subject += subject_get_prefix_length(subject);
3825         g_hash_table_remove(subject_table, subject);
3826 }
3827
3828 /*!
3829  *\brief        Check if a string is prefixed with known (combinations)
3830  *              of prefixes. The function assumes that each prefix
3831  *              is terminated by zero or exactly _one_ space.
3832  *
3833  *\param        str String to check for a prefixes
3834  *
3835  *\return       int Number of chars in the prefix that should be skipped
3836  *              for a "clean" subject line. If no prefix was found, 0
3837  *              is returned.
3838  */
3839 int subject_get_prefix_length(const gchar *subject)
3840 {
3841         /*!< Array with allowable reply prefixes regexps. */
3842         static const gchar * const prefixes[] = {
3843                 "Re\\:",                        /* "Re:" */
3844                 "RE\\:",                        /* "RE:" (outlook) */
3845                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3846                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3847                 "Aw\\:",                        /* "Aw:"   (German) */
3848                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3849                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3850                 "Fw\\:",                        /* "Fw:" Forward */
3851                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3852                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3853                 "Rif\\:",                       /* "Rif:" (Italian Outlook) */
3854                 "SV\\:",                        /* "SV" (Norwegian) */
3855                 "Sv\\:",                        /* "Sv" (Norwegian) */
3856                 "VS\\:",                        /* "VS" (Norwegian) */
3857                 "Vs\\:",                        /* "Vs" (Norwegian) */
3858                 "AD\\:",                        /* "AD" (Norwegian) */
3859                 "Ad\\:"                         /* "Ad" (Norwegian) */
3860                 /* add more */
3861         };
3862         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3863         int n;
3864         regmatch_t pos;
3865         static regex_t regex;
3866         static gboolean init_;
3867
3868         if (!subject) return 0;
3869         if (!*subject) return 0;
3870
3871         if (!init_) {
3872                 GString *s = g_string_new("");
3873
3874                 for (n = 0; n < PREFIXES; n++)
3875                         /* Terminate each prefix regexpression by a
3876                          * "\ ?" (zero or ONE space), and OR them */
3877                         g_string_append_printf(s, "(%s\\ ?)%s",
3878                                           prefixes[n],
3879                                           n < PREFIXES - 1 ?
3880                                           "|" : "");
3881
3882                 g_string_prepend(s, "(");
3883                 g_string_append(s, ")+");       /* match at least once */
3884                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3885
3886
3887                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3888                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3889                 if (regcomp(&regex, s->str, REG_EXTENDED | REG_ICASE)) {
3890                         debug_print("Error compiling regexp %s\n", s->str);
3891                         g_string_free(s, TRUE);
3892                         return 0;
3893                 } else {
3894                         init_ = TRUE;
3895                         g_string_free(s, TRUE);
3896                 }
3897         }
3898
3899         if (!regexec(&regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3900                 return pos.rm_eo;
3901         else
3902                 return 0;
3903 }
3904
3905 guint g_stricase_hash(gconstpointer gptr)
3906 {
3907         guint hash_result = 0;
3908         const char *str;
3909
3910         for (str = gptr; str && *str; str++) {
3911                 hash_result += toupper(*str);
3912         }
3913
3914         return hash_result;
3915 }
3916
3917 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3918 {
3919         const char *str1 = gptr1;
3920         const char *str2 = gptr2;
3921
3922         return !strcasecmp(str1, str2);
3923 }
3924
3925 gint g_int_compare(gconstpointer a, gconstpointer b)
3926 {
3927         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3928 }
3929
3930 gchar *generate_msgid(gchar *buf, gint len)
3931 {
3932         struct tm *lt;
3933         time_t t;
3934         gchar *addr;
3935
3936         t = time(NULL);
3937         lt = localtime(&t);
3938
3939         addr = g_strconcat("@", get_domain_name(), NULL);
3940
3941         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3942                    lt->tm_year + 1900, lt->tm_mon + 1,
3943                    lt->tm_mday, lt->tm_hour,
3944                    lt->tm_min, lt->tm_sec,
3945                    (guint) rand(), addr);
3946
3947         g_free(addr);
3948         return buf;
3949 }
3950
3951 /*
3952    quote_cmd_argument()
3953
3954    return a quoted string safely usable in argument of a command.
3955
3956    code is extracted and adapted from etPan! project -- DINH V. Hoà.
3957 */
3958
3959 gint quote_cmd_argument(gchar * result, guint size,
3960                         const gchar * path)
3961 {
3962         const gchar * p;
3963         gchar * result_p;
3964         guint remaining;
3965
3966         result_p = result;
3967         remaining = size;
3968
3969         for(p = path ; * p != '\0' ; p ++) {
3970
3971                 if (isalnum((guchar)*p) || (* p == '/')) {
3972                         if (remaining > 0) {
3973                                 * result_p = * p;
3974                                 result_p ++;
3975                                 remaining --;
3976                         }
3977                         else {
3978                                 result[size - 1] = '\0';
3979                                 return -1;
3980                         }
3981                 }
3982                 else {
3983                         if (remaining >= 2) {
3984                                 * result_p = '\\';
3985                                 result_p ++;
3986                                 * result_p = * p;
3987                                 result_p ++;
3988                                 remaining -= 2;
3989                         }
3990                         else {
3991                                 result[size - 1] = '\0';
3992                                 return -1;
3993                         }
3994                 }
3995         }
3996         if (remaining > 0) {
3997                 * result_p = '\0';
3998         }
3999         else {
4000                 result[size - 1] = '\0';
4001                 return -1;
4002         }
4003
4004         return 0;
4005 }
4006
4007 typedef struct
4008 {
4009         GNode           *parent;
4010         GNodeMapFunc     func;
4011         gpointer         data;
4012 } GNodeMapData;
4013
4014 static void g_node_map_recursive(GNode *node, gpointer data)
4015 {
4016         GNodeMapData *mapdata = (GNodeMapData *) data;
4017         GNode *newnode;
4018         GNodeMapData newmapdata;
4019         gpointer newdata;
4020
4021         newdata = mapdata->func(node->data, mapdata->data);
4022         if (newdata != NULL) {
4023                 newnode = g_node_new(newdata);
4024                 g_node_append(mapdata->parent, newnode);
4025
4026                 newmapdata.parent = newnode;
4027                 newmapdata.func = mapdata->func;
4028                 newmapdata.data = mapdata->data;
4029
4030                 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4031         }
4032 }
4033
4034 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4035 {
4036         GNode *root;
4037         GNodeMapData mapdata;
4038
4039         g_return_val_if_fail(node != NULL, NULL);
4040         g_return_val_if_fail(func != NULL, NULL);
4041
4042         root = g_node_new(func(node->data, data));
4043
4044         mapdata.parent = root;
4045         mapdata.func = func;
4046         mapdata.data = data;
4047
4048         g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4049
4050         return root;
4051 }
4052
4053 #define HEX_TO_INT(val, hex)                    \
4054 {                                               \
4055         gchar c = hex;                          \
4056                                                 \
4057         if ('0' <= c && c <= '9') {             \
4058                 val = c - '0';                  \
4059         } else if ('a' <= c && c <= 'f') {      \
4060                 val = c - 'a' + 10;             \
4061         } else if ('A' <= c && c <= 'F') {      \
4062                 val = c - 'A' + 10;             \
4063         } else {                                \
4064                 val = -1;                       \
4065         }                                       \
4066 }
4067
4068 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4069 {
4070         gint hi, lo;
4071
4072         HEX_TO_INT(hi, c1);
4073         HEX_TO_INT(lo, c2);
4074
4075         if (hi == -1 || lo == -1)
4076                 return FALSE;
4077
4078         *out = (hi << 4) + lo;
4079         return TRUE;
4080 }
4081
4082 #define INT_TO_HEX(hex, val)            \
4083 {                                       \
4084         if ((val) < 10)                 \
4085                 hex = '0' + (val);      \
4086         else                            \
4087                 hex = 'A' + (val) - 10; \
4088 }
4089
4090 void get_hex_str(gchar *out, guchar ch)
4091 {
4092         gchar hex;
4093
4094         INT_TO_HEX(hex, ch >> 4);
4095         *out++ = hex;
4096         INT_TO_HEX(hex, ch & 0x0f);
4097         *out++ = hex;
4098 }
4099
4100 #undef REF_DEBUG
4101 #ifndef REF_DEBUG
4102 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4103 #else
4104 #define G_PRINT_REF g_print
4105 #endif
4106
4107 /*!
4108  *\brief        Register ref counted pointer. It is based on GBoxed, so should
4109  *              work with anything that uses the GType system. The semantics
4110  *              are similar to a C++ auto pointer, with the exception that
4111  *              C doesn't have automatic closure (calling destructors) when
4112  *              exiting a block scope.
4113  *              Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4114  *              function directly.
4115  *
4116  *\return       GType A GType type.
4117  */
4118 GType g_auto_pointer_register(void)
4119 {
4120         static GType auto_pointer_type;
4121         if (!auto_pointer_type)
4122                 auto_pointer_type =
4123                         g_boxed_type_register_static
4124                                 ("G_TYPE_AUTO_POINTER",
4125                                  (GBoxedCopyFunc) g_auto_pointer_copy,
4126                                  (GBoxedFreeFunc) g_auto_pointer_free);
4127         return auto_pointer_type;
4128 }
4129
4130 /*!
4131  *\brief        Structure with g_new() allocated pointer guarded by the
4132  *              auto pointer
4133  */
4134 typedef struct AutoPointerRef {
4135         void          (*free) (gpointer);
4136         gpointer        pointer;
4137         glong           cnt;
4138 } AutoPointerRef;
4139
4140 /*!
4141  *\brief        The auto pointer opaque structure that references the
4142  *              pointer guard block.
4143  */
4144 typedef struct AutoPointer {
4145         AutoPointerRef *ref;
4146         gpointer        ptr; /*!< access to protected pointer */
4147 } AutoPointer;
4148
4149 /*!
4150  *\brief        Creates an auto pointer for a g_new()ed pointer. Example:
4151  *
4152  *\code
4153  *
4154  *              ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4155  *              ... when assigning, copying and freeing storage elements
4156  *
4157  *              gtk_list_store_new(N_S_COLUMNS,
4158  *                                 G_TYPE_AUTO_POINTER,
4159  *                                 -1);
4160  *
4161  *
4162  *              Template *precious_data = g_new0(Template, 1);
4163  *              g_pointer protect = g_auto_pointer_new(precious_data);
4164  *
4165  *              gtk_list_store_set(container, &iter,
4166  *                                 S_DATA, protect,
4167  *                                 -1);
4168  *
4169  *              ... the gtk_list_store has copied the pointer and
4170  *              ... incremented its reference count, we should free
4171  *              ... the auto pointer (in C++ a destructor would do
4172  *              ... this for us when leaving block scope)
4173  *
4174  *              g_auto_pointer_free(protect);
4175  *
4176  *              ... gtk_list_store_set() now manages the data. When
4177  *              ... *explicitly* requesting a pointer from the list
4178  *              ... store, don't forget you get a copy that should be
4179  *              ... freed with g_auto_pointer_free() eventually.
4180  *
4181  *\endcode
4182  *
4183  *\param        pointer Pointer to be guarded.
4184  *
4185  *\return       GAuto * Pointer that should be used in containers with
4186  *              GType support.
4187  */
4188 GAuto *g_auto_pointer_new(gpointer p)
4189 {
4190         AutoPointerRef *ref;
4191         AutoPointer    *ptr;
4192
4193         if (p == NULL)
4194                 return NULL;
4195
4196         ref = g_new0(AutoPointerRef, 1);
4197         ptr = g_new0(AutoPointer, 1);
4198
4199         ref->pointer = p;
4200         ref->free = g_free;
4201         ref->cnt = 1;
4202
4203         ptr->ref = ref;
4204         ptr->ptr = p;
4205
4206 #ifdef REF_DEBUG
4207         G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4208 #endif
4209         return ptr;
4210 }
4211
4212 /*!
4213  *\brief        Allocate an autopointer using the passed \a free function to
4214  *              free the guarded pointer
4215  */
4216 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4217 {
4218         AutoPointer *aptr;
4219
4220         if (p == NULL)
4221                 return NULL;
4222
4223         aptr = g_auto_pointer_new(p);
4224         aptr->ref->free = free_;
4225         return aptr;
4226 }
4227
4228 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4229 {
4230         if (auto_ptr == NULL)
4231                 return NULL;
4232         return ((AutoPointer *) auto_ptr)->ptr;
4233 }
4234
4235 /*!
4236  *\brief        Copies an auto pointer by. It's mostly not necessary
4237  *              to call this function directly, unless you copy/assign
4238  *              the guarded pointer.
4239  *
4240  *\param        auto_ptr Auto pointer returned by previous call to
4241  *              g_auto_pointer_new_XXX()
4242  *
4243  *\return       gpointer An auto pointer
4244  */
4245 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4246 {
4247         AutoPointer     *ptr;
4248         AutoPointerRef  *ref;
4249         AutoPointer     *newp;
4250
4251         if (auto_ptr == NULL)
4252                 return NULL;
4253
4254         ptr = auto_ptr;
4255         ref = ptr->ref;
4256         newp = g_new0(AutoPointer, 1);
4257
4258         newp->ref = ref;
4259         newp->ptr = ref->pointer;
4260         ++(ref->cnt);
4261
4262 #ifdef REF_DEBUG
4263         G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4264 #endif
4265         return newp;
4266 }
4267
4268 /*!
4269  *\brief        Free an auto pointer
4270  */
4271 void g_auto_pointer_free(GAuto *auto_ptr)
4272 {
4273         AutoPointer     *ptr;
4274         AutoPointerRef  *ref;
4275
4276         if (auto_ptr == NULL)
4277                 return;
4278
4279         ptr = auto_ptr;
4280         ref = ptr->ref;
4281
4282         if (--(ref->cnt) == 0) {
4283 #ifdef REF_DEBUG
4284                 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4285 #endif
4286                 ref->free(ref->pointer);
4287                 g_free(ref);
4288         }
4289 #ifdef REF_DEBUG
4290         else
4291                 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4292 #endif
4293         g_free(ptr);
4294 }
4295
4296 void replace_returns(gchar *str)
4297 {
4298         if (!str)
4299                 return;
4300
4301         while (strstr(str, "\n")) {
4302                 *strstr(str, "\n") = ' ';
4303         }
4304         while (strstr(str, "\r")) {
4305                 *strstr(str, "\r") = ' ';
4306         }
4307 }
4308
4309 /* get_uri_part() - retrieves a URI starting from scanpos.
4310                     Returns TRUE if succesful */
4311 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4312                              const gchar **bp, const gchar **ep, gboolean hdr)
4313 {
4314         const gchar *ep_;
4315         gint parenthese_cnt = 0;
4316
4317         g_return_val_if_fail(start != NULL, FALSE);
4318         g_return_val_if_fail(scanpos != NULL, FALSE);
4319         g_return_val_if_fail(bp != NULL, FALSE);
4320         g_return_val_if_fail(ep != NULL, FALSE);
4321
4322         *bp = scanpos;
4323
4324         /* find end point of URI */
4325         for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4326                 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4327                     !IS_ASCII(*(const guchar *)ep_) ||
4328                     strchr("[]{}<>\"", *ep_)) {
4329                         break;
4330                 } else if (strchr("(", *ep_)) {
4331                         parenthese_cnt++;
4332                 } else if (strchr(")", *ep_)) {
4333                         if (parenthese_cnt > 0)
4334                                 parenthese_cnt--;
4335                         else
4336                                 break;
4337                 }
4338         }
4339
4340         /* no punctuation at end of string */
4341
4342         /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4343          * should pass some URI type to this function and decide on that whether
4344          * to perform punctuation stripping */
4345
4346 #define IS_REAL_PUNCT(ch)       (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4347
4348         for (; ep_ - 1 > scanpos + 1 &&
4349                IS_REAL_PUNCT(*(ep_ - 1));
4350              ep_--)
4351                 ;
4352
4353 #undef IS_REAL_PUNCT
4354
4355         *ep = ep_;
4356
4357         return TRUE;
4358 }
4359
4360 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4361 {
4362         while (bp && *bp && g_ascii_isspace(*bp))
4363                 bp++;
4364         return g_strndup(bp, ep - bp);
4365 }
4366
4367 /* valid mail address characters */
4368 #define IS_RFC822_CHAR(ch) \
4369         (IS_ASCII(ch) && \
4370          (ch) > 32   && \
4371          (ch) != 127 && \
4372          !g_ascii_isspace(ch) && \
4373          !strchr("(),;<>\"", (ch)))
4374
4375 /* alphabet and number within 7bit ASCII */
4376 #define IS_ASCII_ALNUM(ch)      (IS_ASCII(ch) && g_ascii_isalnum(ch))
4377 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4378
4379 static GHashTable *create_domain_tab(void)
4380 {
4381         static const gchar *toplvl_domains [] = {
4382             "museum", "aero",
4383             "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4384             "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4385             "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4386             "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4387             "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4388             "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4389             "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4390             "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4391             "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4392             "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4393             "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4394             "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4395             "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4396             "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4397             "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4398             "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4399             "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4400             "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4401             "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4402             "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4403             "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4404             "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4405             "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4406             "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4407             "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4408             "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4409         };
4410         gint n;
4411         GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4412
4413         g_return_val_if_fail(htab, NULL);
4414         for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4415                 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4416         return htab;
4417 }
4418
4419 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4420 {
4421         const gint MAX_LVL_DOM_NAME_LEN = 6;
4422         gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4423         const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4424         register gchar *p;
4425
4426         if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4427                 return FALSE;
4428
4429         for (p = buf; p < m &&  first < last; *p++ = *first++)
4430                 ;
4431         *p = 0;
4432
4433         return g_hash_table_lookup(tab, buf) != NULL;
4434 }
4435
4436 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4437 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4438                                const gchar **bp, const gchar **ep, gboolean hdr)
4439 {
4440         /* more complex than the uri part because we need to scan back and forward starting from
4441          * the scan position. */
4442         gboolean result = FALSE;
4443         const gchar *bp_ = NULL;
4444         const gchar *ep_ = NULL;
4445         static GHashTable *dom_tab;
4446         const gchar *last_dot = NULL;
4447         const gchar *prelast_dot = NULL;
4448         const gchar *last_tld_char = NULL;
4449
4450         /* the informative part of the email address (describing the name
4451          * of the email address owner) may contain quoted parts. the
4452          * closure stack stores the last encountered quotes. */
4453         gchar closure_stack[128];
4454         gchar *ptr = closure_stack;
4455
4456         g_return_val_if_fail(start != NULL, FALSE);
4457         g_return_val_if_fail(scanpos != NULL, FALSE);
4458         g_return_val_if_fail(bp != NULL, FALSE);
4459         g_return_val_if_fail(ep != NULL, FALSE);
4460
4461         if (hdr) {
4462                 const gchar *start_quote = NULL;
4463                 const gchar *end_quote = NULL;
4464 search_again:
4465                 /* go to the real start */
4466                 if (start[0] == ',')
4467                         start++;
4468                 if (start[0] == ';')
4469                         start++;
4470                 while (start[0] == '\n' || start[0] == '\r')
4471                         start++;
4472                 while (start[0] == ' ' || start[0] == '\t')
4473                         start++;
4474
4475                 *bp = start;
4476                 
4477                 /* check if there are quotes (to skip , in them) */
4478                 if (*start == '"') {
4479                         start_quote = start;
4480                         start++;
4481                         end_quote = strstr(start, "\"");
4482                 } else {
4483                         start_quote = NULL;
4484                         end_quote = NULL;
4485                 }
4486                 
4487                 /* skip anything between quotes */
4488                 if (start_quote && end_quote) {
4489                         start = end_quote;
4490                         
4491                 } 
4492
4493                 /* find end (either , or ; or end of line) */
4494                 if (strstr(start, ",") && strstr(start, ";"))
4495                         *ep = strstr(start,",") < strstr(start, ";")
4496                                 ? strstr(start, ",") : strstr(start, ";");
4497                 else if (strstr(start, ","))
4498                         *ep = strstr(start, ",");
4499                 else if (strstr(start, ";"))
4500                         *ep = strstr(start, ";");
4501                 else
4502                         *ep = start+strlen(start);
4503
4504                 /* go back to real start */
4505                 if (start_quote && end_quote) {
4506                         start = start_quote;
4507                 }
4508
4509                 /* check there's still an @ in that, or search
4510                  * further if possible */
4511                 if (strstr(start, "@") && strstr(start, "@") < *ep)
4512                         return TRUE;
4513                 else if (*ep < start+strlen(start)) {
4514                         start = *ep;
4515                         goto search_again;
4516                 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4517                         *bp = start_quote;
4518                         return TRUE;
4519                 } else
4520                         return FALSE;
4521         }
4522
4523         if (!dom_tab)
4524                 dom_tab = create_domain_tab();
4525         g_return_val_if_fail(dom_tab, FALSE);
4526
4527         /* scan start of address */
4528         for (bp_ = scanpos - 1;
4529              bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4530                 ;
4531
4532         /* TODO: should start with an alnum? */
4533         bp_++;
4534         for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4535                 ;
4536
4537         if (bp_ != scanpos) {
4538                 /* scan end of address */
4539                 for (ep_ = scanpos + 1;
4540                      *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4541                         if (*ep_ == '.') {
4542                                 prelast_dot = last_dot;
4543                                 last_dot = ep_;
4544                                 if (*(last_dot + 1) == '.') {
4545                                         if (prelast_dot == NULL)
4546                                                 return FALSE;
4547                                         last_dot = prelast_dot;
4548                                         break;
4549                                 }
4550                         }
4551
4552                 /* TODO: really should terminate with an alnum? */
4553                 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4554                      --ep_)
4555                         ;
4556                 ep_++;
4557
4558                 if (last_dot == NULL)
4559                         return FALSE;
4560                 if (last_dot >= ep_)
4561                         last_dot = prelast_dot;
4562                 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4563                         return FALSE;
4564                 last_dot++;
4565
4566                 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4567                         if (*last_tld_char == '?')
4568                                 break;
4569
4570                 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4571                         result = TRUE;
4572
4573                 *ep = ep_;
4574                 *bp = bp_;
4575         }
4576
4577         if (!result) return FALSE;
4578
4579         if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4580         && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4581         && IS_RFC822_CHAR(*(ep_ + 3))) {
4582                 /* this informative part with an @ in it is
4583                  * followed by the email address */
4584                 ep_ += 3;
4585
4586                 /* go to matching '>' (or next non-rfc822 char, like \n) */
4587                 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4588                         ;
4589
4590                 /* include the bracket */
4591                 if (*ep_ == '>') ep_++;
4592
4593                 /* include the leading quote */
4594                 bp_--;
4595
4596                 *ep = ep_;
4597                 *bp = bp_;
4598                 return TRUE;
4599         }
4600
4601         /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4602         if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4603                 return FALSE;
4604
4605         /* see if this is <bracketed>; in this case we also scan for the informative part. */
4606         if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4607                 return TRUE;
4608
4609 #define FULL_STACK()    ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4610 #define IN_STACK()      (ptr > closure_stack)
4611 /* has underrun check */
4612 #define POP_STACK()     if(IN_STACK()) --ptr
4613 /* has overrun check */
4614 #define PUSH_STACK(c)   if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4615 /* has underrun check */
4616 #define PEEK_STACK()    (IN_STACK() ? *(ptr - 1) : 0)
4617
4618         ep_++;
4619
4620         /* scan for the informative part. */
4621         for (bp_ -= 2; bp_ >= start; bp_--) {
4622                 /* if closure on the stack keep scanning */
4623                 if (PEEK_STACK() == *bp_) {
4624                         POP_STACK();
4625                         continue;
4626                 }
4627                 if (*bp_ == '\'' || *bp_ == '"') {
4628                         PUSH_STACK(*bp_);
4629                         continue;
4630                 }
4631
4632                 /* if nothing in the closure stack, do the special conditions
4633                  * the following if..else expression simply checks whether
4634                  * a token is acceptable. if not acceptable, the clause
4635                  * should terminate the loop with a 'break' */
4636                 if (!PEEK_STACK()) {
4637                         if (*bp_ == '-'
4638                         && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4639                         && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
4640                                 /* hyphens are allowed, but only in
4641                                    between alnums */
4642                         } else if (strchr(" \"'", *bp_)) {
4643                                 /* but anything not being a punctiation
4644                                    is ok */
4645                         } else {
4646                                 break; /* anything else is rejected */
4647                         }
4648                 }
4649         }
4650
4651         bp_++;
4652
4653         /* scan forward (should start with an alnum) */
4654         for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4655                 ;
4656 #undef PEEK_STACK
4657 #undef PUSH_STACK
4658 #undef POP_STACK
4659 #undef IN_STACK
4660 #undef FULL_STACK
4661
4662
4663         *bp = bp_;
4664         *ep = ep_;
4665
4666         return result;
4667 }
4668
4669 #undef IS_QUOTE
4670 #undef IS_ASCII_ALNUM
4671 #undef IS_RFC822_CHAR
4672
4673 gchar *make_email_string(const gchar *bp, const gchar *ep)
4674 {
4675         /* returns a mailto: URI; mailto: is also used to detect the
4676          * uri type later on in the button_pressed signal handler */
4677         gchar *tmp;
4678         gchar *result;
4679
4680         tmp = g_strndup(bp, ep - bp);
4681         result = g_strconcat("mailto:", tmp, NULL);
4682         g_free(tmp);
4683
4684         return result;
4685 }
4686
4687 gchar *make_http_string(const gchar *bp, const gchar *ep)
4688 {
4689         /* returns an http: URI; */
4690         gchar *tmp;
4691         gchar *result;
4692
4693         while (bp && *bp && g_ascii_isspace(*bp))
4694                 bp++;
4695         tmp = g_strndup(bp, ep - bp);
4696         result = g_strconcat("http://", tmp, NULL);
4697         g_free(tmp);
4698
4699         return result;
4700 }
4701
4702 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4703 {
4704         FILE *fp = fopen(path, "rb");
4705         gchar buf[BUFFSIZE];
4706         gchar *result = NULL;
4707         if (!fp)
4708                 return NULL;
4709         while (fgets(buf, sizeof (buf), fp) != NULL) {
4710                 gchar **parts = g_strsplit(buf, ";", -1);
4711                 gchar *trimmed = parts[0];
4712                 while (trimmed[0] == ' ')
4713                         trimmed++;
4714                 while (trimmed[strlen(trimmed)-1] == ' ')
4715                         trimmed[strlen(trimmed)-1] = '\0';
4716
4717                 if (!strcmp(trimmed, type)) {
4718                         trimmed = parts[1];
4719                         while (trimmed[0] == ' ')
4720                                 trimmed++;
4721                         while (trimmed[strlen(trimmed)-1] == ' ')
4722                                 trimmed[strlen(trimmed)-1] = '\0';
4723                         while (trimmed[strlen(trimmed)-1] == '\n')
4724                                 trimmed[strlen(trimmed)-1] = '\0';
4725                         while (trimmed[strlen(trimmed)-1] == '\r')
4726                                 trimmed[strlen(trimmed)-1] = '\0';
4727                         result = g_strdup(trimmed);
4728                         g_strfreev(parts);
4729                         fclose(fp);
4730                         if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4731                                 gchar *start = g_strdup(result);
4732                                 gchar *end = g_strdup(strstr(result, "%s")+2);
4733                                 gchar *tmp;
4734                                 *strstr(start, "%s") = '\0';
4735                                 tmp = g_strconcat(start,"'%s'",end, NULL);
4736                                 g_free(start);
4737                                 g_free(end);
4738                                 g_free(result);
4739                                 result = tmp;
4740                         }
4741                         return result;
4742                 }
4743                 g_strfreev(parts);
4744         }
4745         fclose(fp);
4746         return NULL;
4747 }
4748 gchar *mailcap_get_command_for_type(const gchar *type)
4749 {
4750         gchar *result = NULL;
4751         gchar *path = NULL;
4752         if (type == NULL)
4753                 return NULL;
4754         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4755         result = mailcap_get_command_in_file(path, type);
4756         g_free(path);
4757         if (result)
4758                 return result;
4759         result = mailcap_get_command_in_file("/etc/mailcap", type);
4760         return result;
4761 }
4762
4763 gint copy_dir(const gchar *src, const gchar *dst)
4764 {
4765         GDir *dir;
4766         const gchar *name;
4767
4768         if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4769                 g_warning("failed to open directory: %s\n", src);
4770                 return -1;
4771         }
4772
4773         if (make_dir(dst) < 0)
4774                 return -1;
4775
4776         while ((name = g_dir_read_name(dir)) != NULL) {
4777                 gchar *old_file, *new_file;
4778                 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4779                 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4780                 debug_print("copying: %s -> %s\n", old_file, new_file);
4781                 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4782                         gint r = copy_file(old_file, new_file, TRUE);
4783                         if (r < 0)
4784                                 return r;
4785                 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4786                         gint r = copy_dir(old_file, new_file);
4787                         if (r < 0)
4788                                 return r;
4789                 }
4790         }
4791         return 0;
4792 }
4793
4794 /* crude test to see if a file is an email. */
4795 gboolean file_is_email (const gchar *filename)
4796 {
4797         FILE *fp = NULL;
4798         gchar buffer[2048];
4799         gint i = 0;
4800         gint score = 0;
4801         if (filename == NULL)
4802                 return FALSE;
4803         if ((fp = g_fopen(filename, "rb")) == NULL)
4804                 return FALSE;
4805         while (i < 60 && score < 3
4806                && fgets(buffer, sizeof (buffer), fp) > 0) {
4807                 if (!strncmp(buffer, "From:", strlen("From:")))
4808                         score++;
4809                 if (!strncmp(buffer, "To:", strlen("To:")))
4810                         score++;
4811                 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4812                         score++;
4813                 i++;
4814         }
4815         fclose(fp);
4816         return (score >= 3);
4817 }