00363528daf8b56e4b69ed7f51fbbabdb7e053c2
[claws.git] / src / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
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
45 #include "intl.h"
46 #include "utils.h"
47 #include "statusbar.h"
48 #include "logwindow.h"
49
50 #define BUFFSIZE        8192
51
52 extern gboolean debug_mode;
53
54 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data);
55
56 void list_free_strings(GList *list)
57 {
58         list = g_list_first(list);
59
60         while (list != NULL) {
61                 g_free(list->data);
62                 list = list->next;
63         }
64 }
65
66 void slist_free_strings(GSList *list)
67 {
68         while (list != NULL) {
69                 g_free(list->data);
70                 list = list->next;
71         }
72 }
73
74 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
75 {
76         g_free(key);
77 }
78
79 void hash_free_strings(GHashTable *table)
80 {
81         g_hash_table_foreach(table, hash_free_strings_func, NULL);
82 }
83
84 static void hash_free_value_mem_func(gpointer key, gpointer value,
85                                      gpointer data)
86 {
87         g_free(value);
88 }
89
90 void hash_free_value_mem(GHashTable *table)
91 {
92         g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
93 }
94
95 void ptr_array_free_strings(GPtrArray *array)
96 {
97         gint i;
98         gchar *str;
99
100         g_return_if_fail(array != NULL);
101
102         for (i = 0; i < array->len; i++) {
103                 str = g_ptr_array_index(array, i);
104                 g_free(str);
105         }
106 }
107
108 gint to_number(const gchar *nstr)
109 {
110         register const gchar *p;
111
112         if (*nstr == '\0') return -1;
113
114         for (p = nstr; *p != '\0'; p++)
115                 if (!isdigit(*p)) return -1;
116
117         return atoi(nstr);
118 }
119
120 /* convert integer into string,
121    nstr must be not lower than 11 characters length */
122 gchar *itos_buf(gchar *nstr, gint n)
123 {
124         g_snprintf(nstr, 11, "%d", n);
125         return nstr;
126 }
127
128 /* convert integer into string */
129 gchar *itos(gint n)
130 {
131         static gchar nstr[11];
132
133         return itos_buf(nstr, n);
134 }
135
136 gchar *to_human_readable(off_t size)
137 {
138         static gchar str[10];
139
140         if (size < 1024)
141                 g_snprintf(str, sizeof(str), "%dB", (gint)size);
142         else if (size >> 10 < 1024)
143                 g_snprintf(str, sizeof(str), "%.1fKB", (gfloat)size / (1 << 10));
144         else if (size >> 20 < 1024)
145                 g_snprintf(str, sizeof(str), "%.2fMB", (gfloat)size / (1 << 20));
146         else
147                 g_snprintf(str, sizeof(str), "%.2fGB", (gfloat)size / (1 << 30));
148
149         return str;
150 }
151
152 /* strcmp with NULL-checking */
153 gint strcmp2(const gchar *s1, const gchar *s2)
154 {
155         if (s1 == NULL || s2 == NULL)
156                 return -1;
157         else
158                 return strcmp(s1, s2);
159 }
160 /* strstr with NULL-checking */
161 gint strstr2(const gchar *s1, const gchar *s2)
162 {
163         if (s1 == NULL || s2 == NULL)
164                 return -1;
165         else
166                 if( strstr(s1, s2) !=NULL) 
167                 return 0;
168                 else 
169                 return -1;
170 }
171 /* compare paths */
172 gint path_cmp(const gchar *s1, const gchar *s2)
173 {
174         gint len1, len2;
175
176         if (s1 == NULL || s2 == NULL) return -1;
177         if (*s1 == '\0' || *s2 == '\0') return -1;
178
179         len1 = strlen(s1);
180         len2 = strlen(s2);
181
182         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
183         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
184
185         return strncmp(s1, s2, MAX(len1, len2));
186 }
187
188 /* remove trailing return code */
189 gchar *strretchomp(gchar *str)
190 {
191         register gchar *s;
192
193         if (!*str) return str;
194
195         for (s = str + strlen(str) - 1;
196              s >= str && (*s == '\n' || *s == '\r');
197              s--)
198                 *s = '\0';
199
200         return str;
201 }
202
203 /* remove trailing character */
204 gchar *strtailchomp(gchar *str, gchar tail_char)
205 {
206         register gchar *s;
207
208         if (!*str) return str;
209         if (tail_char == '\0') return str;
210
211         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
212                 *s = '\0';
213
214         return str;
215 }
216
217
218 /* Similar to `strstr' but this function ignores the case of both strings.  */
219 gchar *strcasestr(const gchar *haystack, const gchar *needle)
220 {
221         register size_t haystack_len, needle_len;
222
223         haystack_len = strlen(haystack);
224         needle_len   = strlen(needle);
225
226         if (haystack_len < needle_len || needle_len == 0)
227                 return NULL;
228
229         while (haystack_len >= needle_len) {
230                 if (!strncasecmp(haystack, needle, needle_len))
231                         return (gchar *)haystack;
232                 else {
233                         haystack++;
234                         haystack_len--;
235                 }
236         }
237
238         return NULL;
239 }
240
241 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
242 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
243 {
244         register gchar c;
245         gchar *s = dest;
246
247         do {
248                 if (--n == 0) {
249                         *dest = '\0';
250                         return s;
251                 }
252                 c = *src++;
253                 *dest++ = c;
254         } while (c != '\0');
255
256         /* don't do zero fill */
257         return s;
258 }
259
260 #if !HAVE_ISWALNUM
261 int iswalnum(wint_t wc)
262 {
263         return isalnum((int)wc);
264 }
265 #endif
266
267 #if !HAVE_ISWSPACE
268 int iswspace(wint_t wc)
269 {
270         return isspace((int)wc);
271 }
272 #endif
273
274 #if !HAVE_TOWLOWER
275 wint_t towlower(wint_t wc)
276 {
277         if (wc >= L'A' && wc <= L'Z')
278                 return wc + L'a' - L'A';
279
280         return wc;
281 }
282 #endif
283
284 #if !HAVE_WCSLEN
285 size_t wcslen(const wchar_t *s)
286 {
287         size_t len = 0;
288
289         while (*s != L'\0')
290                 ++len, ++s;
291
292         return len;
293 }
294 #endif
295
296 #if !HAVE_WCSCPY
297 /* Copy SRC to DEST.  */
298 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
299 {
300         wint_t c;
301         wchar_t *s = dest;
302
303         do {
304                 c = *src++;
305                 *dest++ = c;
306         } while (c != L'\0');
307
308         return s;
309 }
310 #endif
311
312 #if !HAVE_WCSNCPY
313 /* Copy no more than N wide-characters of SRC to DEST.  */
314 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
315 {
316         wint_t c;
317         wchar_t *s = dest;
318
319         do {
320                 c = *src++;
321                 *dest++ = c;
322                 if (--n == 0)
323                         return s;
324         } while (c != L'\0');
325
326         /* zero fill */
327         do
328                 *dest++ = L'\0';
329         while (--n > 0);
330
331         return s;
332 }
333 #endif
334
335 /* Duplicate S, returning an identical malloc'd string. */
336 wchar_t *wcsdup(const wchar_t *s)
337 {
338         wchar_t *new_str;
339
340         if (s) {
341                 new_str = g_new(wchar_t, wcslen(s) + 1);
342                 wcscpy(new_str, s);
343         } else
344                 new_str = NULL;
345
346         return new_str;
347 }
348
349 /* Duplicate no more than N wide-characters of S,
350    returning an identical malloc'd string. */
351 wchar_t *wcsndup(const wchar_t *s, size_t n)
352 {
353         wchar_t *new_str;
354
355         if (s) {
356                 new_str = g_new(wchar_t, n + 1);
357                 wcsncpy(new_str, s, n);
358                 new_str[n] = (wchar_t)0;
359         } else
360                 new_str = NULL;
361
362         return new_str;
363 }
364
365 wchar_t *strdup_mbstowcs(const gchar *s)
366 {
367         wchar_t *new_str;
368
369         if (s) {
370                 new_str = g_new(wchar_t, strlen(s) + 1);
371                 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
372                         g_free(new_str);
373                         new_str = NULL;
374                 } else
375                         new_str = g_realloc(new_str,
376                                             sizeof(wchar_t) * (wcslen(new_str) + 1));
377         } else
378                 new_str = NULL;
379
380         return new_str;
381 }
382
383 gchar *strdup_wcstombs(const wchar_t *s)
384 {
385         gchar *new_str;
386         size_t len;
387
388         if (s) {
389                 len = wcslen(s) * MB_CUR_MAX + 1;
390                 new_str = g_new(gchar, len);
391                 if (wcstombs(new_str, s, len) < 0) {
392                         g_free(new_str);
393                         new_str = NULL;
394                 } else
395                         new_str = g_realloc(new_str, strlen(new_str) + 1);
396         } else
397                 new_str = NULL;
398
399         return new_str;
400 }
401
402 /* Compare S1 and S2, ignoring case.  */
403 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
404 {
405         wint_t c1;
406         wint_t c2;
407
408         while (n--) {
409                 c1 = towlower(*s1++);
410                 c2 = towlower(*s2++);
411                 if (c1 != c2)
412                         return c1 - c2;
413                 else if (c1 == 0 && c2 == 0)
414                         break;
415         }
416
417         return 0;
418 }
419
420 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
421 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
422 {
423         register size_t haystack_len, needle_len;
424
425         haystack_len = wcslen(haystack);
426         needle_len   = wcslen(needle);
427
428         if (haystack_len < needle_len || needle_len == 0)
429                 return NULL;
430
431         while (haystack_len >= needle_len) {
432                 if (!wcsncasecmp(haystack, needle, needle_len))
433                         return (wchar_t *)haystack;
434                 else {
435                         haystack++;
436                         haystack_len--;
437                 }
438         }
439
440         return NULL;
441 }
442
443 /* Examine if next block is non-ASCII string */
444 gboolean is_next_nonascii(const wchar_t *s)
445 {
446         const wchar_t *wp;
447
448         /* skip head space */
449         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
450                 ;
451         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
452                 if (*wp > 127)
453                         return TRUE;
454         }
455
456         return FALSE;
457 }
458
459 /* Examine if next block is multi-byte string */
460 gboolean is_next_mbs(const wchar_t *s)
461 {
462         gint mbl;
463         const wchar_t *wp;
464         gchar tmp[MB_LEN_MAX];
465
466         /* skip head space */
467         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
468                 ;
469         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
470                 mbl = wctomb(tmp, *wp);
471                 if (mbl > 1)
472                         return TRUE;
473         }
474
475         return FALSE;
476 }
477
478 wchar_t *find_wspace(const wchar_t *s)
479 {
480         const wchar_t *wp;
481
482         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
483                 ;
484         for (; *wp != (wchar_t)0; wp++) {
485                 if (iswspace(*wp))
486                         return (wchar_t *)wp;
487         }
488
489         return NULL;
490 }
491
492 /* compare subjects */
493 gint subject_compare(const gchar *s1, const gchar *s2)
494 {
495         gchar *str1, *str2;
496
497         if (!s1 || !s2) return -1;
498         if (!*s1 || !*s2) return -1;
499
500         Xalloca(str1, strlen(s1) + 1, return -1);
501         Xalloca(str2, strlen(s2) + 1, return -1);
502         strcpy(str1, s1);
503         strcpy(str2, s2);
504
505         trim_subject(str1);
506         trim_subject(str2);
507
508         if (!*str1 || !*str2) return -1;
509
510         return strcmp(str1, str2);
511 }
512
513 void trim_subject(gchar *str)
514 {
515         gchar *srcp;
516
517         eliminate_parenthesis(str, '[', ']');
518         eliminate_parenthesis(str, '(', ')');
519         g_strstrip(str);
520
521         while (!strncasecmp(str, "Re:", 3)) {
522                 srcp = str + 3;
523                 while (isspace(*srcp)) srcp++;
524                 memmove(str, srcp, strlen(srcp) + 1);
525         }
526 }
527
528 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
529 {
530         register gchar *srcp, *destp;
531         gint in_brace;
532
533         srcp = destp = str;
534
535         while ((destp = strchr(destp, op))) {
536                 in_brace = 1;
537                 srcp = destp + 1;
538                 while (*srcp) {
539                         if (*srcp == op)
540                                 in_brace++;
541                         else if (*srcp == cl)
542                                 in_brace--;
543                         srcp++;
544                         if (in_brace == 0)
545                                 break;
546                 }
547                 while (isspace(*srcp)) srcp++;
548                 memmove(destp, srcp, strlen(srcp) + 1);
549         }
550 }
551
552 void extract_parenthesis(gchar *str, gchar op, gchar cl)
553 {
554         register gchar *srcp, *destp;
555         gint in_brace;
556
557         srcp = destp = str;
558
559         while ((srcp = strchr(destp, op))) {
560                 if (destp > str)
561                         *destp++ = ' ';
562                 memmove(destp, srcp + 1, strlen(srcp));
563                 in_brace = 1;
564                 while(*destp) {
565                         if (*destp == op)
566                                 in_brace++;
567                         else if (*destp == cl)
568                                 in_brace--;
569
570                         if (in_brace == 0)
571                                 break;
572
573                         destp++;
574                 }
575         }
576         *destp = '\0';
577 }
578
579 void extract_one_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
580                                              gchar op, gchar cl)
581 {
582         register gchar *srcp, *destp;
583         gint in_brace;
584         gboolean in_quote = FALSE;
585
586         srcp = destp = str;
587
588         if ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
589                 memmove(destp, srcp + 1, strlen(srcp));
590                 in_brace = 1;
591                 while(*destp) {
592                         if (*destp == op && !in_quote)
593                                 in_brace++;
594                         else if (*destp == cl && !in_quote)
595                                 in_brace--;
596                         else if (*destp == quote_chr)
597                                 in_quote ^= TRUE;
598
599                         if (in_brace == 0)
600                                 break;
601
602                         destp++;
603                 }
604         }
605         *destp = '\0';
606 }
607
608 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
609                                          gchar op, gchar cl)
610 {
611         register gchar *srcp, *destp;
612         gint in_brace;
613         gboolean in_quote = FALSE;
614
615         srcp = destp = str;
616
617         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
618                 if (destp > str)
619                         *destp++ = ' ';
620                 memmove(destp, srcp + 1, strlen(srcp));
621                 in_brace = 1;
622                 while(*destp) {
623                         if (*destp == op && !in_quote)
624                                 in_brace++;
625                         else if (*destp == cl && !in_quote)
626                                 in_brace--;
627                         else if (*destp == quote_chr)
628                                 in_quote ^= TRUE;
629
630                         if (in_brace == 0)
631                                 break;
632
633                         destp++;
634                 }
635         }
636         *destp = '\0';
637 }
638
639 void eliminate_quote(gchar *str, gchar quote_chr)
640 {
641         register gchar *srcp, *destp;
642
643         srcp = destp = str;
644
645         while ((destp = strchr(destp, quote_chr))) {
646                 if ((srcp = strchr(destp + 1, quote_chr))) {
647                         srcp++;
648                         while (isspace(*srcp)) srcp++;
649                         memmove(destp, srcp, strlen(srcp) + 1);
650                 } else {
651                         *destp = '\0';
652                         break;
653                 }
654         }
655 }
656
657 void extract_quote(gchar *str, gchar quote_chr)
658 {
659         register gchar *p;
660
661         if ((str = strchr(str, quote_chr))) {
662                 if ((p = strchr(str + 1, quote_chr))) {
663                         *p = '\0';
664                         memmove(str, str + 1, p - str);
665                 }
666         }
667 }
668
669 void eliminate_address_comment(gchar *str)
670 {
671         register gchar *srcp, *destp;
672         gint in_brace;
673
674         srcp = destp = str;
675
676         while ((destp = strchr(destp, '"'))) {
677                 if ((srcp = strchr(destp + 1, '"'))) {
678                         srcp++;
679                         if (*srcp == '@') {
680                                 destp = srcp + 1;
681                         } else {
682                                 while (isspace(*srcp)) srcp++;
683                                 memmove(destp, srcp, strlen(srcp) + 1);
684                         }
685                 } else {
686                         *destp = '\0';
687                         break;
688                 }
689         }
690
691         srcp = destp = str;
692
693         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
694                 in_brace = 1;
695                 srcp = destp + 1;
696                 while (*srcp) {
697                         if (*srcp == '(')
698                                 in_brace++;
699                         else if (*srcp == ')')
700                                 in_brace--;
701                         srcp++;
702                         if (in_brace == 0)
703                                 break;
704                 }
705                 while (isspace(*srcp)) srcp++;
706                 memmove(destp, srcp, strlen(srcp) + 1);
707         }
708 }
709
710 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
711 {
712         gboolean in_quote = FALSE;
713
714         while (*str) {
715                 if (*str == c && !in_quote)
716                         return (gchar *)str;
717                 if (*str == quote_chr)
718                         in_quote ^= TRUE;
719                 str++;
720         }
721
722         return NULL;
723 }
724
725 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
726 {
727         gboolean in_quote = FALSE;
728         const gchar *p;
729
730         p = str + strlen(str) - 1;
731         while (p >= str) {
732                 if (*p == c && !in_quote)
733                         return (gchar *)p;
734                 if (*p == quote_chr)
735                         in_quote ^= TRUE;
736                 p--;
737         }
738
739         return NULL;
740 }
741
742 void extract_address(gchar *str)
743 {
744         eliminate_address_comment(str);
745         if (strchr_with_skip_quote(str, '"', '<'))
746                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
747         g_strstrip(str);
748 }
749
750 GSList *address_list_append(GSList *addr_list, const gchar *str)
751 {
752         gchar *work;
753         gchar *workp;
754
755         if (!str) return addr_list;
756
757         Xstrdup_a(work, str, return addr_list);
758
759         eliminate_address_comment(work);
760         workp = work;
761
762         while (workp && *workp) {
763                 gchar *p, *next;
764
765                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
766                         *p = '\0';
767                         next = p + 1;
768                 } else
769                         next = NULL;
770
771                 if (strchr_with_skip_quote(workp, '"', '<'))
772                         extract_parenthesis_with_skip_quote
773                                 (workp, '"', '<', '>');
774
775                 g_strstrip(workp);
776                 if (*workp)
777                         addr_list = g_slist_append(addr_list, g_strdup(workp));
778
779                 workp = next;
780         }
781
782         return addr_list;
783 }
784
785 GSList *references_list_append(GSList *msgid_list, const gchar *str)
786 {
787         const gchar *strp;
788
789         if (!str) return msgid_list;
790         strp = str;
791
792         while (strp && *strp) {
793                 const gchar *start, *end;
794                 gchar *msgid;
795
796                 if ((start = strchr(strp, '<')) != NULL) {
797                         end = strchr(start + 1, '>');
798                         if (!end) break;
799                 } else
800                         break;
801
802                 msgid = g_strndup(start + 1, end - start - 1);
803                 g_strstrip(msgid);
804                 if (*msgid)
805                         msgid_list = g_slist_append(msgid_list, msgid);
806                 else
807                         g_free(msgid);
808
809                 strp = end + 1;
810         }
811
812         return msgid_list;
813 }
814
815 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
816 {
817         gchar *work;
818         gchar *workp;
819
820         if (!str) return group_list;
821
822         Xstrdup_a(work, str, return group_list);
823
824         workp = work;
825
826         while (workp && *workp) {
827                 gchar *p, *next;
828
829                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
830                         *p = '\0';
831                         next = p + 1;
832                 } else
833                         next = NULL;
834
835                 g_strstrip(workp);
836                 if (*workp)
837                         group_list = g_slist_append(group_list,
838                                                     g_strdup(workp));
839
840                 workp = next;
841         }
842
843         return group_list;
844 }
845
846 void remove_return(gchar *str)
847 {
848         register gchar *p = str;
849
850         while (*p) {
851                 if (*p == '\n' || *p == '\r')
852                         memmove(p, p + 1, strlen(p));
853                 else
854                         p++;
855         }
856 }
857
858 void remove_space(gchar *str)
859 {
860         register gchar *p = str;
861         register gint spc;
862
863         while (*p) {
864                 spc = 0;
865                 while (isspace(*(p + spc)))
866                         spc++;
867                 if (spc)
868                         memmove(p, p + spc, strlen(p + spc) + 1);
869                 else
870                         p++;
871         }
872 }
873
874 void unfold_line(gchar *str)
875 {
876         register gchar *p = str;
877         register gint spc;
878
879         while (*p) {
880                 if (*p == '\n' || *p == '\r') {
881                         *p++ = ' ';
882                         spc = 0;
883                         while (isspace(*(p + spc)))
884                                 spc++;
885                         if (spc)
886                                 memmove(p, p + spc, strlen(p + spc) + 1);
887                 } else
888                         p++;
889         }
890 }
891
892 void subst_char(gchar *str, gchar orig, gchar subst)
893 {
894         register gchar *p = str;
895
896         while (*p) {
897                 if (*p == orig)
898                         *p = subst;
899                 p++;
900         }
901 }
902
903 gboolean is_header_line(const gchar *str)
904 {
905         if (str[0] == ':') return FALSE;
906
907         while (*str != '\0' && *str != ' ') {
908                 if (*str == ':')
909                         return TRUE;
910                 str++;
911         }
912
913         return FALSE;
914 }
915
916 gboolean is_ascii_str(const guchar *str)
917 {
918         while (*str != '\0') {
919                 if (*str != '\t' && *str != ' ' &&
920                     *str != '\r' && *str != '\n' &&
921                     (*str < 32 || *str >= 127))
922                         return FALSE;
923                 str++;
924         }
925
926         return TRUE;
927 }
928
929 gint get_quote_level(const gchar *str)
930 {
931         const gchar *first_pos;
932         const gchar *last_pos;
933         const gchar *p = str;
934         gint quote_level = -1;
935
936         /* speed up line processing by only searching to the last '>' */
937         if ((first_pos = strchr(str, '>')) != NULL) {
938                 /* skip a line if it contains a '<' before the initial '>' */
939                 if (memchr(str, '<', first_pos - str) != NULL)
940                         return -1;
941                 last_pos = strrchr(first_pos, '>');
942         } else
943                 return -1;
944
945         while (p <= last_pos) {
946                 while (p < last_pos) {
947                         if (isspace(*p))
948                                 p++;
949                         else
950                                 break;
951                 }
952
953                 if (*p == '>')
954                         quote_level++;
955                 else if (*p != '-' && !isspace(*p) && p <= last_pos) {
956                         /* any characters are allowed except '-' and space */
957                         while (*p != '-' && *p != '>' && !isspace(*p) &&
958                                p < last_pos)
959                                 p++;
960                         if (*p == '>')
961                                 quote_level++;
962                         else
963                                 break;
964                 }
965
966                 p++;
967         }
968
969         return quote_level;
970 }
971
972 GList *uri_list_extract_filenames(const gchar *uri_list)
973 {
974         GList *result = NULL;
975         const gchar *p, *q;
976         gchar *file;
977
978         p = uri_list;
979
980         while (p) {
981                 if (*p != '#') {
982                         while (isspace(*p)) p++;
983                         if (!strncmp(p, "file:", 5)) {
984                                 p += 5;
985                                 q = p;
986                                 while (*q && *q != '\n' && *q != '\r') q++;
987
988                                 if (q > p) {
989                                         q--;
990                                         while (q > p && isspace(*q)) q--;
991                                         file = g_malloc(q - p + 2);
992                                         strncpy(file, p, q - p + 1);
993                                         file[q - p + 1] = '\0';
994                                         result = g_list_append(result,file);
995                                 }
996                         }
997                 }
998                 p = strchr(p, '\n');
999                 if (p) p++;
1000         }
1001
1002         return result;
1003 }
1004
1005 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1006 {
1007         register guint haystack_len, needle_len;
1008         gboolean in_squote = FALSE, in_dquote = FALSE;
1009
1010         haystack_len = strlen(haystack);
1011         needle_len   = strlen(needle);
1012
1013         if (haystack_len < needle_len || needle_len == 0)
1014                 return NULL;
1015
1016         while (haystack_len >= needle_len) {
1017                 if (!in_squote && !in_dquote &&
1018                     !strncmp(haystack, needle, needle_len))
1019                         return (gchar *)haystack;
1020
1021                 /* 'foo"bar"' -> foo"bar"
1022                    "foo'bar'" -> foo'bar' */
1023                 if (*haystack == '\'') {
1024                         if (in_squote)
1025                                 in_squote = FALSE;
1026                         else if (!in_dquote)
1027                                 in_squote = TRUE;
1028                 } else if (*haystack == '\"') {
1029                         if (in_dquote)
1030                                 in_dquote = FALSE;
1031                         else if (!in_squote)
1032                                 in_dquote = TRUE;
1033                 }
1034
1035                 haystack++;
1036                 haystack_len--;
1037         }
1038
1039         return NULL;
1040 }
1041
1042 /* this fuction was taken from gstrfuncs.c in glib. */
1043 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1044                             gint max_tokens)
1045 {
1046         GSList *string_list = NULL, *slist;
1047         gchar **str_array, *s;
1048         guint i, n = 1;
1049
1050         g_return_val_if_fail(str != NULL, NULL);
1051         g_return_val_if_fail(delim != NULL, NULL);
1052
1053         if (max_tokens < 1)
1054                 max_tokens = G_MAXINT;
1055
1056         s = strstr_with_skip_quote(str, delim);
1057         if (s) {
1058                 guint delimiter_len = strlen(delim);
1059
1060                 do {
1061                         guint len;
1062                         gchar *new_str;
1063
1064                         len = s - str;
1065                         new_str = g_new(gchar, len + 1);
1066                         strncpy(new_str, str, len);
1067                         new_str[len] = 0;
1068                         string_list = g_slist_prepend(string_list, new_str);
1069                         n++;
1070                         str = s + delimiter_len;
1071                         s = strstr_with_skip_quote(str, delim);
1072                 } while (--max_tokens && s);
1073         }
1074
1075         if (*str) {
1076                 n++;
1077                 string_list = g_slist_prepend(string_list, g_strdup(str));
1078         }
1079
1080         str_array = g_new(gchar*, n);
1081
1082         i = n - 1;
1083
1084         str_array[i--] = NULL;
1085         for (slist = string_list; slist; slist = slist->next)
1086                 str_array[i--] = slist->data;
1087
1088         g_slist_free(string_list);
1089
1090         return str_array;
1091 }
1092
1093 /*
1094  * We need this wrapper around g_get_home_dir(), so that
1095  * we can fix some Windoze things here.  Should be done in glibc of course
1096  * but as long as we are not able to do our own extensions to glibc, we do 
1097  * it here.
1098  */
1099 gchar *get_home_dir(void)
1100 {
1101 #if HAVE_DOSISH_SYSTEM
1102     static gchar *home_dir;
1103
1104     if (!home_dir) {
1105         home_dir = read_w32_registry_string(NULL,
1106                                             "Software\\Sylpheed", "HomeDir" );
1107         if (!home_dir || !*home_dir) {
1108             if (getenv ("HOMEDRIVE") && getenv("HOMEPATH")) {
1109                 const char *s = g_get_home_dir();
1110                 if (s && *s)
1111                     home_dir = g_strdup (s);
1112             }
1113             if (!home_dir || !*home_dir) 
1114                 home_dir = g_strdup ("c:\\sylpheed");
1115         }
1116         debug_print("initialized home_dir to `%s'\n", home_dir);
1117     }
1118     return home_dir;
1119 #else /* standard glib */
1120     return g_get_home_dir();
1121 #endif
1122 }
1123
1124 gchar *get_rc_dir(void)
1125 {
1126         static gchar *rc_dir = NULL;
1127
1128         if (!rc_dir)
1129                 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1130                                      RC_DIR, NULL);
1131
1132         return rc_dir;
1133 }
1134
1135 gchar *get_news_cache_dir(void)
1136 {
1137         static gchar *news_cache_dir = NULL;
1138
1139         if (!news_cache_dir)
1140                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1141                                              NEWS_CACHE_DIR, NULL);
1142
1143         return news_cache_dir;
1144 }
1145
1146 gchar *get_imap_cache_dir(void)
1147 {
1148         static gchar *imap_cache_dir = NULL;
1149
1150         if (!imap_cache_dir)
1151                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1152                                              IMAP_CACHE_DIR, NULL);
1153
1154         return imap_cache_dir;
1155 }
1156
1157 gchar *get_mbox_cache_dir(void)
1158 {
1159         static gchar *mbox_cache_dir = NULL;
1160
1161         if (!mbox_cache_dir)
1162                 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1163                                              MBOX_CACHE_DIR, NULL);
1164
1165         return mbox_cache_dir;
1166 }
1167
1168 gchar *get_mime_tmp_dir(void)
1169 {
1170         static gchar *mime_tmp_dir = NULL;
1171
1172         if (!mime_tmp_dir)
1173                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1174                                            MIME_TMP_DIR, NULL);
1175
1176         return mime_tmp_dir;
1177 }
1178
1179 gchar *get_tmp_file(void)
1180 {
1181         static gchar *tmp_file = NULL;
1182
1183         if (!tmp_file)
1184                 tmp_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1185                                        "tmpfile", NULL);
1186
1187         return tmp_file;
1188 }
1189
1190 gchar *get_domain_name(void)
1191 {
1192         static gchar *domain_name = NULL;
1193
1194         if (!domain_name) {
1195                 gchar buf[BUFFSIZE] = "";
1196
1197                 if (gethostname(buf, sizeof(buf)) < 0) {
1198                         perror("gethostname");
1199                         strcpy(buf, "unknown");
1200                 }
1201
1202                 domain_name = g_strdup(buf);
1203         }
1204
1205         return domain_name;
1206 }
1207
1208 off_t get_file_size(const gchar *file)
1209 {
1210         struct stat s;
1211
1212         if (stat(file, &s) < 0) {
1213                 FILE_OP_ERROR(file, "stat");
1214                 return -1;
1215         }
1216
1217         return s.st_size;
1218 }
1219
1220 off_t get_left_file_size(FILE *fp)
1221 {
1222         glong pos;
1223         glong end;
1224         off_t size;
1225
1226         if ((pos = ftell(fp)) < 0) {
1227                 perror("ftell");
1228                 return -1;
1229         }
1230         if (fseek(fp, 0L, SEEK_END) < 0) {
1231                 perror("fseek");
1232                 return -1;
1233         }
1234         if ((end = ftell(fp)) < 0) {
1235                 perror("fseek");
1236                 return -1;
1237         }
1238         size = end - pos;
1239         if (fseek(fp, pos, SEEK_SET) < 0) {
1240                 perror("fseek");
1241                 return -1;
1242         }
1243
1244         return size;
1245 }
1246
1247 gboolean file_exist(const gchar *file, gboolean allow_fifo)
1248 {
1249         struct stat s;
1250
1251         if (stat(file, &s) < 0) {
1252                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1253                 return FALSE;
1254         }
1255
1256         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1257                 return TRUE;
1258
1259         return FALSE;
1260 }
1261
1262 gboolean is_dir_exist(const gchar *dir)
1263 {
1264         struct stat s;
1265
1266         if (stat(dir, &s) < 0) {
1267                 if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1268                 return FALSE;
1269         }
1270
1271         if (S_ISDIR(s.st_mode))
1272                 return TRUE;
1273
1274         return FALSE;
1275 }
1276
1277 gint change_dir(const gchar *dir)
1278 {
1279         gchar *prevdir = NULL;
1280
1281         if (debug_mode)
1282                 prevdir = g_get_current_dir();
1283
1284         if (chdir(dir) < 0) {
1285                 FILE_OP_ERROR(dir, "chdir");
1286                 if (debug_mode) g_free(prevdir);
1287                 return -1;
1288         } else if (debug_mode) {
1289                 gchar *cwd;
1290
1291                 cwd = g_get_current_dir();
1292                 if (strcmp(prevdir, cwd) != 0)
1293                         g_print("current dir: %s\n", cwd);
1294                 g_free(cwd);
1295                 g_free(prevdir);
1296         }
1297
1298         return 0;
1299 }
1300
1301 gint make_dir_hier(const gchar *dir)
1302 {
1303         gchar *parent_dir;
1304         const gchar *p;
1305
1306         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1307                 parent_dir = g_strndup(dir, p - dir);
1308                 if (*parent_dir != '\0') {
1309                         if (!is_dir_exist(parent_dir)) {
1310                                 if (mkdir(parent_dir, S_IRWXU) < 0) {
1311                                         FILE_OP_ERROR(parent_dir, "mkdir");
1312                                         g_free(parent_dir);
1313                                         return -1;
1314                                 }
1315                                 if (chmod(parent_dir, S_IRWXU) < 0)
1316                                         FILE_OP_ERROR(parent_dir, "chmod");
1317                         }
1318                 }
1319                 g_free(parent_dir);
1320         }
1321         if (!is_dir_exist(dir)) {
1322                 if (mkdir(dir, S_IRWXU) < 0) {
1323                         FILE_OP_ERROR(dir, "mkdir");
1324                         return -1;
1325                 }
1326                 if (chmod(dir, S_IRWXU) < 0)
1327                         FILE_OP_ERROR(dir, "chmod");
1328         }
1329
1330         return 0;
1331 }
1332
1333 gint remove_all_files(const gchar *dir)
1334 {
1335         DIR *dp;
1336         struct dirent *d;
1337         gchar *prev_dir;
1338
1339         prev_dir = g_get_current_dir();
1340
1341         if (chdir(dir) < 0) {
1342                 FILE_OP_ERROR(dir, "chdir");
1343                 return -1;
1344         }
1345
1346         if ((dp = opendir(".")) == NULL) {
1347                 FILE_OP_ERROR(dir, "opendir");
1348                 return -1;
1349         }
1350
1351         while ((d = readdir(dp)) != NULL) {
1352                 if (!strcmp(d->d_name, ".") ||
1353                     !strcmp(d->d_name, ".."))
1354                         continue;
1355
1356                 if (unlink(d->d_name) < 0)
1357                         FILE_OP_ERROR(d->d_name, "unlink");
1358         }
1359
1360         closedir(dp);
1361
1362         if (chdir(prev_dir) < 0) {
1363                 FILE_OP_ERROR(prev_dir, "chdir");
1364                 g_free(prev_dir);
1365                 return -1;
1366         }
1367
1368         g_free(prev_dir);
1369
1370         return 0;
1371 }
1372
1373 gint remove_numbered_files(const gchar *dir, guint first, guint last)
1374 {
1375         DIR *dp;
1376         struct dirent *d;
1377         gchar *prev_dir;
1378         gint fileno;
1379
1380         prev_dir = g_get_current_dir();
1381
1382         if (chdir(dir) < 0) {
1383                 FILE_OP_ERROR(dir, "chdir");
1384                 return -1;
1385         }
1386
1387         if ((dp = opendir(".")) == NULL) {
1388                 FILE_OP_ERROR(dir, "opendir");
1389                 return -1;
1390         }
1391
1392         while ((d = readdir(dp)) != NULL) {
1393                 fileno = to_number(d->d_name);
1394                 if (fileno >= 0 && first <= fileno && fileno <= last) {
1395                         if (unlink(d->d_name) < 0)
1396                                 FILE_OP_ERROR(d->d_name, "unlink");
1397                 }
1398         }
1399
1400         closedir(dp);
1401
1402         if (chdir(prev_dir) < 0) {
1403                 FILE_OP_ERROR(prev_dir, "chdir");
1404                 g_free(prev_dir);
1405                 return -1;
1406         }
1407
1408         g_free(prev_dir);
1409
1410         return 0;
1411 }
1412
1413 gint remove_all_numbered_files(const gchar *dir)
1414 {
1415         return remove_numbered_files(dir, 0, UINT_MAX);
1416 }
1417
1418 gint remove_dir_recursive(const gchar *dir)
1419 {
1420         struct stat s;
1421         DIR *dp;
1422         struct dirent *d;
1423         gchar *prev_dir;
1424
1425         /* g_print("dir = %s\n", dir); */
1426
1427         if (stat(dir, &s) < 0) {
1428                 FILE_OP_ERROR(dir, "stat");
1429                 if (ENOENT == errno) return 0;
1430                 return -1;
1431         }
1432
1433         if (!S_ISDIR(s.st_mode)) {
1434                 if (unlink(dir) < 0) {
1435                         FILE_OP_ERROR(dir, "unlink");
1436                         return -1;
1437                 }
1438
1439                 return 0;
1440         }
1441
1442         prev_dir = g_get_current_dir();
1443         /* g_print("prev_dir = %s\n", prev_dir); */
1444
1445         if (!path_cmp(prev_dir, dir)) {
1446                 g_free(prev_dir);
1447                 if (chdir("..") < 0) {
1448                         FILE_OP_ERROR(dir, "chdir");
1449                         return -1;
1450                 }
1451                 prev_dir = g_get_current_dir();
1452         }
1453
1454         if (chdir(dir) < 0) {
1455                 FILE_OP_ERROR(dir, "chdir");
1456                 g_free(prev_dir);
1457                 return -1;
1458         }
1459
1460         if ((dp = opendir(".")) == NULL) {
1461                 FILE_OP_ERROR(dir, "opendir");
1462                 chdir(prev_dir);
1463                 g_free(prev_dir);
1464                 return -1;
1465         }
1466
1467         /* remove all files in the directory */
1468         while ((d = readdir(dp)) != NULL) {
1469                 if (!strcmp(d->d_name, ".") ||
1470                     !strcmp(d->d_name, ".."))
1471                         continue;
1472
1473                 if (stat(d->d_name, &s) < 0) {
1474                         FILE_OP_ERROR(d->d_name, "stat");
1475                         continue;
1476                 }
1477
1478                 /* g_print("removing %s\n", d->d_name); */
1479
1480                 if (S_ISDIR(s.st_mode)) {
1481                         if (remove_dir_recursive(d->d_name) < 0) {
1482                                 g_warning("can't remove directory\n");
1483                                 return -1;
1484                         }
1485                 } else {
1486                         if (unlink(d->d_name) < 0)
1487                                 FILE_OP_ERROR(d->d_name, "unlink");
1488                 }
1489         }
1490
1491         closedir(dp);
1492
1493         if (chdir(prev_dir) < 0) {
1494                 FILE_OP_ERROR(prev_dir, "chdir");
1495                 g_free(prev_dir);
1496                 return -1;
1497         }
1498
1499         g_free(prev_dir);
1500
1501         if (rmdir(dir) < 0) {
1502                 FILE_OP_ERROR(dir, "rmdir");
1503                 return -1;
1504         }
1505
1506         return 0;
1507 }
1508
1509 #if 0
1510 /* this seems to be slower than the stdio version... */
1511 gint copy_file(const gchar *src, const gchar *dest)
1512 {
1513         gint src_fd, dest_fd;
1514         gint n_read;
1515         gint n_write;
1516         gchar buf[BUFSIZ];
1517         gchar *dest_bak = NULL;
1518
1519         if ((src_fd = open(src, O_RDONLY)) < 0) {
1520                 FILE_OP_ERROR(src, "open");
1521                 return -1;
1522         }
1523
1524         if (is_file_exist(dest)) {
1525                 dest_bak = g_strconcat(dest, ".bak", NULL);
1526                 if (rename(dest, dest_bak) < 0) {
1527                         FILE_OP_ERROR(dest, "rename");
1528                         close(src_fd);
1529                         g_free(dest_bak);
1530                         return -1;
1531                 }
1532         }
1533
1534         if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
1535                 FILE_OP_ERROR(dest, "open");
1536                 close(src_fd);
1537                 if (dest_bak) {
1538                         if (rename(dest_bak, dest) < 0)
1539                                 FILE_OP_ERROR(dest_bak, "rename");
1540                         g_free(dest_bak);
1541                 }
1542                 return -1;
1543         }
1544
1545         while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
1546                 gint len = n_read;
1547                 gchar *bufp = buf;
1548
1549                 while (len > 0) {
1550                         n_write = write(dest_fd, bufp, len);
1551                         if (n_write <= 0) {
1552                                 g_warning(_("writing to %s failed.\n"), dest);
1553                                 close(dest_fd);
1554                                 close(src_fd);
1555                                 unlink(dest);
1556                                 if (dest_bak) {
1557                                         if (rename(dest_bak, dest) < 0)
1558                                                 FILE_OP_ERROR(dest_bak, "rename");
1559                                         g_free(dest_bak);
1560                                 }
1561                                 return -1;
1562                         }
1563                         len -= n_write;
1564                         bufp += n_write;
1565                 }
1566         }
1567
1568         close(src_fd);
1569         close(dest_fd);
1570
1571         if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
1572                 g_warning(_("File copy from %s to %s failed.\n"), src, dest);
1573                 unlink(dest);
1574                 if (dest_bak) {
1575                         if (rename(dest_bak, dest) < 0)
1576                                 FILE_OP_ERROR(dest_bak, "rename");
1577                         g_free(dest_bak);
1578                 }
1579                 return -1;
1580         }
1581         g_free(dest_bak);
1582
1583         return 0;
1584 }
1585 #endif
1586
1587 gint copy_file(const gchar *src, const gchar *dest)
1588 {
1589         FILE *src_fp, *dest_fp;
1590         gint n_read;
1591         gchar buf[BUFSIZ];
1592         gchar *dest_bak = NULL;
1593         gboolean err = FALSE;
1594
1595         if ((src_fp = fopen(src, "r")) == NULL) {
1596                 FILE_OP_ERROR(src, "fopen");
1597                 return -1;
1598         }
1599         if (is_file_exist(dest)) {
1600                 dest_bak = g_strconcat(dest, ".bak", NULL);
1601                 if (rename(dest, dest_bak) < 0) {
1602                         FILE_OP_ERROR(dest, "rename");
1603                         fclose(src_fp);
1604                         g_free(dest_bak);
1605                         return -1;
1606                 }
1607         }
1608
1609         if ((dest_fp = fopen(dest, "w")) == NULL) {
1610                 FILE_OP_ERROR(dest, "fopen");
1611                 fclose(src_fp);
1612                 if (dest_bak) {
1613                         if (rename(dest_bak, dest) < 0)
1614                                 FILE_OP_ERROR(dest_bak, "rename");
1615                         g_free(dest_bak);
1616                 }
1617                 return -1;
1618         }
1619
1620         if (change_file_mode_rw(dest_fp, dest) < 0) {
1621                 FILE_OP_ERROR(dest, "chmod");
1622                 g_warning(_("can't change file mode\n"));
1623         }
1624
1625         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
1626                 if (n_read < sizeof(buf) && ferror(src_fp))
1627                         break;
1628                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1629                         g_warning(_("writing to %s failed.\n"), dest);
1630                         fclose(dest_fp);
1631                         fclose(src_fp);
1632                         unlink(dest);
1633                         if (dest_bak) {
1634                                 if (rename(dest_bak, dest) < 0)
1635                                         FILE_OP_ERROR(dest_bak, "rename");
1636                                 g_free(dest_bak);
1637                         }
1638                         return -1;
1639                 }
1640         }
1641
1642         if (ferror(src_fp)) {
1643                 FILE_OP_ERROR(src, "fread");
1644                 err = TRUE;
1645         }
1646         fclose(src_fp);
1647         if (fclose(dest_fp) == EOF) {
1648                 FILE_OP_ERROR(dest, "fclose");
1649                 err = TRUE;
1650         }
1651
1652         if (err) {
1653                 unlink(dest);
1654                 if (dest_bak) {
1655                         if (rename(dest_bak, dest) < 0)
1656                                 FILE_OP_ERROR(dest_bak, "rename");
1657                         g_free(dest_bak);
1658                 }
1659                 return -1;
1660         }
1661
1662         g_free(dest_bak);
1663
1664         return 0;
1665 }
1666
1667 gint move_file(const gchar *src, const gchar *dest)
1668 {
1669         if (is_file_exist(dest)) {
1670                 g_warning(_("move_file(): file %s already exists."), dest);
1671                 return -1;
1672         }
1673
1674         if (rename(src, dest) == 0) return 0;
1675
1676         if (EXDEV != errno) {
1677                 FILE_OP_ERROR(src, "rename");
1678                 return -1;
1679         }
1680
1681         if (copy_file(src, dest) < 0) return -1;
1682
1683         unlink(src);
1684
1685         return 0;
1686 }
1687
1688 gint change_file_mode_rw(FILE *fp, const gchar *file)
1689 {
1690 #if HAVE_FCHMOD
1691         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
1692 #else
1693         return chmod(file, S_IRUSR|S_IWUSR);
1694 #endif
1695 }
1696
1697 FILE *my_tmpfile(void)
1698 {
1699 #if HAVE_MKSTEMP
1700         const gchar suffix[] = ".XXXXXX";
1701         const gchar *tmpdir;
1702         guint tmplen;
1703         const gchar *progname;
1704         guint proglen;
1705         gchar *fname;
1706         gint fd;
1707         FILE *fp;
1708
1709         tmpdir = g_get_tmp_dir();
1710         tmplen = strlen(tmpdir);
1711         progname = g_get_prgname();
1712         proglen = strlen(progname);
1713         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
1714                 return tmpfile());
1715
1716         memcpy(fname, tmpdir, tmplen);
1717         fname[tmplen] = G_DIR_SEPARATOR;
1718         memcpy(fname + tmplen + 1, progname, proglen);
1719         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
1720
1721         fd = mkstemp(fname);
1722         if (fd < 0)
1723                 return tmpfile();
1724
1725         unlink(fname);
1726
1727         fp = fdopen(fd, "w+b");
1728         if (!fp)
1729                 close(fd);
1730         else
1731                 return fp;
1732 #endif /* HAVE_MKSTEMP */
1733
1734         return tmpfile();
1735 }
1736
1737 gint execute_async(gchar *const argv[])
1738 {
1739         pid_t pid;
1740
1741         if ((pid = fork()) < 0) {
1742                 perror("fork");
1743                 return -1;
1744         }
1745
1746         if (pid == 0) {                 /* child process */
1747                 pid_t gch_pid;
1748
1749                 if ((gch_pid = fork()) < 0) {
1750                         perror("fork");
1751                         _exit(1);
1752                 }
1753
1754                 if (gch_pid == 0) {     /* grandchild process */
1755                         execvp(argv[0], argv);
1756
1757                         perror("execvp");
1758                         _exit(1);
1759                 }
1760
1761                 _exit(0);
1762         }
1763
1764         waitpid(pid, NULL, 0);
1765
1766         return 0;
1767 }
1768
1769 gint execute_sync(gchar *const argv[])
1770 {
1771         pid_t pid;
1772
1773         if ((pid = fork()) < 0) {
1774                 perror("fork");
1775                 return -1;
1776         }
1777
1778         if (pid == 0) {         /* child process */
1779                 execvp(argv[0], argv);
1780
1781                 perror("execvp");
1782                 _exit(1);
1783         }
1784
1785         waitpid(pid, NULL, 0);
1786
1787         return 0;
1788 }
1789
1790 gint execute_command_line(const gchar *cmdline, gboolean async)
1791 {
1792         gchar **argv;
1793         gint i;
1794         gint ret;
1795
1796         argv = strsplit_with_quote(cmdline, " ", 0);
1797
1798         for (i = 0; argv[i] != NULL; i++) {
1799                 gchar *str = argv[i];
1800
1801                 if (str[0] == '\'' || str[0] == '\"') {
1802                         gint len;
1803
1804                         len = strlen(str);
1805                         if (str[len - 1] == str[0]) {
1806                                 str[len - 1] = '\0';
1807                                 memmove(str, str + 1, len - 1);
1808                         }
1809                 }
1810         }
1811
1812         if (async)
1813                 ret = execute_async(argv);
1814         else
1815                 ret = execute_sync(argv);
1816         g_strfreev(argv);
1817
1818         return ret;
1819 }
1820
1821 gint open_uri(const gchar *uri, const gchar *cmdline)
1822 {
1823         static gchar *default_cmdline = "netscape -remote openURL(%s,raise)";
1824         gchar buf[BUFFSIZE];
1825         gchar *p;
1826
1827         g_return_val_if_fail(uri != NULL, -1);
1828
1829         if (cmdline &&
1830             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1831             !strchr(p + 2, '%'))
1832                 g_snprintf(buf, sizeof(buf), cmdline, uri);
1833         else {
1834                 if (cmdline)
1835                         g_warning(_("Open URI command line is invalid: `%s'"),
1836                                   cmdline);
1837                 g_snprintf(buf, sizeof(buf), default_cmdline, uri);
1838         }
1839
1840         execute_command_line(buf, TRUE);
1841
1842         return 0;
1843 }
1844
1845 time_t remote_tzoffset_sec(const gchar *zone)
1846 {
1847         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
1848         gchar zone3[4];
1849         gchar *p;
1850         gchar c;
1851         gint iustz;
1852         gint h, m;
1853         time_t remoteoffset;
1854
1855         strncpy(zone3, zone, 3);
1856         zone3[3] = '\0';
1857         remoteoffset = 0;
1858
1859         if (sscanf(zone, "%c%2d%2d", &c, &h, &m) == 3 &&
1860             (c == '+' || c == '-')) {
1861                 remoteoffset = ((h * 60) + m) * 60;
1862                 if (c == '-')
1863                         remoteoffset = -remoteoffset;
1864         } else if (!strncmp(zone, "UT" , 2) ||
1865                    !strncmp(zone, "GMT", 2)) {
1866                 remoteoffset = 0;
1867         } else if (strlen(zone3) == 3 &&
1868                    (p = strstr(ustzstr, zone3)) != NULL &&
1869                    (p - ustzstr) % 3 == 0) {
1870                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
1871                 remoteoffset = iustz * 3600;
1872         } else if (strlen(zone3) == 1) {
1873                 switch (zone[0]) {
1874                 case 'Z': remoteoffset =   0; break;
1875                 case 'A': remoteoffset =  -1; break;
1876                 case 'B': remoteoffset =  -2; break;
1877                 case 'C': remoteoffset =  -3; break;
1878                 case 'D': remoteoffset =  -4; break;
1879                 case 'E': remoteoffset =  -5; break;
1880                 case 'F': remoteoffset =  -6; break;
1881                 case 'G': remoteoffset =  -7; break;
1882                 case 'H': remoteoffset =  -8; break;
1883                 case 'I': remoteoffset =  -9; break;
1884                 case 'K': remoteoffset = -10; break; /* J is not used */
1885                 case 'L': remoteoffset = -11; break;
1886                 case 'M': remoteoffset = -12; break;
1887                 case 'N': remoteoffset =   1; break;
1888                 case 'O': remoteoffset =   2; break;
1889                 case 'P': remoteoffset =   3; break;
1890                 case 'Q': remoteoffset =   4; break;
1891                 case 'R': remoteoffset =   5; break;
1892                 case 'S': remoteoffset =   6; break;
1893                 case 'T': remoteoffset =   7; break;
1894                 case 'U': remoteoffset =   8; break;
1895                 case 'V': remoteoffset =   9; break;
1896                 case 'W': remoteoffset =  10; break;
1897                 case 'X': remoteoffset =  11; break;
1898                 case 'Y': remoteoffset =  12; break;
1899                 default:  remoteoffset =   0; break;
1900                 }
1901                 remoteoffset = remoteoffset * 3600;
1902         }
1903
1904         return remoteoffset;
1905 }
1906
1907 time_t tzoffset_sec(time_t *now)
1908 {
1909         struct tm gmt, *lt;
1910         gint off;
1911
1912         gmt = *gmtime(now);
1913         lt = localtime(now);
1914
1915         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1916
1917         if (lt->tm_year < gmt.tm_year)
1918                 off -= 24 * 60;
1919         else if (lt->tm_year > gmt.tm_year)
1920                 off += 24 * 60;
1921         else if (lt->tm_yday < gmt.tm_yday)
1922                 off -= 24 * 60;
1923         else if (lt->tm_yday > gmt.tm_yday)
1924                 off += 24 * 60;
1925
1926         if (off >= 24 * 60)             /* should be impossible */
1927                 off = 23 * 60 + 59;     /* if not, insert silly value */
1928         if (off <= -24 * 60)
1929                 off = -(23 * 60 + 59);
1930         if (off > 12 * 60)
1931                 off -= 24 * 60;
1932         if (off < -12 * 60)
1933                 off += 24 * 60;
1934
1935         return off * 60;
1936 }
1937
1938 /* calculate timezone offset */
1939 gchar *tzoffset(time_t *now)
1940 {
1941         static gchar offset_string[6];
1942         struct tm gmt, *lt;
1943         gint off;
1944         gchar sign = '+';
1945
1946         gmt = *gmtime(now);
1947         lt = localtime(now);
1948
1949         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1950
1951         if (lt->tm_year < gmt.tm_year)
1952                 off -= 24 * 60;
1953         else if (lt->tm_year > gmt.tm_year)
1954                 off += 24 * 60;
1955         else if (lt->tm_yday < gmt.tm_yday)
1956                 off -= 24 * 60;
1957         else if (lt->tm_yday > gmt.tm_yday)
1958                 off += 24 * 60;
1959
1960         if (off < 0) {
1961                 sign = '-';
1962                 off = -off;
1963         }
1964
1965         if (off >= 24 * 60)             /* should be impossible */
1966                 off = 23 * 60 + 59;     /* if not, insert silly value */
1967
1968         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
1969
1970         return offset_string;
1971 }
1972
1973 void get_rfc822_date(gchar *buf, gint len)
1974 {
1975         struct tm *lt;
1976         time_t t;
1977         gchar day[4], mon[4];
1978         gint dd, hh, mm, ss, yyyy;
1979
1980         t = time(NULL);
1981         lt = localtime(&t);
1982
1983         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
1984                day, mon, &dd, &hh, &mm, &ss, &yyyy);
1985         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
1986                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
1987 }
1988
1989 static FILE *log_fp = NULL;
1990
1991 void set_log_file(const gchar *filename)
1992 {
1993         if (log_fp) return;
1994         log_fp = fopen(filename, "w");
1995         if (!log_fp)
1996                 FILE_OP_ERROR(filename, "fopen");
1997 }
1998
1999 void close_log_file(void)
2000 {
2001         if (log_fp) {
2002                 fclose(log_fp);
2003                 log_fp = NULL;
2004         }
2005 }
2006
2007 static guint log_verbosity_count = 0;
2008
2009 void log_verbosity_set(gboolean verbose)
2010 {
2011         if (verbose)
2012                 log_verbosity_count++;
2013         else if (log_verbosity_count > 0)
2014                 log_verbosity_count--;
2015 }
2016
2017 void debug_print_real(const gchar *format, ...)
2018 {
2019         va_list args;
2020         gchar buf[BUFFSIZE];
2021
2022         if (!debug_mode) return;
2023
2024         va_start(args, format);
2025         g_vsnprintf(buf, sizeof(buf), format, args);
2026         va_end(args);
2027
2028         fputs(buf, stdout);
2029 }
2030
2031 void log_print(const gchar *format, ...)
2032 {
2033         va_list args;
2034         gchar buf[BUFFSIZE];
2035         gchar *logbuf;
2036         gchar timestr[6];
2037         time_t t;
2038
2039         va_start(args, format);
2040         g_vsnprintf(buf, sizeof(buf), format, args);
2041         va_end(args);
2042         
2043         time(&t);
2044         strftime(timestr, 6, "%H:%M", localtime(&t));
2045         logbuf = g_strdup_printf("[%s] %s", timestr, buf);
2046
2047         if (debug_mode) fputs(logbuf, stdout);
2048         log_window_append(logbuf, LOG_NORMAL);
2049         if (log_fp) {
2050                 fputs(logbuf, log_fp);
2051                 fflush(log_fp);
2052         }
2053         if (log_verbosity_count)
2054                 statusbar_puts_all(buf);
2055         g_free(logbuf);
2056 }
2057
2058 void log_message(const gchar *format, ...)
2059 {
2060         va_list args;
2061         gchar buf[BUFFSIZE];
2062
2063         va_start(args, format);
2064         g_vsnprintf(buf, sizeof(buf), format, args);
2065         va_end(args);
2066
2067         if (debug_mode) g_message("%s", buf);
2068         log_window_append(buf, LOG_MSG);
2069         if (log_fp) {
2070                 fputs("message: ", log_fp);
2071                 fputs(buf, log_fp);
2072                 fflush(log_fp);
2073         }
2074         statusbar_puts_all(buf);
2075 }
2076
2077 void log_warning(const gchar *format, ...)
2078 {
2079         va_list args;
2080         gchar buf[BUFFSIZE];
2081
2082         va_start(args, format);
2083         g_vsnprintf(buf, sizeof(buf), format, args);
2084         va_end(args);
2085
2086         g_warning("%s", buf);
2087         log_window_append(buf, LOG_WARN);
2088         if (log_fp) {
2089                 fputs("*** warning: ", log_fp);
2090                 fputs(buf, log_fp);
2091                 fflush(log_fp);
2092         }
2093 }
2094
2095 void log_error(const gchar *format, ...)
2096 {
2097         va_list args;
2098         gchar buf[BUFFSIZE];
2099
2100         va_start(args, format);
2101         g_vsnprintf(buf, sizeof(buf), format, args);
2102         va_end(args);
2103
2104         g_warning("%s", buf);
2105         log_window_append(buf, LOG_ERROR);
2106         if (log_fp) {
2107                 fputs("*** error: ", log_fp);
2108                 fputs(buf, log_fp);
2109                 fflush(log_fp);
2110         }
2111 }