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