2004-10-29 [colin] 0.9.12cvs136
[claws.git] / src / common / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
33 #  include <wchar.h>
34 #  include <wctype.h>
35 #endif
36 #include <stdlib.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <dirent.h>
43 #include <time.h>
44 #include <regex.h>
45 #include <sys/utsname.h>
46
47 #include "intl.h"
48 #include "utils.h"
49 #include "socket.h"
50
51 #define BUFFSIZE        8192
52
53 static gboolean debug_mode = FALSE;
54
55 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data);
56
57 void list_free_strings(GList *list)
58 {
59         list = g_list_first(list);
60
61         while (list != NULL) {
62                 g_free(list->data);
63                 list = list->next;
64         }
65 }
66
67 void slist_free_strings(GSList *list)
68 {
69         while (list != NULL) {
70                 g_free(list->data);
71                 list = list->next;
72         }
73 }
74
75 GSList *slist_concat_unique (GSList *first, GSList *second)
76 {
77         GSList *tmp, *ret;
78         if (first == NULL) {
79                 if (second == NULL)
80                         return NULL;
81                 else 
82                         return second;
83         } else if (second == NULL)
84                 return first;
85         ret = first;
86         for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
87                 if (g_slist_find(ret, tmp->data) == NULL)
88                         ret = g_slist_prepend(ret, tmp->data);
89         }
90         return ret;
91 }
92  
93 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
94 {
95         g_free(key);
96 }
97
98 void hash_free_strings(GHashTable *table)
99 {
100         g_hash_table_foreach(table, hash_free_strings_func, NULL);
101 }
102
103 static void hash_free_value_mem_func(gpointer key, gpointer value,
104                                      gpointer data)
105 {
106         g_free(value);
107 }
108
109 void hash_free_value_mem(GHashTable *table)
110 {
111         g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
112 }
113
114 gint str_case_equal(gconstpointer v, gconstpointer v2)
115 {
116         return g_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
117 }
118
119 guint str_case_hash(gconstpointer key)
120 {
121         const gchar *p = key;
122         guint h = *p;
123
124         if (h) {
125                 h = tolower(h);
126                 for (p += 1; *p != '\0'; p++)
127                         h = (h << 5) - h + tolower(*p);
128         }
129
130         return h;
131 }
132
133 void ptr_array_free_strings(GPtrArray *array)
134 {
135         gint i;
136         gchar *str;
137
138         g_return_if_fail(array != NULL);
139
140         for (i = 0; i < array->len; i++) {
141                 str = g_ptr_array_index(array, i);
142                 g_free(str);
143         }
144 }
145
146 gint to_number(const gchar *nstr)
147 {
148         register const guchar *p;
149
150         if (*nstr == '\0') return -1;
151
152         for (p = nstr; *p != '\0'; p++)
153                 if (!isdigit(*p)) return -1;
154
155         return atoi(nstr);
156 }
157
158 /* convert integer into string,
159    nstr must be not lower than 11 characters length */
160 gchar *itos_buf(gchar *nstr, gint n)
161 {
162         g_snprintf(nstr, 11, "%d", n);
163         return nstr;
164 }
165
166 /* convert integer into string */
167 gchar *itos(gint n)
168 {
169         static gchar nstr[11];
170
171         return itos_buf(nstr, n);
172 }
173
174 gchar *to_human_readable(off_t size)
175 {
176         static gchar str[10];
177
178         if (size < 1024)
179                 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
180         else if (size >> 10 < 1024)
181                 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
182         else if (size >> 20 < 1024)
183                 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
184         else
185                 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
186
187         return str;
188 }
189
190 /* strcmp with NULL-checking */
191 gint strcmp2(const gchar *s1, const gchar *s2)
192 {
193         if (s1 == NULL || s2 == NULL)
194                 return -1;
195         else
196                 return strcmp(s1, s2);
197 }
198 /* strstr with NULL-checking */
199 gchar *strstr2(const gchar *s1, const gchar *s2)
200 {
201         if (s1 == NULL || s2 == NULL)
202                 return NULL;
203         else
204                 return strstr(s1, s2);
205 }
206 /* compare paths */
207 gint path_cmp(const gchar *s1, const gchar *s2)
208 {
209         gint len1, len2;
210
211         if (s1 == NULL || s2 == NULL) return -1;
212         if (*s1 == '\0' || *s2 == '\0') return -1;
213
214         len1 = strlen(s1);
215         len2 = strlen(s2);
216
217         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
218         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
219
220         return strncmp(s1, s2, MAX(len1, len2));
221 }
222
223 /* remove trailing return code */
224 gchar *strretchomp(gchar *str)
225 {
226         register gchar *s;
227
228         if (!*str) return str;
229
230         for (s = str + strlen(str) - 1;
231              s >= str && (*s == '\n' || *s == '\r');
232              s--)
233                 *s = '\0';
234
235         return str;
236 }
237
238 /* remove trailing character */
239 gchar *strtailchomp(gchar *str, gchar tail_char)
240 {
241         register gchar *s;
242
243         if (!*str) return str;
244         if (tail_char == '\0') return str;
245
246         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
247                 *s = '\0';
248
249         return str;
250 }
251
252 /* remove CR (carriage return) */
253 gchar *strcrchomp(gchar *str)
254 {
255         register gchar *s;
256
257         if (!*str) return str;
258
259         s = str + strlen(str) - 1;
260         if (*s == '\n' && s > str && *(s - 1) == '\r') {
261                 *(s - 1) = '\n';
262                 *s = '\0';
263         }
264
265         return str;
266 }
267
268 /* Similar to `strstr' but this function ignores the case of both strings.  */
269 gchar *strcasestr(const gchar *haystack, const gchar *needle)
270 {
271         register size_t haystack_len, needle_len;
272
273         haystack_len = strlen(haystack);
274         needle_len   = strlen(needle);
275
276         if (haystack_len < needle_len || needle_len == 0)
277                 return NULL;
278
279         while (haystack_len >= needle_len) {
280                 if (!g_strncasecmp(haystack, needle, needle_len))
281                         return (gchar *)haystack;
282                 else {
283                         haystack++;
284                         haystack_len--;
285                 }
286         }
287
288         return NULL;
289 }
290
291 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
292 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
293 {
294         register gchar c;
295         gchar *s = dest;
296
297         do {
298                 if (--n == 0) {
299                         *dest = '\0';
300                         return s;
301                 }
302                 c = *src++;
303                 *dest++ = c;
304         } while (c != '\0');
305
306         /* don't do zero fill */
307         return s;
308 }
309
310 #if !HAVE_ISWALNUM
311 int iswalnum(wint_t wc)
312 {
313         return isalnum((int)wc);
314 }
315 #endif
316
317 #if !HAVE_ISWSPACE
318 int iswspace(wint_t wc)
319 {
320         return isspace((int)wc);
321 }
322 #endif
323
324 #if !HAVE_TOWLOWER
325 wint_t towlower(wint_t wc)
326 {
327         if (wc >= L'A' && wc <= L'Z')
328                 return wc + L'a' - L'A';
329
330         return wc;
331 }
332 #endif
333
334 #if !HAVE_WCSLEN
335 size_t wcslen(const wchar_t *s)
336 {
337         size_t len = 0;
338
339         while (*s != L'\0')
340                 ++len, ++s;
341
342         return len;
343 }
344 #endif
345
346 #if !HAVE_WCSCPY
347 /* Copy SRC to DEST.  */
348 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
349 {
350         wint_t c;
351         wchar_t *s = dest;
352
353         do {
354                 c = *src++;
355                 *dest++ = c;
356         } while (c != L'\0');
357
358         return s;
359 }
360 #endif
361
362 #if !HAVE_WCSNCPY
363 /* Copy no more than N wide-characters of SRC to DEST.  */
364 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
365 {
366         wint_t c;
367         wchar_t *s = dest;
368
369         do {
370                 c = *src++;
371                 *dest++ = c;
372                 if (--n == 0)
373                         return s;
374         } while (c != L'\0');
375
376         /* zero fill */
377         do
378                 *dest++ = L'\0';
379         while (--n > 0);
380
381         return s;
382 }
383 #endif
384
385 /* Duplicate S, returning an identical malloc'd string. */
386 wchar_t *wcsdup(const wchar_t *s)
387 {
388         wchar_t *new_str;
389
390         if (s) {
391                 new_str = g_new(wchar_t, wcslen(s) + 1);
392                 wcscpy(new_str, s);
393         } else
394                 new_str = NULL;
395
396         return new_str;
397 }
398
399 /* Duplicate no more than N wide-characters of S,
400    returning an identical malloc'd string. */
401 wchar_t *wcsndup(const wchar_t *s, size_t n)
402 {
403         wchar_t *new_str;
404
405         if (s) {
406                 new_str = g_new(wchar_t, n + 1);
407                 wcsncpy(new_str, s, n);
408                 new_str[n] = (wchar_t)0;
409         } else
410                 new_str = NULL;
411
412         return new_str;
413 }
414
415 wchar_t *strdup_mbstowcs(const gchar *s)
416 {
417         wchar_t *new_str;
418
419         if (s) {
420                 new_str = g_new(wchar_t, strlen(s) + 1);
421                 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
422                         g_free(new_str);
423                         new_str = NULL;
424                 } else
425                         new_str = g_realloc(new_str,
426                                             sizeof(wchar_t) * (wcslen(new_str) + 1));
427         } else
428                 new_str = NULL;
429
430         return new_str;
431 }
432
433 gchar *strdup_wcstombs(const wchar_t *s)
434 {
435         gchar *new_str;
436         size_t len;
437
438         if (s) {
439                 len = wcslen(s) * MB_CUR_MAX + 1;
440                 new_str = g_new(gchar, len);
441                 if (wcstombs(new_str, s, len) < 0) {
442                         g_free(new_str);
443                         new_str = NULL;
444                 } else
445                         new_str = g_realloc(new_str, strlen(new_str) + 1);
446         } else
447                 new_str = NULL;
448
449         return new_str;
450 }
451
452 /* Compare S1 and S2, ignoring case.  */
453 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
454 {
455         wint_t c1;
456         wint_t c2;
457
458         while (n--) {
459                 c1 = towlower(*s1++);
460                 c2 = towlower(*s2++);
461                 if (c1 != c2)
462                         return c1 - c2;
463                 else if (c1 == 0 && c2 == 0)
464                         break;
465         }
466
467         return 0;
468 }
469
470 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
471 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
472 {
473         register size_t haystack_len, needle_len;
474
475         haystack_len = wcslen(haystack);
476         needle_len   = wcslen(needle);
477
478         if (haystack_len < needle_len || needle_len == 0)
479                 return NULL;
480
481         while (haystack_len >= needle_len) {
482                 if (!wcsncasecmp(haystack, needle, needle_len))
483                         return (wchar_t *)haystack;
484                 else {
485                         haystack++;
486                         haystack_len--;
487                 }
488         }
489
490         return NULL;
491 }
492
493 gint get_mbs_len(const gchar *s)
494 {
495         const gchar *p = s;
496         gint mb_len;
497         gint len = 0;
498
499         if (!p)
500                 return -1;
501
502         while (*p != '\0') {
503                 mb_len = mblen(p, MB_LEN_MAX);
504                 if (mb_len == 0)
505                         break;
506                 else if (mb_len < 0)
507                         return -1;
508                 else
509                         len++;
510
511                 p += mb_len;
512         }
513
514         return len;
515 }
516
517 /* Examine if next block is non-ASCII string */
518 gboolean is_next_nonascii(const guchar *s)
519 {
520         const guchar *p;
521
522         /* skip head space */
523         for (p = s; *p != '\0' && isspace(*p); p++)
524                 ;
525         for (; *p != '\0' && !isspace(*p); p++) {
526                 if (*p > 127 || *p < 32)
527                         return TRUE;
528         }
529
530         return FALSE;
531 }
532
533 gint get_next_word_len(const guchar *s)
534 {
535         gint len = 0;
536
537         for (; *s != '\0' && !isspace(*s); s++, len++)
538                 ;
539
540         return len;
541 }
542
543 /* compare subjects */
544 gint subject_compare(const gchar *s1, const gchar *s2)
545 {
546         gchar *str1, *str2;
547
548         if (!s1 || !s2) return -1;
549         if (!*s1 || !*s2) return -1;
550
551         Xstrdup_a(str1, s1, return -1);
552         Xstrdup_a(str2, s2, return -1);
553
554         trim_subject_for_compare(str1);
555         trim_subject_for_compare(str2);
556
557         if (!*str1 || !*str2) return -1;
558
559         return strcmp(str1, str2);
560 }
561
562 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
563 {
564         gchar *str1, *str2;
565
566         if (!s1 || !s2) return -1;
567
568         Xstrdup_a(str1, s1, return -1);
569         Xstrdup_a(str2, s2, return -1);
570
571         trim_subject_for_sort(str1);
572         trim_subject_for_sort(str2);
573
574         return g_strcasecmp(str1, str2);
575 }
576
577 void trim_subject_for_compare(gchar *str)
578 {
579         gchar *srcp;
580
581         eliminate_parenthesis(str, '[', ']');
582         eliminate_parenthesis(str, '(', ')');
583         g_strstrip(str);
584
585         srcp = str + subject_get_prefix_length(str);
586         if (srcp != str)
587                 memmove(str, srcp, strlen(srcp) + 1);
588 }
589
590 void trim_subject_for_sort(gchar *str)
591 {
592         gchar *srcp;
593
594         g_strstrip(str);
595
596         srcp = str + subject_get_prefix_length(str);
597         if (srcp != str)        
598                 memmove(str, srcp, strlen(srcp) + 1);
599 }
600
601 void trim_subject(gchar *str)
602 {
603         register guchar *srcp, *destp;
604         gchar op, cl;
605         gint in_brace;
606
607         destp = str + subject_get_prefix_length(str);
608
609         if (*destp == '[') {
610                 op = '[';
611                 cl = ']';
612         } else if (*destp == '(') {
613                 op = '(';
614                 cl = ')';
615         } else
616                 return;
617
618         srcp = destp + 1;
619         in_brace = 1;
620         while (*srcp) {
621                 if (*srcp == op)
622                         in_brace++;
623                 else if (*srcp == cl)
624                         in_brace--;
625                 srcp++;
626                 if (in_brace == 0)
627                         break;
628         }
629         while (isspace(*srcp)) srcp++;
630         memmove(destp, srcp, strlen(srcp) + 1);
631 }
632
633 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
634 {
635         register guchar *srcp, *destp;
636         gint in_brace;
637
638         srcp = destp = str;
639
640         while ((destp = strchr(destp, op))) {
641                 in_brace = 1;
642                 srcp = destp + 1;
643                 while (*srcp) {
644                         if (*srcp == op)
645                                 in_brace++;
646                         else if (*srcp == cl)
647                                 in_brace--;
648                         srcp++;
649                         if (in_brace == 0)
650                                 break;
651                 }
652                 while (isspace(*srcp)) srcp++;
653                 memmove(destp, srcp, strlen(srcp) + 1);
654         }
655 }
656
657 void extract_parenthesis(gchar *str, gchar op, gchar cl)
658 {
659         register gchar *srcp, *destp;
660         gint in_brace;
661
662         srcp = destp = str;
663
664         while ((srcp = strchr(destp, op))) {
665                 if (destp > str)
666                         *destp++ = ' ';
667                 memmove(destp, srcp + 1, strlen(srcp));
668                 in_brace = 1;
669                 while(*destp) {
670                         if (*destp == op)
671                                 in_brace++;
672                         else if (*destp == cl)
673                                 in_brace--;
674
675                         if (in_brace == 0)
676                                 break;
677
678                         destp++;
679                 }
680         }
681         *destp = '\0';
682 }
683
684 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
685                                          gchar op, gchar cl)
686 {
687         register gchar *srcp, *destp;
688         gint in_brace;
689         gboolean in_quote = FALSE;
690
691         srcp = destp = str;
692
693         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
694                 if (destp > str)
695                         *destp++ = ' ';
696                 memmove(destp, srcp + 1, strlen(srcp));
697                 in_brace = 1;
698                 while(*destp) {
699                         if (*destp == op && !in_quote)
700                                 in_brace++;
701                         else if (*destp == cl && !in_quote)
702                                 in_brace--;
703                         else if (*destp == quote_chr)
704                                 in_quote ^= TRUE;
705
706                         if (in_brace == 0)
707                                 break;
708
709                         destp++;
710                 }
711         }
712         *destp = '\0';
713 }
714
715 void eliminate_quote(gchar *str, gchar quote_chr)
716 {
717         register guchar *srcp, *destp;
718
719         srcp = destp = str;
720
721         while ((destp = strchr(destp, quote_chr))) {
722                 if ((srcp = strchr(destp + 1, quote_chr))) {
723                         srcp++;
724                         while (isspace(*srcp)) srcp++;
725                         memmove(destp, srcp, strlen(srcp) + 1);
726                 } else {
727                         *destp = '\0';
728                         break;
729                 }
730         }
731 }
732
733 void extract_quote(gchar *str, gchar quote_chr)
734 {
735         register gchar *p;
736
737         if ((str = strchr(str, quote_chr))) {
738                 p = str;
739                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
740                         memmove(p - 1, p, strlen(p) + 1);
741                         p--;
742                 }
743                 if(p) {
744                         *p = '\0';
745                         memmove(str, str + 1, p - str);
746                 }
747         }
748 }
749
750 void eliminate_address_comment(gchar *str)
751 {
752         register guchar *srcp, *destp;
753         gint in_brace;
754
755         srcp = destp = str;
756
757         while ((destp = strchr(destp, '"'))) {
758                 if ((srcp = strchr(destp + 1, '"'))) {
759                         srcp++;
760                         if (*srcp == '@') {
761                                 destp = srcp + 1;
762                         } else {
763                                 while (isspace(*srcp)) srcp++;
764                                 memmove(destp, srcp, strlen(srcp) + 1);
765                         }
766                 } else {
767                         *destp = '\0';
768                         break;
769                 }
770         }
771
772         srcp = destp = str;
773
774         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
775                 in_brace = 1;
776                 srcp = destp + 1;
777                 while (*srcp) {
778                         if (*srcp == '(')
779                                 in_brace++;
780                         else if (*srcp == ')')
781                                 in_brace--;
782                         srcp++;
783                         if (in_brace == 0)
784                                 break;
785                 }
786                 while (isspace(*srcp)) srcp++;
787                 memmove(destp, srcp, strlen(srcp) + 1);
788         }
789 }
790
791 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
792 {
793         gboolean in_quote = FALSE;
794
795         while (*str) {
796                 if (*str == c && !in_quote)
797                         return (gchar *)str;
798                 if (*str == quote_chr)
799                         in_quote ^= TRUE;
800                 str++;
801         }
802
803         return NULL;
804 }
805
806 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
807 {
808         gboolean in_quote = FALSE;
809         const gchar *p;
810
811         p = str + strlen(str) - 1;
812         while (p >= str) {
813                 if (*p == c && !in_quote)
814                         return (gchar *)p;
815                 if (*p == quote_chr)
816                         in_quote ^= TRUE;
817                 p--;
818         }
819
820         return NULL;
821 }
822
823 void extract_address(gchar *str)
824 {
825         eliminate_address_comment(str);
826         if (strchr_with_skip_quote(str, '"', '<'))
827                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
828         g_strstrip(str);
829 }
830
831 void extract_list_id_str(gchar *str)
832 {
833         if (strchr_with_skip_quote(str, '"', '<'))
834                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
835         g_strstrip(str);
836 }
837
838 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
839 {
840         gchar *work;
841         gchar *workp;
842
843         if (!str) return addr_list;
844
845         Xstrdup_a(work, str, return addr_list);
846
847         if (removecomments)
848                 eliminate_address_comment(work);
849         workp = work;
850
851         while (workp && *workp) {
852                 gchar *p, *next;
853
854                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
855                         *p = '\0';
856                         next = p + 1;
857                 } else
858                         next = NULL;
859
860                 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
861                         extract_parenthesis_with_skip_quote
862                                 (workp, '"', '<', '>');
863
864                 g_strstrip(workp);
865                 if (*workp)
866                         addr_list = g_slist_append(addr_list, g_strdup(workp));
867
868                 workp = next;
869         }
870
871         return addr_list;
872 }
873
874 GSList *address_list_append(GSList *addr_list, const gchar *str)
875 {
876         return address_list_append_real(addr_list, str, TRUE);
877 }
878
879 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
880 {
881         return address_list_append_real(addr_list, str, FALSE);
882 }
883
884 GSList *references_list_append(GSList *msgid_list, const gchar *str)
885 {
886         const gchar *strp;
887
888         if (!str) return msgid_list;
889         strp = str;
890
891         while (strp && *strp) {
892                 const gchar *start, *end;
893                 gchar *msgid;
894
895                 if ((start = strchr(strp, '<')) != NULL) {
896                         end = strchr(start + 1, '>');
897                         if (!end) break;
898                 } else
899                         break;
900
901                 msgid = g_strndup(start + 1, end - start - 1);
902                 g_strstrip(msgid);
903                 if (*msgid)
904                         msgid_list = g_slist_append(msgid_list, msgid);
905                 else
906                         g_free(msgid);
907
908                 strp = end + 1;
909         }
910
911         return msgid_list;
912 }
913
914 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
915 {
916         gchar *work;
917         gchar *workp;
918
919         if (!str) return group_list;
920
921         Xstrdup_a(work, str, return group_list);
922
923         workp = work;
924
925         while (workp && *workp) {
926                 gchar *p, *next;
927
928                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
929                         *p = '\0';
930                         next = p + 1;
931                 } else
932                         next = NULL;
933
934                 g_strstrip(workp);
935                 if (*workp)
936                         group_list = g_slist_append(group_list,
937                                                     g_strdup(workp));
938
939                 workp = next;
940         }
941
942         return group_list;
943 }
944
945 GList *add_history(GList *list, const gchar *str)
946 {
947         GList *old;
948
949         g_return_val_if_fail(str != NULL, list);
950
951         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
952         if (old) {
953                 g_free(old->data);
954                 list = g_list_remove(list, old->data);
955         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
956                 GList *last;
957
958                 last = g_list_last(list);
959                 if (last) {
960                         g_free(last->data);
961                         g_list_remove(list, last->data);
962                 }
963         }
964
965         list = g_list_prepend(list, g_strdup(str));
966
967         return list;
968 }
969
970 void remove_return(gchar *str)
971 {
972         register gchar *p = str;
973
974         while (*p) {
975                 if (*p == '\n' || *p == '\r')
976                         memmove(p, p + 1, strlen(p));
977                 else
978                         p++;
979         }
980 }
981
982 void remove_space(gchar *str)
983 {
984         register guchar *p = str;
985         register gint spc;
986
987         while (*p) {
988                 spc = 0;
989                 while (isspace(*(p + spc)))
990                         spc++;
991                 if (spc)
992                         memmove(p, p + spc, strlen(p + spc) + 1);
993                 else
994                         p++;
995         }
996 }
997
998 void unfold_line(gchar *str)
999 {
1000         register guchar *p = str;
1001         register gint spc;
1002
1003         while (*p) {
1004                 if (*p == '\n' || *p == '\r') {
1005                         *p++ = ' ';
1006                         spc = 0;
1007                         while (isspace(*(p + spc)))
1008                                 spc++;
1009                         if (spc)
1010                                 memmove(p, p + spc, strlen(p + spc) + 1);
1011                 } else
1012                         p++;
1013         }
1014 }
1015
1016 void subst_char(gchar *str, gchar orig, gchar subst)
1017 {
1018         register gchar *p = str;
1019
1020         while (*p) {
1021                 if (*p == orig)
1022                         *p = subst;
1023                 p++;
1024         }
1025 }
1026
1027 void subst_chars(gchar *str, gchar *orig, gchar subst)
1028 {
1029         register gchar *p = str;
1030
1031         while (*p) {
1032                 if (strchr(orig, *p) != NULL)
1033                         *p = subst;
1034                 p++;
1035         }
1036 }
1037
1038 void subst_for_filename(gchar *str)
1039 {
1040         subst_chars(str, " \t\r\n\"/\\", '_');
1041 }
1042
1043 void subst_for_shellsafe_filename(gchar *str)
1044 {
1045         subst_for_filename(str);
1046         subst_chars(str, "|&;()<>'!{}[]",'_');
1047 }
1048
1049 gboolean is_header_line(const gchar *str)
1050 {
1051         if (str[0] == ':') return FALSE;
1052
1053         while (*str != '\0' && *str != ' ') {
1054                 if (*str == ':')
1055                         return TRUE;
1056                 str++;
1057         }
1058
1059         return FALSE;
1060 }
1061
1062 gboolean is_ascii_str(const guchar *str)
1063 {
1064         g_return_val_if_fail(str, FALSE);
1065
1066         while (*str != '\0') {
1067                 if (*str != '\t' && *str != ' ' &&
1068                     *str != '\r' && *str != '\n' &&
1069                     (*str < 32 || *str >= 127))
1070                         return FALSE;
1071                 str++;
1072         }
1073
1074         return TRUE;
1075 }
1076
1077 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1078 {
1079         const guchar *first_pos;
1080         const guchar *last_pos;
1081         const guchar *p = str;
1082         gint quote_level = -1;
1083
1084         /* speed up line processing by only searching to the last '>' */
1085         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1086                 /* skip a line if it contains a '<' before the initial '>' */
1087                 if (memchr(str, '<', first_pos - (const guchar *)str) != NULL)
1088                         return -1;
1089                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1090         } else
1091                 return -1;
1092
1093         while (p <= last_pos) {
1094                 while (p < last_pos) {
1095                         if (isspace(*p))
1096                                 p++;
1097                         else
1098                                 break;
1099                 }
1100
1101                 if (strchr(quote_chars, *p))
1102                         quote_level++;
1103                 else if (*p != '-' && !isspace(*p) && p <= last_pos) {
1104                         /* any characters are allowed except '-' and space */
1105                         while (*p != '-' 
1106                                && !strchr(quote_chars, *p) 
1107                                && !isspace(*p) 
1108                                && p < last_pos)
1109                                 p++;
1110                         if (strchr(quote_chars, *p))
1111                                 quote_level++;
1112                         else
1113                                 break;
1114                 }
1115
1116                 p++;
1117         }
1118
1119         return quote_level;
1120 }
1121
1122 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars) 
1123 {
1124         gchar * position = NULL;
1125         gchar * tmp_pos = NULL;
1126         int i;
1127
1128         if (quote_chars == NULL)
1129                 return FALSE;
1130         
1131         for (i = 0; i < strlen(quote_chars); i++) {
1132                 tmp_pos = strchr (str,  quote_chars[i]);
1133                 if(position == NULL 
1134                    || (tmp_pos != NULL && position >= tmp_pos) )
1135                         position = tmp_pos;
1136         }
1137         return position; 
1138 }
1139
1140 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars) 
1141 {
1142         gchar * position = NULL;
1143         gchar * tmp_pos = NULL;
1144         int i;
1145
1146         if (quote_chars == NULL)
1147                 return FALSE;
1148         
1149         for (i = 0; i < strlen(quote_chars); i++) {
1150                 tmp_pos = strrchr (str, quote_chars[i]);
1151                 if(position == NULL 
1152                    || (tmp_pos != NULL && position <= tmp_pos) )
1153                         position = tmp_pos;
1154         }
1155         return position; 
1156 }
1157
1158 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1159 {
1160         register guint haystack_len, needle_len;
1161         gboolean in_squote = FALSE, in_dquote = FALSE;
1162
1163         haystack_len = strlen(haystack);
1164         needle_len   = strlen(needle);
1165
1166         if (haystack_len < needle_len || needle_len == 0)
1167                 return NULL;
1168
1169         while (haystack_len >= needle_len) {
1170                 if (!in_squote && !in_dquote &&
1171                     !strncmp(haystack, needle, needle_len))
1172                         return (gchar *)haystack;
1173
1174                 /* 'foo"bar"' -> foo"bar"
1175                    "foo'bar'" -> foo'bar' */
1176                 if (*haystack == '\'') {
1177                         if (in_squote)
1178                                 in_squote = FALSE;
1179                         else if (!in_dquote)
1180                                 in_squote = TRUE;
1181                 } else if (*haystack == '\"') {
1182                         if (in_dquote)
1183                                 in_dquote = FALSE;
1184                         else if (!in_squote)
1185                                 in_dquote = TRUE;
1186                 }
1187
1188                 haystack++;
1189                 haystack_len--;
1190         }
1191
1192         return NULL;
1193 }
1194
1195 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1196 {
1197         const gchar *p;
1198         gchar quote_chr = '"';
1199         gint in_brace;
1200         gboolean in_quote = FALSE;
1201
1202         p = str;
1203
1204         if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1205                 p++;
1206                 in_brace = 1;
1207                 while (*p) {
1208                         if (*p == op && !in_quote)
1209                                 in_brace++;
1210                         else if (*p == cl && !in_quote)
1211                                 in_brace--;
1212                         else if (*p == quote_chr)
1213                                 in_quote ^= TRUE;
1214
1215                         if (in_brace == 0)
1216                                 return (gchar *)p;
1217
1218                         p++;
1219                 }
1220         }
1221
1222         return NULL;
1223 }
1224
1225 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1226                              gint max_tokens)
1227 {
1228         GSList *string_list = NULL, *slist;
1229         gchar **str_array;
1230         const gchar *s_op, *s_cl;
1231         guint i, n = 1;
1232
1233         g_return_val_if_fail(str != NULL, NULL);
1234
1235         if (max_tokens < 1)
1236                 max_tokens = G_MAXINT;
1237
1238         s_op = strchr_with_skip_quote(str, '"', op);
1239         if (!s_op) return NULL;
1240         str = s_op;
1241         s_cl = strchr_parenthesis_close(str, op, cl);
1242         if (s_cl) {
1243                 do {
1244                         guint len;
1245                         gchar *new_string;
1246
1247                         str++;
1248                         len = s_cl - str;
1249                         new_string = g_new(gchar, len + 1);
1250                         strncpy(new_string, str, len);
1251                         new_string[len] = 0;
1252                         string_list = g_slist_prepend(string_list, new_string);
1253                         n++;
1254                         str = s_cl + 1;
1255
1256                         while (*str && isspace(*(guchar *)str)) str++;
1257                         if (*str != op) {
1258                                 string_list = g_slist_prepend(string_list,
1259                                                               g_strdup(""));
1260                                 n++;
1261                                 s_op = strchr_with_skip_quote(str, '"', op);
1262                                 if (!--max_tokens || !s_op) break;
1263                                 str = s_op;
1264                         } else
1265                                 s_op = str;
1266                         s_cl = strchr_parenthesis_close(str, op, cl);
1267                 } while (--max_tokens && s_cl);
1268         }
1269
1270         str_array = g_new(gchar*, n);
1271
1272         i = n - 1;
1273
1274         str_array[i--] = NULL;
1275         for (slist = string_list; slist; slist = slist->next)
1276                 str_array[i--] = slist->data;
1277
1278         g_slist_free(string_list);
1279
1280         return str_array;
1281 }
1282
1283 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1284                             gint max_tokens)
1285 {
1286         GSList *string_list = NULL, *slist;
1287         gchar **str_array, *s, *new_str;
1288         guint i, n = 1, len;
1289
1290         g_return_val_if_fail(str != NULL, NULL);
1291         g_return_val_if_fail(delim != NULL, NULL);
1292
1293         if (max_tokens < 1)
1294                 max_tokens = G_MAXINT;
1295
1296         s = strstr_with_skip_quote(str, delim);
1297         if (s) {
1298                 guint delimiter_len = strlen(delim);
1299
1300                 do {
1301                         len = s - str;
1302                         new_str = g_strndup(str, len);
1303
1304                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1305                                 if (new_str[len - 1] == new_str[0]) {
1306                                         new_str[len - 1] = '\0';
1307                                         memmove(new_str, new_str + 1, len - 1);
1308                                 }
1309                         }
1310                         string_list = g_slist_prepend(string_list, new_str);
1311                         n++;
1312                         str = s + delimiter_len;
1313                         s = strstr_with_skip_quote(str, delim);
1314                 } while (--max_tokens && s);
1315         }
1316
1317         if (*str) {
1318                 new_str = g_strdup(str);
1319                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1320                         len = strlen(str);
1321                         if (new_str[len - 1] == new_str[0]) {
1322                                 new_str[len - 1] = '\0';
1323                                 memmove(new_str, new_str + 1, len - 1);
1324                         }
1325                 }
1326                 string_list = g_slist_prepend(string_list, new_str);
1327                 n++;
1328         }
1329
1330         str_array = g_new(gchar*, n);
1331
1332         i = n - 1;
1333
1334         str_array[i--] = NULL;
1335         for (slist = string_list; slist; slist = slist->next)
1336                 str_array[i--] = slist->data;
1337
1338         g_slist_free(string_list);
1339
1340         return str_array;
1341 }
1342
1343 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1344 {
1345         gchar *abbrev_group;
1346         gchar *ap;
1347         const gchar *p = group;
1348         const gchar *last;
1349
1350         g_return_val_if_fail(group != NULL, NULL);
1351
1352         last = group + strlen(group);
1353         abbrev_group = ap = g_malloc(strlen(group) + 1);
1354
1355         while (*p) {
1356                 while (*p == '.')
1357                         *ap++ = *p++;
1358                 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1359                         *ap++ = *p++;
1360                         while (*p != '.') p++;
1361                 } else {
1362                         strcpy(ap, p);
1363                         return abbrev_group;
1364                 }
1365         }
1366
1367         *ap = '\0';
1368         return abbrev_group;
1369 }
1370
1371 gchar *trim_string(const gchar *str, gint len)
1372 {
1373         const gchar *p = str;
1374         gint mb_len;
1375         gchar *new_str;
1376         gint new_len = 0;
1377
1378         if (!str) return NULL;
1379         if (strlen(str) <= len)
1380                 return g_strdup(str);
1381
1382         while (*p != '\0') {
1383                 mb_len = mblen(p, MB_LEN_MAX);
1384                 if (mb_len == 0)
1385                         break;
1386                 else if (mb_len < 0)
1387                         return g_strdup(str);
1388                 else if (new_len + mb_len > len)
1389                         break;
1390                 else
1391                         new_len += mb_len;
1392                 p += mb_len;
1393         }
1394
1395         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1396         return g_strconcat(new_str, "...", NULL);
1397 }
1398
1399 GList *uri_list_extract_filenames(const gchar *uri_list)
1400 {
1401         GList *result = NULL;
1402         const guchar *p, *q;
1403         gchar *file;
1404
1405         p = uri_list;
1406
1407         while (p) {
1408                 if (*p != '#') {
1409                         while (isspace(*p)) p++;
1410                         if (!strncmp(p, "file:", 5)) {
1411                                 p += 5;
1412                                 q = p;
1413                                 while (*q && *q != '\n' && *q != '\r') q++;
1414
1415                                 if (q > p) {
1416                                         q--;
1417                                         while (q > p && isspace(*q)) q--;
1418                                         file = g_malloc(q - p + 2);
1419                                         strncpy(file, p, q - p + 1);
1420                                         file[q - p + 1] = '\0';
1421                                         result = g_list_append(result,file);
1422                                 }
1423                         }
1424                 }
1425                 p = strchr(p, '\n');
1426                 if (p) p++;
1427         }
1428
1429         return result;
1430 }
1431
1432 #define HEX_TO_INT(val, hex) \
1433 { \
1434         gchar c = hex; \
1435  \
1436         if ('0' <= c && c <= '9') { \
1437                 val = c - '0'; \
1438         } else if ('a' <= c && c <= 'f') { \
1439                 val = c - 'a' + 10; \
1440         } else if ('A' <= c && c <= 'F') { \
1441                 val = c - 'A' + 10; \
1442         } else { \
1443                 val = 0; \
1444         } \
1445 }
1446
1447 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped 
1448  * characters
1449  */
1450 static gint axtoi(const gchar *hexstr)
1451 {
1452         gint hi, lo, result;
1453        
1454         hi = hexstr[0];
1455         if ('0' <= hi && hi <= '9') {
1456                 hi -= '0';
1457         } else
1458                 if ('a' <= hi && hi <= 'f') {
1459                         hi -= ('a' - 10);
1460                 } else
1461                         if ('A' <= hi && hi <= 'F') {
1462                                 hi -= ('A' - 10);
1463                         }
1464
1465         lo = hexstr[1];
1466         if ('0' <= lo && lo <= '9') {
1467                 lo -= '0';
1468         } else
1469                 if ('a' <= lo && lo <= 'f') {
1470                         lo -= ('a'-10);
1471                 } else
1472                         if ('A' <= lo && lo <= 'F') {
1473                                 lo -= ('A' - 10);
1474                         }
1475         result = lo + (16 * hi);
1476         return result;
1477 }
1478
1479 gboolean is_uri_string(const gchar *str)
1480 {
1481         return (g_strncasecmp(str, "http://", 7) == 0 ||
1482                 g_strncasecmp(str, "https://", 8) == 0 ||
1483                 g_strncasecmp(str, "ftp://", 6) == 0 ||
1484                 g_strncasecmp(str, "www.", 4) == 0);
1485 }
1486
1487 gchar *get_uri_path(const gchar *uri)
1488 {
1489         if (g_strncasecmp(uri, "http://", 7) == 0)
1490                 return (gchar *)(uri + 7);
1491         else if (g_strncasecmp(uri, "https://", 8) == 0)
1492                 return (gchar *)(uri + 8);
1493         else if (g_strncasecmp(uri, "ftp://", 6) == 0)
1494                 return (gchar *)(uri + 6);
1495         else
1496                 return (gchar *)uri;
1497 }
1498
1499 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1500  * plusses, and escape characters are used)
1501  */
1502 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1503 {
1504         gchar *dec = decoded_uri;
1505         const gchar *enc = encoded_uri;
1506
1507         while (*enc) {
1508                 if (*enc == '%') {
1509                         enc++;
1510                         if (isxdigit((guchar)enc[0]) &&
1511                             isxdigit((guchar)enc[1])) {
1512                                 *dec = axtoi(enc);
1513                                 dec++;
1514                                 enc += 2;
1515                         }
1516                 } else {
1517                         if (*enc == '+')
1518                                 *dec = ' ';
1519                         else
1520                                 *dec = *enc;
1521                         dec++;
1522                         enc++;
1523                 }
1524         }
1525
1526         *dec = '\0';
1527 }
1528
1529 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1530                      gchar **subject, gchar **body)
1531 {
1532         gchar *tmp_mailto;
1533         gchar *p;
1534
1535         Xstrdup_a(tmp_mailto, mailto, return -1);
1536
1537         if (!strncmp(tmp_mailto, "mailto:", 7))
1538                 tmp_mailto += 7;
1539
1540         p = strchr(tmp_mailto, '?');
1541         if (p) {
1542                 *p = '\0';
1543                 p++;
1544         }
1545
1546         if (to && !*to)
1547                 *to = g_strdup(tmp_mailto);
1548
1549         while (p) {
1550                 gchar *field, *value;
1551
1552                 field = p;
1553
1554                 p = strchr(p, '=');
1555                 if (!p) break;
1556                 *p = '\0';
1557                 p++;
1558
1559                 value = p;
1560
1561                 p = strchr(p, '&');
1562                 if (p) {
1563                         *p = '\0';
1564                         p++;
1565                 }
1566
1567                 if (*value == '\0') continue;
1568
1569                 if (cc && !*cc && !g_strcasecmp(field, "cc")) {
1570                         *cc = g_strdup(value);
1571                 } else if (bcc && !*bcc && !g_strcasecmp(field, "bcc")) {
1572                         *bcc = g_strdup(value);
1573                 } else if (subject && !*subject &&
1574                            !g_strcasecmp(field, "subject")) {
1575                         *subject = g_malloc(strlen(value) + 1);
1576                         decode_uri(*subject, value);
1577                 } else if (body && !*body && !g_strcasecmp(field, "body")) {
1578                         *body = g_malloc(strlen(value) + 1);
1579                         decode_uri(*body, value);
1580                 }
1581         }
1582
1583         return 0;
1584 }
1585
1586 /*
1587  * We need this wrapper around g_get_home_dir(), so that
1588  * we can fix some Windoze things here.  Should be done in glibc of course
1589  * but as long as we are not able to do our own extensions to glibc, we do
1590  * it here.
1591  */
1592 gchar *get_home_dir(void)
1593 {
1594 #if HAVE_DOSISH_SYSTEM
1595     static gchar *home_dir;
1596
1597     if (!home_dir) {
1598         home_dir = read_w32_registry_string(NULL,
1599                                             "Software\\Sylpheed", "HomeDir" );
1600         if (!home_dir || !*home_dir) {
1601             if (getenv ("HOMEDRIVE") && getenv("HOMEPATH")) {
1602                 const char *s = g_get_home_dir();
1603                 if (s && *s)
1604                     home_dir = g_strdup (s);
1605             }
1606             if (!home_dir || !*home_dir) 
1607                 home_dir = g_strdup ("c:\\sylpheed");
1608         }
1609         debug_print("initialized home_dir to `%s'\n", home_dir);
1610     }
1611     return home_dir;
1612 #else /* standard glib */
1613     return g_get_home_dir();
1614 #endif
1615 }
1616
1617 gchar *get_rc_dir(void)
1618 {
1619         static gchar *rc_dir = NULL;
1620
1621         if (!rc_dir)
1622                 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1623                                      RC_DIR, NULL);
1624
1625         return rc_dir;
1626 }
1627
1628 gchar *get_news_cache_dir(void)
1629 {
1630         static gchar *news_cache_dir = NULL;
1631
1632         if (!news_cache_dir)
1633                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1634                                              NEWS_CACHE_DIR, NULL);
1635
1636         return news_cache_dir;
1637 }
1638
1639 gchar *get_imap_cache_dir(void)
1640 {
1641         static gchar *imap_cache_dir = NULL;
1642
1643         if (!imap_cache_dir)
1644                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1645                                              IMAP_CACHE_DIR, NULL);
1646
1647         return imap_cache_dir;
1648 }
1649
1650 gchar *get_mbox_cache_dir(void)
1651 {
1652         static gchar *mbox_cache_dir = NULL;
1653
1654         if (!mbox_cache_dir)
1655                 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1656                                              MBOX_CACHE_DIR, NULL);
1657
1658         return mbox_cache_dir;
1659 }
1660
1661 gchar *get_mime_tmp_dir(void)
1662 {
1663         static gchar *mime_tmp_dir = NULL;
1664
1665         if (!mime_tmp_dir)
1666                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1667                                            MIME_TMP_DIR, NULL);
1668
1669         return mime_tmp_dir;
1670 }
1671
1672 gchar *get_template_dir(void)
1673 {
1674         static gchar *template_dir = NULL;
1675
1676         if (!template_dir)
1677                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1678                                            TEMPLATE_DIR, NULL);
1679
1680         return template_dir;
1681 }
1682
1683 gchar *get_header_cache_dir(void)
1684 {
1685         static gchar *header_dir = NULL;
1686
1687         if (!header_dir)
1688                 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1689                                          HEADER_CACHE_DIR, NULL);
1690
1691         return header_dir;
1692 }
1693
1694 gchar *get_tmp_dir(void)
1695 {
1696         static gchar *tmp_dir = NULL;
1697
1698         if (!tmp_dir)
1699                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1700                                       TMP_DIR, NULL);
1701
1702         return tmp_dir;
1703 }
1704
1705 gchar *get_tmp_file(void)
1706 {
1707         gchar *tmp_file;
1708         static guint32 id = 0;
1709
1710         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1711                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
1712
1713         return tmp_file;
1714 }
1715
1716 gchar *get_domain_name(void)
1717 {
1718         static gchar *domain_name = NULL;
1719
1720         if (!domain_name) {
1721                 struct hostent *hp;
1722                 struct utsname uts;
1723
1724                 if (uname(&uts) < 0) {
1725                         perror("gethostname");
1726                         domain_name = "unknown";
1727                 } else {
1728                         if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
1729                                 perror("gethostbyname");
1730                                 domain_name = g_strdup(uts.nodename);
1731                         } else {
1732                                 domain_name = g_strdup(hp->h_name);
1733                         }
1734                 }
1735
1736                 debug_print("domain name = %s\n", domain_name);
1737         }
1738
1739         return domain_name;
1740 }
1741
1742 off_t get_file_size(const gchar *file)
1743 {
1744         struct stat s;
1745
1746         if (stat(file, &s) < 0) {
1747                 FILE_OP_ERROR(file, "stat");
1748                 return -1;
1749         }
1750
1751         return s.st_size;
1752 }
1753
1754 off_t get_file_size_as_crlf(const gchar *file)
1755 {
1756         FILE *fp;
1757         off_t size = 0;
1758         gchar buf[BUFFSIZE];
1759
1760         if ((fp = fopen(file, "rb")) == NULL) {
1761                 FILE_OP_ERROR(file, "fopen");
1762                 return -1;
1763         }
1764
1765         while (fgets(buf, sizeof(buf), fp) != NULL) {
1766                 strretchomp(buf);
1767                 size += strlen(buf) + 2;
1768         }
1769
1770         if (ferror(fp)) {
1771                 FILE_OP_ERROR(file, "fgets");
1772                 size = -1;
1773         }
1774
1775         fclose(fp);
1776
1777         return size;
1778 }
1779
1780 off_t get_left_file_size(FILE *fp)
1781 {
1782         glong pos;
1783         glong end;
1784         off_t size;
1785
1786         if ((pos = ftell(fp)) < 0) {
1787                 perror("ftell");
1788                 return -1;
1789         }
1790         if (fseek(fp, 0L, SEEK_END) < 0) {
1791                 perror("fseek");
1792                 return -1;
1793         }
1794         if ((end = ftell(fp)) < 0) {
1795                 perror("fseek");
1796                 return -1;
1797         }
1798         size = end - pos;
1799         if (fseek(fp, pos, SEEK_SET) < 0) {
1800                 perror("fseek");
1801                 return -1;
1802         }
1803
1804         return size;
1805 }
1806
1807 gboolean file_exist(const gchar *file, gboolean allow_fifo)
1808 {
1809         struct stat s;
1810
1811         if (file == NULL)
1812                 return FALSE;
1813
1814         if (stat(file, &s) < 0) {
1815                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1816                 return FALSE;
1817         }
1818
1819         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1820                 return TRUE;
1821
1822         return FALSE;
1823 }
1824
1825 gboolean is_dir_exist(const gchar *dir)
1826 {
1827         struct stat s;
1828
1829         if (dir == NULL)
1830                 return FALSE;
1831
1832         if (stat(dir, &s) < 0) {
1833                 if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1834                 return FALSE;
1835         }
1836
1837         if (S_ISDIR(s.st_mode))
1838                 return TRUE;
1839
1840         return FALSE;
1841 }
1842
1843 gboolean is_file_entry_exist(const gchar *file)
1844 {
1845         struct stat s;
1846
1847         if (file == NULL)
1848                 return FALSE;
1849
1850         if (stat(file, &s) < 0) {
1851                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1852                 return FALSE;
1853         }
1854
1855         return TRUE;
1856 }
1857
1858 gboolean dirent_is_regular_file(struct dirent *d)
1859 {
1860         struct stat s;
1861
1862 #ifdef HAVE_DIRENT_D_TYPE
1863         if (d->d_type == DT_REG)
1864                 return TRUE;
1865         else if (d->d_type != DT_UNKNOWN)
1866                 return FALSE;
1867 #endif
1868
1869         return (stat(d->d_name, &s) == 0 && S_ISREG(s.st_mode));
1870 }
1871
1872 gboolean dirent_is_directory(struct dirent *d)
1873 {
1874         struct stat s;
1875
1876 #ifdef HAVE_DIRENT_D_TYPE
1877         if (d->d_type == DT_DIR)
1878                 return TRUE;
1879         else if (d->d_type != DT_UNKNOWN)
1880                 return FALSE;
1881 #endif
1882
1883         return (stat(d->d_name, &s) == 0 && S_ISDIR(s.st_mode));
1884 }
1885
1886 gint change_dir(const gchar *dir)
1887 {
1888         gchar *prevdir = NULL;
1889
1890         if (debug_mode)
1891                 prevdir = g_get_current_dir();
1892
1893         if (chdir(dir) < 0) {
1894                 FILE_OP_ERROR(dir, "chdir");
1895                 if (debug_mode) g_free(prevdir);
1896                 return -1;
1897         } else if (debug_mode) {
1898                 gchar *cwd;
1899
1900                 cwd = g_get_current_dir();
1901                 if (strcmp(prevdir, cwd) != 0)
1902                         g_print("current dir: %s\n", cwd);
1903                 g_free(cwd);
1904                 g_free(prevdir);
1905         }
1906
1907         return 0;
1908 }
1909
1910 gint make_dir(const gchar *dir)
1911 {
1912         if (mkdir(dir, S_IRWXU) < 0) {
1913                 FILE_OP_ERROR(dir, "mkdir");
1914                 return -1;
1915         }
1916         if (chmod(dir, S_IRWXU) < 0)
1917                 FILE_OP_ERROR(dir, "chmod");
1918
1919         return 0;
1920 }
1921
1922 gint make_dir_hier(const gchar *dir)
1923 {
1924         gchar *parent_dir;
1925         const gchar *p;
1926
1927         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1928                 parent_dir = g_strndup(dir, p - dir);
1929                 if (*parent_dir != '\0') {
1930                         if (!is_dir_exist(parent_dir)) {
1931                                 if (make_dir(parent_dir) < 0) {
1932                                         g_free(parent_dir);
1933                                         return -1;
1934                                 }
1935                         }
1936                 }
1937                 g_free(parent_dir);
1938         }
1939
1940         if (!is_dir_exist(dir)) {
1941                 if (make_dir(dir) < 0)
1942                         return -1;
1943         }
1944
1945         return 0;
1946 }
1947
1948 gint remove_all_files(const gchar *dir)
1949 {
1950         DIR *dp;
1951         struct dirent *d;
1952         gchar *prev_dir;
1953
1954         prev_dir = g_get_current_dir();
1955
1956         if (chdir(dir) < 0) {
1957                 FILE_OP_ERROR(dir, "chdir");
1958                 g_free(prev_dir);
1959                 return -1;
1960         }
1961
1962         if ((dp = opendir(".")) == NULL) {
1963                 FILE_OP_ERROR(dir, "opendir");
1964                 g_free(prev_dir);
1965                 return -1;
1966         }
1967
1968         while ((d = readdir(dp)) != NULL) {
1969                 if (!strcmp(d->d_name, ".") ||
1970                     !strcmp(d->d_name, ".."))
1971                         continue;
1972
1973                 if (unlink(d->d_name) < 0)
1974                         FILE_OP_ERROR(d->d_name, "unlink");
1975         }
1976
1977         closedir(dp);
1978
1979         if (chdir(prev_dir) < 0) {
1980                 FILE_OP_ERROR(prev_dir, "chdir");
1981                 g_free(prev_dir);
1982                 return -1;
1983         }
1984
1985         g_free(prev_dir);
1986
1987         return 0;
1988 }
1989
1990 gint remove_numbered_files(const gchar *dir, guint first, guint last)
1991 {
1992         DIR *dp;
1993         struct dirent *d;
1994         gchar *prev_dir;
1995         gint fileno;
1996
1997         prev_dir = g_get_current_dir();
1998
1999         if (chdir(dir) < 0) {
2000                 FILE_OP_ERROR(dir, "chdir");
2001                 g_free(prev_dir);
2002                 return -1;
2003         }
2004
2005         if ((dp = opendir(".")) == NULL) {
2006                 FILE_OP_ERROR(dir, "opendir");
2007                 g_free(prev_dir);
2008                 return -1;
2009         }
2010
2011         while ((d = readdir(dp)) != NULL) {
2012                 fileno = to_number(d->d_name);
2013                 if (fileno >= 0 && first <= fileno && fileno <= last) {
2014                         if (is_dir_exist(d->d_name))
2015                                 continue;
2016                         if (unlink(d->d_name) < 0)
2017                                 FILE_OP_ERROR(d->d_name, "unlink");
2018                 }
2019         }
2020
2021         closedir(dp);
2022
2023         if (chdir(prev_dir) < 0) {
2024                 FILE_OP_ERROR(prev_dir, "chdir");
2025                 g_free(prev_dir);
2026                 return -1;
2027         }
2028
2029         g_free(prev_dir);
2030
2031         return 0;
2032 }
2033
2034 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2035 {
2036         DIR *dp;
2037         struct dirent *d;
2038         gchar *prev_dir;
2039         gint fileno;
2040
2041         prev_dir = g_get_current_dir();
2042
2043         if (chdir(dir) < 0) {
2044                 FILE_OP_ERROR(dir, "chdir");
2045                 g_free(prev_dir);
2046                 return -1;
2047         }
2048
2049         if ((dp = opendir(".")) == NULL) {
2050                 FILE_OP_ERROR(dir, "opendir");
2051                 g_free(prev_dir);
2052                 return -1;
2053         }
2054
2055         while ((d = readdir(dp)) != NULL) {
2056                 fileno = to_number(d->d_name);
2057                 if (fileno >= 0 && (g_slist_find(numberlist, GINT_TO_POINTER(fileno)) == NULL)) {
2058                         debug_print("removing unwanted file %d from %s\n", fileno, dir);
2059                         if (is_dir_exist(d->d_name))
2060                                 continue;
2061                         if (unlink(d->d_name) < 0)
2062                                 FILE_OP_ERROR(d->d_name, "unlink");
2063                 }
2064         }
2065
2066         closedir(dp);
2067
2068         if (chdir(prev_dir) < 0) {
2069                 FILE_OP_ERROR(prev_dir, "chdir");
2070                 g_free(prev_dir);
2071                 return -1;
2072         }
2073
2074         g_free(prev_dir);
2075
2076         return 0;
2077 }
2078
2079 gint remove_all_numbered_files(const gchar *dir)
2080 {
2081         return remove_numbered_files(dir, 0, UINT_MAX);
2082 }
2083
2084 gint remove_expired_files(const gchar *dir, guint hours)
2085 {
2086         DIR *dp;
2087         struct dirent *d;
2088         struct stat s;
2089         gchar *prev_dir;
2090         gint fileno;
2091         time_t mtime, now, expire_time;
2092
2093         prev_dir = g_get_current_dir();
2094
2095         if (chdir(dir) < 0) {
2096                 FILE_OP_ERROR(dir, "chdir");
2097                 g_free(prev_dir);
2098                 return -1;
2099         }
2100
2101         if ((dp = opendir(".")) == NULL) {
2102                 FILE_OP_ERROR(dir, "opendir");
2103                 g_free(prev_dir);
2104                 return -1;
2105         }
2106
2107         now = time(NULL);
2108         expire_time = hours * 60 * 60;
2109
2110         while ((d = readdir(dp)) != NULL) {
2111                 fileno = to_number(d->d_name);
2112                 if (fileno >= 0) {
2113                         if (stat(d->d_name, &s) < 0) {
2114                                 FILE_OP_ERROR(d->d_name, "stat");
2115                                 continue;
2116                         }
2117                         if (S_ISDIR(s.st_mode))
2118                                 continue;
2119                         mtime = MAX(s.st_mtime, s.st_atime);
2120                         if (now - mtime > expire_time) {
2121                                 if (unlink(d->d_name) < 0)
2122                                         FILE_OP_ERROR(d->d_name, "unlink");
2123                         }
2124                 }
2125         }
2126
2127         closedir(dp);
2128
2129         if (chdir(prev_dir) < 0) {
2130                 FILE_OP_ERROR(prev_dir, "chdir");
2131                 g_free(prev_dir);
2132                 return -1;
2133         }
2134
2135         g_free(prev_dir);
2136
2137         return 0;
2138 }
2139
2140 gint remove_dir_recursive(const gchar *dir)
2141 {
2142         struct stat s;
2143         DIR *dp;
2144         struct dirent *d;
2145         gchar *prev_dir;
2146
2147         /* g_print("dir = %s\n", dir); */
2148
2149         if (stat(dir, &s) < 0) {
2150                 FILE_OP_ERROR(dir, "stat");
2151                 if (ENOENT == errno) return 0;
2152                 return -1;
2153         }
2154
2155         if (!S_ISDIR(s.st_mode)) {
2156                 if (unlink(dir) < 0) {
2157                         FILE_OP_ERROR(dir, "unlink");
2158                         return -1;
2159                 }
2160
2161                 return 0;
2162         }
2163
2164         prev_dir = g_get_current_dir();
2165         /* g_print("prev_dir = %s\n", prev_dir); */
2166
2167         if (!path_cmp(prev_dir, dir)) {
2168                 g_free(prev_dir);
2169                 if (chdir("..") < 0) {
2170                         FILE_OP_ERROR(dir, "chdir");
2171                         return -1;
2172                 }
2173                 prev_dir = g_get_current_dir();
2174         }
2175
2176         if (chdir(dir) < 0) {
2177                 FILE_OP_ERROR(dir, "chdir");
2178                 g_free(prev_dir);
2179                 return -1;
2180         }
2181
2182         if ((dp = opendir(".")) == NULL) {
2183                 FILE_OP_ERROR(dir, "opendir");
2184                 chdir(prev_dir);
2185                 g_free(prev_dir);
2186                 return -1;
2187         }
2188
2189         /* remove all files in the directory */
2190         while ((d = readdir(dp)) != NULL) {
2191                 if (!strcmp(d->d_name, ".") ||
2192                     !strcmp(d->d_name, ".."))
2193                         continue;
2194
2195                 /* g_print("removing %s\n", d->d_name); */
2196
2197                 if (dirent_is_directory(d)) {
2198                         if (remove_dir_recursive(d->d_name) < 0) {
2199                                 g_warning("can't remove directory\n");
2200                                 return -1;
2201                         }
2202                 } else {
2203                         if (unlink(d->d_name) < 0)
2204                                 FILE_OP_ERROR(d->d_name, "unlink");
2205                 }
2206         }
2207
2208         closedir(dp);
2209
2210         if (chdir(prev_dir) < 0) {
2211                 FILE_OP_ERROR(prev_dir, "chdir");
2212                 g_free(prev_dir);
2213                 return -1;
2214         }
2215
2216         g_free(prev_dir);
2217
2218         if (rmdir(dir) < 0) {
2219                 FILE_OP_ERROR(dir, "rmdir");
2220                 return -1;
2221         }
2222
2223         return 0;
2224 }
2225
2226 #if 0
2227 /* this seems to be slower than the stdio version... */
2228 gint copy_file(const gchar *src, const gchar *dest)
2229 {
2230         gint src_fd, dest_fd;
2231         gint n_read;
2232         gint n_write;
2233         gchar buf[BUFSIZ];
2234         gchar *dest_bak = NULL;
2235
2236         if ((src_fd = open(src, O_RDONLY)) < 0) {
2237                 FILE_OP_ERROR(src, "open");
2238                 return -1;
2239         }
2240
2241         if (is_file_exist(dest)) {
2242                 dest_bak = g_strconcat(dest, ".bak", NULL);
2243                 if (rename(dest, dest_bak) < 0) {
2244                         FILE_OP_ERROR(dest, "rename");
2245                         close(src_fd);
2246                         g_free(dest_bak);
2247                         return -1;
2248                 }
2249         }
2250
2251         if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
2252                 FILE_OP_ERROR(dest, "open");
2253                 close(src_fd);
2254                 if (dest_bak) {
2255                         if (rename(dest_bak, dest) < 0)
2256                                 FILE_OP_ERROR(dest_bak, "rename");
2257                         g_free(dest_bak);
2258                 }
2259                 return -1;
2260         }
2261
2262         while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
2263                 gint len = n_read;
2264                 gchar *bufp = buf;
2265
2266                 while (len > 0) {
2267                         n_write = write(dest_fd, bufp, len);
2268                         if (n_write <= 0) {
2269                                 g_warning("writing to %s failed.\n", dest);
2270                                 close(dest_fd);
2271                                 close(src_fd);
2272                                 unlink(dest);
2273                                 if (dest_bak) {
2274                                         if (rename(dest_bak, dest) < 0)
2275                                                 FILE_OP_ERROR(dest_bak, "rename");
2276                                         g_free(dest_bak);
2277                                 }
2278                                 return -1;
2279                         }
2280                         len -= n_write;
2281                         bufp += n_write;
2282                 }
2283         }
2284
2285         close(src_fd);
2286         close(dest_fd);
2287
2288         if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
2289                 g_warning("File copy from %s to %s failed.\n", src, dest);
2290                 unlink(dest);
2291                 if (dest_bak) {
2292                         if (rename(dest_bak, dest) < 0)
2293                                 FILE_OP_ERROR(dest_bak, "rename");
2294                         g_free(dest_bak);
2295                 }
2296                 return -1;
2297         }
2298         g_free(dest_bak);
2299
2300         return 0;
2301 }
2302 #endif
2303
2304
2305 /*
2306  * Append src file body to the tail of dest file.
2307  * Now keep_backup has no effects.
2308  */
2309 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2310 {
2311         FILE *src_fp, *dest_fp;
2312         gint n_read;
2313         gchar buf[BUFSIZ];
2314
2315         gboolean err = FALSE;
2316
2317         if ((src_fp = fopen(src, "rb")) == NULL) {
2318                 FILE_OP_ERROR(src, "fopen");
2319                 return -1;
2320         }
2321         
2322         if ((dest_fp = fopen(dest, "ab")) == NULL) {
2323                 FILE_OP_ERROR(dest, "fopen");
2324                 fclose(src_fp);
2325                 return -1;
2326         }
2327
2328         if (change_file_mode_rw(dest_fp, dest) < 0) {
2329                 FILE_OP_ERROR(dest, "chmod");
2330                 g_warning("can't change file mode\n");
2331         }
2332
2333         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2334                 if (n_read < sizeof(buf) && ferror(src_fp))
2335                         break;
2336                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2337                         g_warning("writing to %s failed.\n", dest);
2338                         fclose(dest_fp);
2339                         fclose(src_fp);
2340                         unlink(dest);
2341                         return -1;
2342                 }
2343         }
2344
2345         if (ferror(src_fp)) {
2346                 FILE_OP_ERROR(src, "fread");
2347                 err = TRUE;
2348         }
2349         fclose(src_fp);
2350         if (fclose(dest_fp) == EOF) {
2351                 FILE_OP_ERROR(dest, "fclose");
2352                 err = TRUE;
2353         }
2354
2355         if (err) {
2356                 unlink(dest);
2357                 return -1;
2358         }
2359
2360         return 0;
2361 }
2362
2363 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2364 {
2365         FILE *src_fp, *dest_fp;
2366         gint n_read;
2367         gchar buf[BUFSIZ];
2368         gchar *dest_bak = NULL;
2369         gboolean err = FALSE;
2370
2371         if ((src_fp = fopen(src, "rb")) == NULL) {
2372                 FILE_OP_ERROR(src, "fopen");
2373                 return -1;
2374         }
2375         if (is_file_exist(dest)) {
2376                 dest_bak = g_strconcat(dest, ".bak", NULL);
2377                 if (rename(dest, dest_bak) < 0) {
2378                         FILE_OP_ERROR(dest, "rename");
2379                         fclose(src_fp);
2380                         g_free(dest_bak);
2381                         return -1;
2382                 }
2383         }
2384
2385         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2386                 FILE_OP_ERROR(dest, "fopen");
2387                 fclose(src_fp);
2388                 if (dest_bak) {
2389                         if (rename(dest_bak, dest) < 0)
2390                                 FILE_OP_ERROR(dest_bak, "rename");
2391                         g_free(dest_bak);
2392                 }
2393                 return -1;
2394         }
2395
2396         if (change_file_mode_rw(dest_fp, dest) < 0) {
2397                 FILE_OP_ERROR(dest, "chmod");
2398                 g_warning("can't change file mode\n");
2399         }
2400
2401         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2402                 if (n_read < sizeof(buf) && ferror(src_fp))
2403                         break;
2404                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2405                         g_warning("writing to %s failed.\n", dest);
2406                         fclose(dest_fp);
2407                         fclose(src_fp);
2408                         unlink(dest);
2409                         if (dest_bak) {
2410                                 if (rename(dest_bak, dest) < 0)
2411                                         FILE_OP_ERROR(dest_bak, "rename");
2412                                 g_free(dest_bak);
2413                         }
2414                         return -1;
2415                 }
2416         }
2417
2418         if (ferror(src_fp)) {
2419                 FILE_OP_ERROR(src, "fread");
2420                 err = TRUE;
2421         }
2422         fclose(src_fp);
2423         if (fclose(dest_fp) == EOF) {
2424                 FILE_OP_ERROR(dest, "fclose");
2425                 err = TRUE;
2426         }
2427
2428         if (err) {
2429                 unlink(dest);
2430                 if (dest_bak) {
2431                         if (rename(dest_bak, dest) < 0)
2432                                 FILE_OP_ERROR(dest_bak, "rename");
2433                         g_free(dest_bak);
2434                 }
2435                 return -1;
2436         }
2437
2438         if (keep_backup == FALSE && dest_bak)
2439                 unlink(dest_bak);
2440
2441         g_free(dest_bak);
2442
2443         return 0;
2444 }
2445
2446 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2447 {
2448         if (overwrite == FALSE && is_file_exist(dest)) {
2449                 g_warning("move_file(): file %s already exists.", dest);
2450                 return -1;
2451         }
2452
2453         if (rename(src, dest) == 0) return 0;
2454
2455         if (EXDEV != errno) {
2456                 FILE_OP_ERROR(src, "rename");
2457                 return -1;
2458         }
2459
2460         if (copy_file(src, dest, FALSE) < 0) return -1;
2461
2462         unlink(src);
2463
2464         return 0;
2465 }
2466
2467 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2468 {
2469         gint n_read;
2470         gint bytes_left, to_read;
2471         gchar buf[BUFSIZ];
2472
2473         if (fseek(fp, offset, SEEK_SET) < 0) {
2474                 perror("fseek");
2475                 return -1;
2476         }
2477
2478         bytes_left = length;
2479         to_read = MIN(bytes_left, sizeof(buf));
2480
2481         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2482                 if (n_read < to_read && ferror(fp))
2483                         break;
2484                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2485                         return -1;
2486                 }
2487                 bytes_left -= n_read;
2488                 if (bytes_left == 0)
2489                         break;
2490                 to_read = MIN(bytes_left, sizeof(buf));
2491         }
2492
2493         if (ferror(fp)) {
2494                 perror("fread");
2495                 return -1;
2496         }
2497
2498         return 0;
2499 }
2500
2501 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2502 {
2503         FILE *dest_fp;
2504         gboolean err = FALSE;
2505
2506         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2507                 FILE_OP_ERROR(dest, "fopen");
2508                 return -1;
2509         }
2510
2511         if (change_file_mode_rw(dest_fp, dest) < 0) {
2512                 FILE_OP_ERROR(dest, "chmod");
2513                 g_warning("can't change file mode\n");
2514         }
2515
2516         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2517                 err = TRUE;
2518
2519         if (!err && fclose(dest_fp) == EOF) {
2520                 FILE_OP_ERROR(dest, "fclose");
2521                 err = TRUE;
2522         }
2523
2524         if (err) {
2525                 g_warning("writing to %s failed.\n", dest);
2526                 unlink(dest);
2527                 return -1;
2528         }
2529
2530         return 0;
2531 }
2532
2533 /* convert line endings into CRLF. If the last line doesn't end with
2534  * linebreak, add it.
2535  */
2536 gchar *canonicalize_str(const gchar *str)
2537 {
2538         const gchar *p;
2539         guint new_len = 0;
2540         gchar *out, *outp;
2541
2542         for (p = str; *p != '\0'; ++p) {
2543                 if (*p != '\r') {
2544                         ++new_len;
2545                         if (*p == '\n')
2546                                 ++new_len;
2547                 }
2548         }
2549         if (p == str || *(p - 1) != '\n')
2550                 new_len += 2;
2551
2552         out = outp = g_malloc(new_len + 1);
2553         for (p = str; *p != '\0'; ++p) {
2554                 if (*p != '\r') {
2555                         if (*p == '\n')
2556                                 *outp++ = '\r';
2557                         *outp++ = *p;
2558                 }
2559         }
2560         if (p == str || *(p - 1) != '\n') {
2561                 *outp++ = '\r';
2562                 *outp++ = '\n';
2563         }
2564         *outp = '\0';
2565
2566         return out;
2567 }
2568
2569 gint canonicalize_file(const gchar *src, const gchar *dest)
2570 {
2571         FILE *src_fp, *dest_fp;
2572         gchar buf[BUFFSIZE];
2573         gint len;
2574         gboolean err = FALSE;
2575         gboolean last_linebreak = FALSE;
2576
2577         if ((src_fp = fopen(src, "rb")) == NULL) {
2578                 FILE_OP_ERROR(src, "fopen");
2579                 return -1;
2580         }
2581
2582         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2583                 FILE_OP_ERROR(dest, "fopen");
2584                 fclose(src_fp);
2585                 return -1;
2586         }
2587
2588         if (change_file_mode_rw(dest_fp, dest) < 0) {
2589                 FILE_OP_ERROR(dest, "chmod");
2590                 g_warning("can't change file mode\n");
2591         }
2592
2593         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2594                 gint r = 0;
2595
2596                 len = strlen(buf);
2597                 if (len == 0) break;
2598                 last_linebreak = FALSE;
2599
2600                 if (buf[len - 1] != '\n') {
2601                         last_linebreak = TRUE;
2602                         r = fputs(buf, dest_fp);
2603                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2604                         r = fputs(buf, dest_fp);
2605                 } else {
2606                         if (len > 1) {
2607                                 r = fwrite(buf, len - 1, 1, dest_fp);
2608                                 if (r != 1)
2609                                         r = EOF;
2610                         }
2611                         if (r != EOF)
2612                                 r = fputs("\r\n", dest_fp);
2613                 }
2614
2615                 if (r == EOF) {
2616                         g_warning("writing to %s failed.\n", dest);
2617                         fclose(dest_fp);
2618                         fclose(src_fp);
2619                         unlink(dest);
2620                         return -1;
2621                 }
2622         }
2623
2624         if (last_linebreak == TRUE) {
2625                 if (fputs("\r\n", dest_fp) == EOF)
2626                         err = TRUE;
2627         }
2628
2629         if (ferror(src_fp)) {
2630                 FILE_OP_ERROR(src, "fgets");
2631                 err = TRUE;
2632         }
2633         fclose(src_fp);
2634         if (fclose(dest_fp) == EOF) {
2635                 FILE_OP_ERROR(dest, "fclose");
2636                 err = TRUE;
2637         }
2638
2639         if (err) {
2640                 unlink(dest);
2641                 return -1;
2642         }
2643
2644         return 0;
2645 }
2646
2647 gint canonicalize_file_replace(const gchar *file)
2648 {
2649         gchar *tmp_file;
2650
2651         tmp_file = get_tmp_file();
2652
2653         if (canonicalize_file(file, tmp_file) < 0) {
2654                 g_free(tmp_file);
2655                 return -1;
2656         }
2657
2658         if (move_file(tmp_file, file, TRUE) < 0) {
2659                 g_warning("can't replace %s .\n", file);
2660                 unlink(tmp_file);
2661                 g_free(tmp_file);
2662                 return -1;
2663         }
2664
2665         g_free(tmp_file);
2666         return 0;
2667 }
2668
2669 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2670 {
2671         FILE *src_fp, *dest_fp;
2672         gchar buf[BUFFSIZE];
2673         gboolean err = FALSE;
2674
2675         if ((src_fp = fopen(src, "rb")) == NULL) {
2676                 FILE_OP_ERROR(src, "fopen");
2677                 return -1;
2678         }
2679
2680         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2681                 FILE_OP_ERROR(dest, "fopen");
2682                 fclose(src_fp);
2683                 return -1;
2684         }
2685
2686         if (change_file_mode_rw(dest_fp, dest) < 0) {
2687                 FILE_OP_ERROR(dest, "chmod");
2688                 g_warning("can't change file mode\n");
2689         }
2690
2691         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2692                 strcrchomp(buf);
2693                 if (fputs(buf, dest_fp) == EOF) {
2694                         g_warning("writing to %s failed.\n", dest);
2695                         fclose(dest_fp);
2696                         fclose(src_fp);
2697                         unlink(dest);
2698                         return -1;
2699                 }
2700         }
2701
2702         if (ferror(src_fp)) {
2703                 FILE_OP_ERROR(src, "fgets");
2704                 err = TRUE;
2705         }
2706         fclose(src_fp);
2707         if (fclose(dest_fp) == EOF) {
2708                 FILE_OP_ERROR(dest, "fclose");
2709                 err = TRUE;
2710         }
2711
2712         if (err) {
2713                 unlink(dest);
2714                 return -1;
2715         }
2716
2717         return 0;
2718 }
2719
2720 gint uncanonicalize_file_replace(const gchar *file)
2721 {
2722         gchar *tmp_file;
2723
2724         tmp_file = get_tmp_file();
2725
2726         if (uncanonicalize_file(file, tmp_file) < 0) {
2727                 g_free(tmp_file);
2728                 return -1;
2729         }
2730
2731         if (move_file(tmp_file, file, TRUE) < 0) {
2732                 g_warning("can't replace %s .\n", file);
2733                 unlink(tmp_file);
2734                 g_free(tmp_file);
2735                 return -1;
2736         }
2737
2738         g_free(tmp_file);
2739         return 0;
2740 }
2741
2742 gchar *normalize_newlines(const gchar *str)
2743 {
2744         const gchar *p = str;
2745         gchar *out, *outp;
2746
2747         out = outp = g_malloc(strlen(str) + 1);
2748         for (p = str; *p != '\0'; ++p) {
2749                 if (*p == '\r') {
2750                         if (*(p + 1) != '\n')
2751                                 *outp++ = '\n';
2752                 } else
2753                         *outp++ = *p;
2754         }
2755
2756         *outp = '\0';
2757
2758         return out;
2759 }
2760
2761 gchar *get_outgoing_rfc2822_str(FILE *fp)
2762 {
2763         gchar buf[BUFFSIZE];
2764         GString *str;
2765         gchar *ret;
2766
2767         str = g_string_new(NULL);
2768
2769         /* output header part */
2770         while (fgets(buf, sizeof(buf), fp) != NULL) {
2771                 strretchomp(buf);
2772                 if (!g_strncasecmp(buf, "Bcc:", 4)) {
2773                         gint next;
2774
2775                         for (;;) {
2776                                 next = fgetc(fp);
2777                                 if (next == EOF)
2778                                         break;
2779                                 else if (next != ' ' && next != '\t') {
2780                                         ungetc(next, fp);
2781                                         break;
2782                                 }
2783                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2784                                         break;
2785                         }
2786                 } else {
2787                         g_string_append(str, buf);
2788                         g_string_append(str, "\r\n");
2789                         if (buf[0] == '\0')
2790                                 break;
2791                 }
2792         }
2793
2794         /* output body part */
2795         while (fgets(buf, sizeof(buf), fp) != NULL) {
2796                 strretchomp(buf);
2797                 if (buf[0] == '.')
2798                         g_string_append_c(str, '.');
2799                 g_string_append(str, buf);
2800                 g_string_append(str, "\r\n");
2801         }
2802
2803         ret = str->str;
2804         g_string_free(str, FALSE);
2805
2806         return ret;
2807 }
2808
2809 /*
2810  * Create a new boundary in a way that it is very unlikely that this
2811  * will occur in the following text.  It would be easy to ensure
2812  * uniqueness if everything is either quoted-printable or base64
2813  * encoded (note that conversion is allowed), but because MIME bodies
2814  * may be nested, it may happen that the same boundary has already
2815  * been used. We avoid scanning the message for conflicts and hope the
2816  * best.
2817  *
2818  *   boundary := 0*69<bchars> bcharsnospace
2819  *   bchars := bcharsnospace / " "
2820  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2821  *                    "+" / "_" / "," / "-" / "." /
2822  *                    "/" / ":" / "=" / "?"
2823  *
2824  * some special characters removed because of buggy MTAs
2825  */
2826
2827 gchar *generate_mime_boundary(const gchar *prefix)
2828 {
2829         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2830                              "abcdefghijklmnopqrstuvwxyz"
2831                              "1234567890+_./=";
2832         gchar buf_uniq[17];
2833         gchar buf_date[64];
2834         gint i;
2835         gint pid;
2836
2837         pid = getpid();
2838
2839         /* We make the boundary depend on the pid, so that all running
2840          * processes generate different values even when they have been
2841          * started within the same second and srandom(time(NULL)) has been
2842          * used.  I can't see whether this is really an advantage but it
2843          * doesn't do any harm.
2844          */
2845         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2846                 buf_uniq[i] = tbl[(rand() ^ pid) % (sizeof(tbl) - 1)];
2847         buf_uniq[i] = '\0';
2848
2849         get_rfc822_date(buf_date, sizeof(buf_date));
2850         subst_char(buf_date, ' ', '_');
2851         subst_char(buf_date, ',', '_');
2852         subst_char(buf_date, ':', '_');
2853
2854         return g_strdup_printf("%s_%s_%s", prefix ? prefix : "Multipart",
2855                                buf_date, buf_uniq);
2856 }
2857
2858 gint change_file_mode_rw(FILE *fp, const gchar *file)
2859 {
2860 #if HAVE_FCHMOD
2861         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2862 #else
2863         return chmod(file, S_IRUSR|S_IWUSR);
2864 #endif
2865 }
2866
2867 FILE *my_tmpfile(void)
2868 {
2869 #if HAVE_MKSTEMP
2870         const gchar suffix[] = ".XXXXXX";
2871         const gchar *tmpdir;
2872         guint tmplen;
2873         const gchar *progname;
2874         guint proglen;
2875         gchar *fname;
2876         gint fd;
2877         FILE *fp;
2878
2879         tmpdir = get_tmp_dir();
2880         tmplen = strlen(tmpdir);
2881         progname = g_get_prgname();
2882         if (progname == NULL)
2883                 progname = "sylpheed-claws";
2884         proglen = strlen(progname);
2885         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2886                 return tmpfile());
2887
2888         memcpy(fname, tmpdir, tmplen);
2889         fname[tmplen] = G_DIR_SEPARATOR;
2890         memcpy(fname + tmplen + 1, progname, proglen);
2891         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2892
2893         fd = mkstemp(fname);
2894         if (fd < 0)
2895                 return tmpfile();
2896
2897         unlink(fname);
2898
2899         fp = fdopen(fd, "w+b");
2900         if (!fp)
2901                 close(fd);
2902         else
2903                 return fp;
2904 #endif /* HAVE_MKSTEMP */
2905
2906         return tmpfile();
2907 }
2908
2909 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2910 {
2911         int fd;
2912         
2913         *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
2914         fd = mkstemp(*filename);
2915
2916         return fdopen(fd, "w+");
2917 }
2918
2919 FILE *str_open_as_stream(const gchar *str)
2920 {
2921         FILE *fp;
2922         size_t len;
2923
2924         g_return_val_if_fail(str != NULL, NULL);
2925
2926         fp = my_tmpfile();
2927         if (!fp) {
2928                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2929                 return NULL;
2930         }
2931
2932         len = strlen(str);
2933         if (len == 0) return fp;
2934
2935         if (fwrite(str, len, 1, fp) != 1) {
2936                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2937                 fclose(fp);
2938                 return NULL;
2939         }
2940
2941         rewind(fp);
2942         return fp;
2943 }
2944
2945 gint str_write_to_file(const gchar *str, const gchar *file)
2946 {
2947         FILE *fp;
2948         size_t len;
2949
2950         g_return_val_if_fail(str != NULL, -1);
2951         g_return_val_if_fail(file != NULL, -1);
2952
2953         if ((fp = fopen(file, "wb")) == NULL) {
2954                 FILE_OP_ERROR(file, "fopen");
2955                 return -1;
2956         }
2957
2958         len = strlen(str);
2959         if (len == 0) {
2960                 fclose(fp);
2961                 return 0;
2962         }
2963
2964         if (fwrite(str, len, 1, fp) != 1) {
2965                 FILE_OP_ERROR(file, "fwrite");
2966                 fclose(fp);
2967                 unlink(file);
2968                 return -1;
2969         }
2970
2971         if (fclose(fp) == EOF) {
2972                 FILE_OP_ERROR(file, "fclose");
2973                 unlink(file);
2974                 return -1;
2975         }
2976
2977         return 0;
2978 }
2979
2980 gchar *file_read_to_str(const gchar *file)
2981 {
2982         FILE *fp;
2983         gchar *str;
2984
2985         g_return_val_if_fail(file != NULL, NULL);
2986
2987         if ((fp = fopen(file, "rb")) == NULL) {
2988                 FILE_OP_ERROR(file, "fopen");
2989                 return NULL;
2990         }
2991
2992         str = file_read_stream_to_str(fp);
2993
2994         fclose(fp);
2995
2996         return str;
2997 }
2998
2999 gchar *file_read_stream_to_str(FILE *fp)
3000 {
3001         GByteArray *array;
3002         gchar buf[BUFSIZ];
3003         gint n_read;
3004         gchar *str;
3005
3006         g_return_val_if_fail(fp != NULL, NULL);
3007
3008         array = g_byte_array_new();
3009
3010         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3011                 if (n_read < sizeof(buf) && ferror(fp))
3012                         break;
3013                 g_byte_array_append(array, buf, n_read);
3014         }
3015
3016         if (ferror(fp)) {
3017                 FILE_OP_ERROR("file stream", "fread");
3018                 g_byte_array_free(array, TRUE);
3019                 return NULL;
3020         }
3021
3022         buf[0] = '\0';
3023         g_byte_array_append(array, buf, 1);
3024         str = (gchar *)array->data;
3025         g_byte_array_free(array, FALSE);
3026
3027         return str;
3028 }
3029
3030 gint execute_async(gchar *const argv[])
3031 {
3032         pid_t pid;
3033         gint status;
3034
3035         if ((pid = fork()) < 0) {
3036                 perror("fork");
3037                 return -1;
3038         }
3039
3040         if (pid == 0) {                 /* child process */
3041                 pid_t gch_pid;
3042
3043                 if ((gch_pid = fork()) < 0) {
3044                         perror("fork");
3045                         _exit(1);
3046                 }
3047
3048                 if (gch_pid == 0) {     /* grandchild process */
3049                         execvp(argv[0], argv);
3050
3051                         perror("execvp");
3052                         _exit(1);
3053                 }
3054
3055                 _exit(0);
3056         }
3057
3058         waitpid(pid, &status, 0);
3059
3060         if (WIFEXITED(status))
3061                 return WEXITSTATUS(status);
3062         else
3063                 return -1;
3064 }
3065
3066 gint execute_sync(gchar *const argv[])
3067 {
3068         pid_t pid;
3069         gint status;
3070
3071         if ((pid = fork()) < 0) {
3072                 perror("fork");
3073                 return -1;
3074         }
3075
3076         if (pid == 0) {         /* child process */
3077                 execvp(argv[0], argv);
3078
3079                 perror("execvp");
3080                 _exit(1);
3081         }
3082
3083         waitpid(pid, &status, 0);
3084
3085         if (WIFEXITED(status))
3086                 return WEXITSTATUS(status);
3087         else
3088                 return -1;
3089 }
3090
3091 gint execute_command_line(const gchar *cmdline, gboolean async)
3092 {
3093         gchar **argv;
3094         gint ret;
3095
3096         debug_print("executing: %s\n", cmdline);
3097
3098         argv = strsplit_with_quote(cmdline, " ", 0);
3099
3100         if (async)
3101                 ret = execute_async(argv);
3102         else
3103                 ret = execute_sync(argv);
3104
3105         g_strfreev(argv);
3106
3107         return ret;
3108 }
3109
3110 gchar *get_command_output(const gchar *cmdline)
3111 {
3112         gchar buf[BUFFSIZE];
3113         FILE *fp;
3114         GString *str;
3115         gchar *ret;
3116
3117         g_return_val_if_fail(cmdline != NULL, NULL);
3118
3119         if ((fp = popen(cmdline, "r")) == NULL) {
3120                 FILE_OP_ERROR(cmdline, "popen");
3121                 return NULL;
3122         }
3123
3124         str = g_string_new("");
3125
3126         while (fgets(buf, sizeof(buf), fp) != NULL)
3127                 g_string_append(str, buf);
3128
3129         pclose(fp);
3130
3131         ret = str->str;
3132         g_string_free(str, FALSE);
3133
3134         return ret;
3135 }
3136
3137 static gint is_unchanged_uri_char(char c)
3138 {
3139         switch (c) {
3140                 case '(':
3141                 case ')':
3142                 case ',':
3143                         return 0;
3144                 default:
3145                         return 1;
3146         }
3147 }
3148
3149 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3150 {
3151         int i;
3152         int k;
3153
3154         k = 0;
3155         for(i = 0; i < strlen(uri) ; i++) {
3156                 if (is_unchanged_uri_char(uri[i])) {
3157                         if (k + 2 >= bufsize)
3158                                 break;
3159                         encoded_uri[k++] = uri[i];
3160                 }
3161                 else {
3162                         char * hexa = "0123456789ABCDEF";
3163                         
3164                         if (k + 4 >= bufsize)
3165                                 break;
3166                         encoded_uri[k++] = '%';
3167                         encoded_uri[k++] = hexa[uri[i] / 16];
3168                         encoded_uri[k++] = hexa[uri[i] % 16];
3169                 }
3170         }
3171         encoded_uri[k] = 0;
3172 }
3173
3174 gint open_uri(const gchar *uri, const gchar *cmdline)
3175 {
3176         gchar buf[BUFFSIZE];
3177         gchar *p;
3178         gchar encoded_uri[BUFFSIZE];
3179         
3180         g_return_val_if_fail(uri != NULL, -1);
3181
3182         /* an option to choose whether to use encode_uri or not ? */
3183         encode_uri(encoded_uri, BUFFSIZE, uri);
3184         
3185         if (cmdline &&
3186             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3187             !strchr(p + 2, '%'))
3188                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3189         else {
3190                 if (cmdline)
3191                         g_warning("Open URI command line is invalid "
3192                                   "(there must be only one '%%s'): %s",
3193                                   cmdline);
3194                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3195         }
3196
3197         execute_command_line(buf, TRUE);
3198
3199         return 0;
3200 }
3201
3202 time_t remote_tzoffset_sec(const gchar *zone)
3203 {
3204         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3205         gchar zone3[4];
3206         gchar *p;
3207         gchar c;
3208         gint iustz;
3209         gint offset;
3210         time_t remoteoffset;
3211
3212         strncpy(zone3, zone, 3);
3213         zone3[3] = '\0';
3214         remoteoffset = 0;
3215
3216         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3217             (c == '+' || c == '-')) {
3218                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3219                 if (c == '-')
3220                         remoteoffset = -remoteoffset;
3221         } else if (!strncmp(zone, "UT" , 2) ||
3222                    !strncmp(zone, "GMT", 2)) {
3223                 remoteoffset = 0;
3224         } else if (strlen(zone3) == 3) {
3225                 for (p = ustzstr; *p != '\0'; p += 3) {
3226                         if (!g_strncasecmp(p, zone3, 3)) {
3227                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3228                                 remoteoffset = iustz * 3600;
3229                                 break;
3230                         }
3231                 }
3232                 if (*p == '\0')
3233                         return -1;
3234         } else if (strlen(zone3) == 1) {
3235                 switch (zone[0]) {
3236                 case 'Z': remoteoffset =   0; break;
3237                 case 'A': remoteoffset =  -1; break;
3238                 case 'B': remoteoffset =  -2; break;
3239                 case 'C': remoteoffset =  -3; break;
3240                 case 'D': remoteoffset =  -4; break;
3241                 case 'E': remoteoffset =  -5; break;
3242                 case 'F': remoteoffset =  -6; break;
3243                 case 'G': remoteoffset =  -7; break;
3244                 case 'H': remoteoffset =  -8; break;
3245                 case 'I': remoteoffset =  -9; break;
3246                 case 'K': remoteoffset = -10; break; /* J is not used */
3247                 case 'L': remoteoffset = -11; break;
3248                 case 'M': remoteoffset = -12; break;
3249                 case 'N': remoteoffset =   1; break;
3250                 case 'O': remoteoffset =   2; break;
3251                 case 'P': remoteoffset =   3; break;
3252                 case 'Q': remoteoffset =   4; break;
3253                 case 'R': remoteoffset =   5; break;
3254                 case 'S': remoteoffset =   6; break;
3255                 case 'T': remoteoffset =   7; break;
3256                 case 'U': remoteoffset =   8; break;
3257                 case 'V': remoteoffset =   9; break;
3258                 case 'W': remoteoffset =  10; break;
3259                 case 'X': remoteoffset =  11; break;
3260                 case 'Y': remoteoffset =  12; break;
3261                 default:  remoteoffset =   0; break;
3262                 }
3263                 remoteoffset = remoteoffset * 3600;
3264         } else
3265                 return -1;
3266
3267         return remoteoffset;
3268 }
3269
3270 time_t tzoffset_sec(time_t *now)
3271 {
3272         struct tm gmt, *lt;
3273         gint off;
3274
3275         gmt = *gmtime(now);
3276         lt = localtime(now);
3277
3278         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3279
3280         if (lt->tm_year < gmt.tm_year)
3281                 off -= 24 * 60;
3282         else if (lt->tm_year > gmt.tm_year)
3283                 off += 24 * 60;
3284         else if (lt->tm_yday < gmt.tm_yday)
3285                 off -= 24 * 60;
3286         else if (lt->tm_yday > gmt.tm_yday)
3287                 off += 24 * 60;
3288
3289         if (off >= 24 * 60)             /* should be impossible */
3290                 off = 23 * 60 + 59;     /* if not, insert silly value */
3291         if (off <= -24 * 60)
3292                 off = -(23 * 60 + 59);
3293
3294         return off * 60;
3295 }
3296
3297 /* calculate timezone offset */
3298 gchar *tzoffset(time_t *now)
3299 {
3300         static gchar offset_string[6];
3301         struct tm gmt, *lt;
3302         gint off;
3303         gchar sign = '+';
3304
3305         gmt = *gmtime(now);
3306         lt = localtime(now);
3307
3308         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3309
3310         if (lt->tm_year < gmt.tm_year)
3311                 off -= 24 * 60;
3312         else if (lt->tm_year > gmt.tm_year)
3313                 off += 24 * 60;
3314         else if (lt->tm_yday < gmt.tm_yday)
3315                 off -= 24 * 60;
3316         else if (lt->tm_yday > gmt.tm_yday)
3317                 off += 24 * 60;
3318
3319         if (off < 0) {
3320                 sign = '-';
3321                 off = -off;
3322         }
3323
3324         if (off >= 24 * 60)             /* should be impossible */
3325                 off = 23 * 60 + 59;     /* if not, insert silly value */
3326
3327         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3328
3329         return offset_string;
3330 }
3331
3332 void get_rfc822_date(gchar *buf, gint len)
3333 {
3334         struct tm *lt;
3335         time_t t;
3336         gchar day[4], mon[4];
3337         gint dd, hh, mm, ss, yyyy;
3338
3339         t = time(NULL);
3340         lt = localtime(&t);
3341
3342         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3343                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3344         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3345                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3346 }
3347
3348 void debug_set_mode(gboolean mode)
3349 {
3350         debug_mode = mode;
3351 }
3352
3353 gboolean debug_get_mode(void)
3354 {
3355         return debug_mode;
3356 }
3357
3358 void debug_print_real(const gchar *format, ...)
3359 {
3360         va_list args;
3361         gchar buf[BUFFSIZE];
3362
3363         if (!debug_mode) return;
3364
3365         va_start(args, format);
3366         g_vsnprintf(buf, sizeof(buf), format, args);
3367         va_end(args);
3368
3369         fputs(buf, stdout);
3370 }
3371
3372 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3373 {
3374         if (subject == NULL)
3375                 subject = "";
3376         else
3377                 subject += subject_get_prefix_length(subject);
3378
3379         return g_hash_table_lookup(subject_table, subject);
3380 }
3381
3382 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3383                           void * data)
3384 {
3385         if (subject == NULL || *subject == 0)
3386                 return;
3387         subject += subject_get_prefix_length(subject);
3388         g_hash_table_insert(subject_table, subject, data);
3389 }
3390
3391 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3392 {
3393         if (subject == NULL)
3394                 return;
3395
3396         subject += subject_get_prefix_length(subject);  
3397         g_hash_table_remove(subject_table, subject);
3398 }
3399
3400 /*!
3401  *\brief        Check if a string is prefixed with known (combinations) 
3402  *              of prefixes. The function assumes that each prefix 
3403  *              is terminated by zero or exactly _one_ space.
3404  *
3405  *\param        str String to check for a prefixes
3406  *
3407  *\return       int Number of chars in the prefix that should be skipped 
3408  *              for a "clean" subject line. If no prefix was found, 0
3409  *              is returned.
3410  */             
3411 int subject_get_prefix_length(const gchar *subject)
3412 {
3413         /*!< Array with allowable reply prefixes regexps. */
3414         static const gchar * const prefixes[] = {
3415                 "Re\\:",                        /* "Re:" */
3416                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3417                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3418                 "Aw\\:",                        /* "Aw:"   (German) */
3419                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3420                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3421                 "Fw\\:",                        /* "Fw:" Forward */
3422                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3423                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3424                 "Rif\\:"                        /* "Rif:" (Italian Outlook) */
3425                 /* add more */
3426         };
3427         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3428         int n;
3429         regmatch_t pos;
3430         static regex_t regex;
3431         static gboolean init_;
3432
3433         if (!subject) return 0;
3434         if (!*subject) return 0;
3435
3436         if (!init_) {
3437                 GString *s = g_string_new("");
3438                 
3439                 for (n = 0; n < PREFIXES; n++)
3440                         /* Terminate each prefix regexpression by a
3441                          * "\ ?" (zero or ONE space), and OR them */
3442                         g_string_sprintfa(s, "(%s\\ ?)%s",
3443                                           prefixes[n],
3444                                           n < PREFIXES - 1 ? 
3445                                           "|" : "");
3446                 
3447                 g_string_prepend(s, "(");
3448                 g_string_append(s, ")+");       /* match at least once */
3449                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3450                 
3451
3452                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+" 
3453                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3454                 if (regcomp(&regex, s->str, REG_EXTENDED | REG_ICASE)) { 
3455                         debug_print("Error compiling regexp %s\n", s->str);
3456                         g_string_free(s, TRUE);
3457                         return 0;
3458                 } else {
3459                         init_ = TRUE;
3460                         g_string_free(s, TRUE);
3461                 }
3462         }
3463         
3464         if (!regexec(&regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3465                 return pos.rm_eo;
3466         else
3467                 return 0;
3468 }
3469
3470 guint g_stricase_hash(gconstpointer gptr)
3471 {
3472         guint hash_result = 0;
3473         const char *str;
3474
3475         for (str = gptr; str && *str; str++) {
3476                 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3477                 else hash_result += *str;
3478         }
3479
3480         return hash_result;
3481 }
3482
3483 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3484 {
3485         const char *str1 = gptr1;
3486         const char *str2 = gptr2;
3487
3488         return !g_strcasecmp(str1, str2);
3489 }
3490
3491 gint g_int_compare(gconstpointer a, gconstpointer b)
3492 {
3493         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3494 }
3495
3496 gchar *generate_msgid(const gchar *address, gchar *buf, gint len)
3497 {
3498         struct tm *lt;
3499         time_t t;
3500         gchar *addr;
3501
3502         t = time(NULL);
3503         lt = localtime(&t);
3504
3505         if (address && *address) {
3506                 if (strchr(address, '@'))
3507                         addr = g_strdup(address);
3508                 else
3509                         addr = g_strconcat(address, "@", get_domain_name(), NULL);
3510         } else
3511                 addr = g_strconcat(g_get_user_name(), "@", get_domain_name(),
3512                                    NULL);
3513
3514         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x.%s",
3515                    lt->tm_year + 1900, lt->tm_mon + 1,
3516                    lt->tm_mday, lt->tm_hour,
3517                    lt->tm_min, lt->tm_sec,
3518                    (guint) rand(), addr);
3519
3520         g_free(addr);
3521         return buf;
3522 }
3523
3524
3525 /*
3526    quote_cmd_argument()
3527    
3528    return a quoted string safely usable in argument of a command.
3529    
3530    code is extracted and adapted from etPan! project -- DINH V. HoĆ .
3531 */
3532
3533 gint quote_cmd_argument(gchar * result, guint size,
3534                         const gchar * path)
3535 {
3536         const gchar * p;
3537         gchar * result_p;
3538         guint remaining;
3539
3540         result_p = result;
3541         remaining = size;
3542
3543         for(p = path ; * p != '\0' ; p ++) {
3544
3545                 if (isalnum((guchar)*p) || (* p == '/')) {
3546                         if (remaining > 0) {
3547                                 * result_p = * p;
3548                                 result_p ++; 
3549                                 remaining --;
3550                         }
3551                         else {
3552                                 result[size - 1] = '\0';
3553                                 return -1;
3554                         }
3555                 }
3556                 else { 
3557                         if (remaining >= 2) {
3558                                 * result_p = '\\';
3559                                 result_p ++; 
3560                                 * result_p = * p;
3561                                 result_p ++; 
3562                                 remaining -= 2;
3563                         }
3564                         else {
3565                                 result[size - 1] = '\0';
3566                                 return -1;
3567                         }
3568                 }
3569         }
3570         if (remaining > 0) {
3571                 * result_p = '\0';
3572         }
3573         else {
3574                 result[size - 1] = '\0';
3575                 return -1;
3576         }
3577   
3578         return 0;
3579 }
3580
3581 typedef struct 
3582 {
3583         GNode           *parent;
3584         GNodeMapFunc     func;
3585         gpointer         data;
3586 } GNodeMapData;
3587
3588 static void g_node_map_recursive(GNode *node, gpointer data)
3589 {
3590         GNodeMapData *mapdata = (GNodeMapData *) data;
3591         GNode *newnode;
3592         GNodeMapData newmapdata;
3593         gpointer newdata;
3594
3595         newdata = mapdata->func(node->data, mapdata->data);
3596         if (newdata != NULL) {
3597                 newnode = g_node_new(newdata);
3598                 g_node_append(mapdata->parent, newnode);
3599
3600                 newmapdata.parent = newnode;
3601                 newmapdata.func = mapdata->func;
3602                 newmapdata.data = mapdata->data;
3603
3604                 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3605         }
3606 }
3607
3608 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3609 {
3610         GNode *root;
3611         GNodeMapData mapdata;
3612
3613         g_return_val_if_fail(node != NULL, NULL);
3614         g_return_val_if_fail(func != NULL, NULL);
3615
3616         root = g_node_new(func(node->data, data));
3617
3618         mapdata.parent = root;
3619         mapdata.func = func;
3620         mapdata.data = data;
3621
3622         g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3623
3624         return root;
3625 }
3626
3627 #define HEX_TO_INT(val, hex)                    \
3628 {                                               \
3629         gchar c = hex;                          \
3630                                                 \
3631         if ('0' <= c && c <= '9') {             \
3632                 val = c - '0';                  \
3633         } else if ('a' <= c && c <= 'f') {      \
3634                 val = c - 'a' + 10;             \
3635         } else if ('A' <= c && c <= 'F') {      \
3636                 val = c - 'A' + 10;             \
3637         } else {                                \
3638                 val = -1;                       \
3639         }                                       \
3640 }
3641
3642 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3643 {
3644         gint hi, lo;
3645
3646         HEX_TO_INT(hi, c1);
3647         HEX_TO_INT(lo, c2);
3648
3649         if (hi == -1 || lo == -1)
3650                 return FALSE;
3651
3652         *out = (hi << 4) + lo;
3653         return TRUE;
3654 }
3655
3656 #define INT_TO_HEX(hex, val)            \
3657 {                                       \
3658         if ((val) < 10)                 \
3659                 hex = '0' + (val);      \
3660         else                            \
3661                 hex = 'A' + (val) - 10; \
3662 }
3663
3664 void get_hex_str(gchar *out, guchar ch)
3665 {
3666         gchar hex;
3667
3668         INT_TO_HEX(hex, ch >> 4);
3669         *out++ = hex;
3670         INT_TO_HEX(hex, ch & 0x0f);
3671         *out++ = hex;
3672 }