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