Added NNTP authentication support
[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 gchar *get_rc_dir(void)
1032 {
1033         static gchar *rc_dir = NULL;
1034
1035         if (!rc_dir)
1036                 rc_dir = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S,
1037                                      RC_DIR, NULL);
1038
1039         return rc_dir;
1040 }
1041
1042 gchar *get_news_cache_dir(void)
1043 {
1044         static gchar *news_cache_dir = NULL;
1045
1046         if (!news_cache_dir)
1047                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1048                                              NEWS_CACHE_DIR, NULL);
1049
1050         return news_cache_dir;
1051 }
1052
1053 gchar *get_imap_cache_dir(void)
1054 {
1055         static gchar *imap_cache_dir = NULL;
1056
1057         if (!imap_cache_dir)
1058                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1059                                              IMAP_CACHE_DIR, NULL);
1060
1061         return imap_cache_dir;
1062 }
1063
1064 gchar *get_mime_tmp_dir(void)
1065 {
1066         static gchar *mime_tmp_dir = NULL;
1067
1068         if (!mime_tmp_dir)
1069                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1070                                            MIME_TMP_DIR, NULL);
1071
1072         return mime_tmp_dir;
1073 }
1074
1075 gchar *get_tmp_file(void)
1076 {
1077         static gchar *tmp_file = NULL;
1078
1079         if (!tmp_file)
1080                 tmp_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1081                                        "tmpfile", NULL);
1082
1083         return tmp_file;
1084 }
1085
1086 gchar *get_domain_name(void)
1087 {
1088         static gchar *domain_name = NULL;
1089
1090         if (!domain_name) {
1091                 gchar buf[BUFFSIZE] = "";
1092
1093                 if (gethostname(buf, sizeof(buf)) < 0) {
1094                         perror("gethostname");
1095                         strcpy(buf, "unknown");
1096                 }
1097
1098                 domain_name = g_strdup(buf);
1099         }
1100
1101         return domain_name;
1102 }
1103
1104 off_t get_file_size(const gchar *file)
1105 {
1106         struct stat s;
1107
1108         if (stat(file, &s) < 0) {
1109                 FILE_OP_ERROR(file, "stat");
1110                 return -1;
1111         }
1112
1113         return s.st_size;
1114 }
1115
1116 gboolean file_exist(const gchar *file, gboolean allow_fifo)
1117 {
1118         struct stat s;
1119
1120         if (stat(file, &s) < 0) {
1121                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1122                 return FALSE;
1123         }
1124
1125         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1126                 return TRUE;
1127
1128         return FALSE;
1129 }
1130
1131 gboolean is_dir_exist(const gchar *dir)
1132 {
1133         struct stat s;
1134
1135         if (stat(dir, &s) < 0) {
1136                 if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1137                 return FALSE;
1138         }
1139
1140         if (S_ISDIR(s.st_mode))
1141                 return TRUE;
1142
1143         return FALSE;
1144 }
1145
1146 gint change_dir(const gchar *dir)
1147 {
1148         gchar *prevdir = NULL;
1149
1150         if (debug_mode)
1151                 prevdir = g_get_current_dir();
1152
1153         if (chdir(dir) < 0) {
1154                 FILE_OP_ERROR(dir, "chdir");
1155                 if (debug_mode) g_free(prevdir);
1156                 return -1;
1157         } else if (debug_mode) {
1158                 gchar *cwd;
1159
1160                 cwd = g_get_current_dir();
1161                 if (strcmp(prevdir, cwd) != 0)
1162                         g_print("current dir: %s\n", cwd);
1163                 g_free(cwd);
1164                 g_free(prevdir);
1165         }
1166
1167         return 0;
1168 }
1169
1170 gint make_dir_hier(const gchar *dir)
1171 {
1172         gchar *parent_dir;
1173         const gchar *p;
1174
1175         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1176                 parent_dir = g_strndup(dir, p - dir);
1177                 if (*parent_dir != '\0') {
1178                         if (!is_dir_exist(parent_dir)) {
1179                                 if (mkdir(parent_dir, S_IRWXU) < 0) {
1180                                         FILE_OP_ERROR(parent_dir, "mkdir");
1181                                         g_free(parent_dir);
1182                                         return -1;
1183                                 }
1184                                 if (chmod(parent_dir, S_IRWXU) < 0)
1185                                         FILE_OP_ERROR(parent_dir, "chmod");
1186                         }
1187                 }
1188                 g_free(parent_dir);
1189         }
1190         if (!is_dir_exist(dir)) {
1191                 if (mkdir(dir, S_IRWXU) < 0) {
1192                         FILE_OP_ERROR(dir, "mkdir");
1193                         return -1;
1194                 }
1195                 if (chmod(dir, S_IRWXU) < 0)
1196                         FILE_OP_ERROR(dir, "chmod");
1197         }
1198
1199         return 0;
1200 }
1201
1202 gint remove_all_files(const gchar *dir)
1203 {
1204         DIR *dp;
1205         struct dirent *d;
1206         gchar *prev_dir;
1207
1208         prev_dir = g_get_current_dir();
1209
1210         if (chdir(dir) < 0) {
1211                 FILE_OP_ERROR(dir, "chdir");
1212                 return -1;
1213         }
1214
1215         if ((dp = opendir(".")) == NULL) {
1216                 FILE_OP_ERROR(dir, "opendir");
1217                 return -1;
1218         }
1219
1220         while ((d = readdir(dp)) != NULL) {
1221                 if (!strcmp(d->d_name, ".") ||
1222                     !strcmp(d->d_name, ".."))
1223                         continue;
1224
1225                 if (unlink(d->d_name) < 0)
1226                         FILE_OP_ERROR(d->d_name, "unlink");
1227         }
1228
1229         closedir(dp);
1230
1231         if (chdir(prev_dir) < 0) {
1232                 FILE_OP_ERROR(prev_dir, "chdir");
1233                 g_free(prev_dir);
1234                 return -1;
1235         }
1236
1237         g_free(prev_dir);
1238
1239         return 0;
1240 }
1241
1242 gint remove_dir_recursive(const gchar *dir)
1243 {
1244         struct stat s;
1245         DIR *dp;
1246         struct dirent *d;
1247         gchar *prev_dir;
1248
1249         //g_print("dir = %s\n", dir);
1250
1251         if (stat(dir, &s) < 0) {
1252                 FILE_OP_ERROR(dir, "stat");
1253                 if (ENOENT == errno) return 0;
1254                 return -1;
1255         }
1256
1257         if (!S_ISDIR(s.st_mode)) {
1258                 if (unlink(dir) < 0) {
1259                         FILE_OP_ERROR(dir, "unlink");
1260                         return -1;
1261                 }
1262
1263                 return 0;
1264         }
1265
1266         prev_dir = g_get_current_dir();
1267         //g_print("prev_dir = %s\n", prev_dir);
1268
1269         if (!path_cmp(prev_dir, dir)) {
1270                 g_free(prev_dir);
1271                 if (chdir("..") < 0) {
1272                         FILE_OP_ERROR(dir, "chdir");
1273                         return -1;
1274                 }
1275                 prev_dir = g_get_current_dir();
1276         }
1277
1278         if (chdir(dir) < 0) {
1279                 FILE_OP_ERROR(dir, "chdir");
1280                 g_free(prev_dir);
1281                 return -1;
1282         }
1283
1284         if ((dp = opendir(".")) == NULL) {
1285                 FILE_OP_ERROR(dir, "opendir");
1286                 chdir(prev_dir);
1287                 g_free(prev_dir);
1288                 return -1;
1289         }
1290
1291         /* remove all files in the directory */
1292         while ((d = readdir(dp)) != NULL) {
1293                 if (!strcmp(d->d_name, ".") ||
1294                     !strcmp(d->d_name, ".."))
1295                         continue;
1296
1297                 if (stat(d->d_name, &s) < 0) {
1298                         FILE_OP_ERROR(d->d_name, "stat");
1299                         continue;
1300                 }
1301
1302                 //g_print("removing %s\n", d->d_name);
1303
1304                 if (S_ISDIR(s.st_mode)) {
1305                         if (remove_dir_recursive(d->d_name) < 0) {
1306                                 g_warning("can't remove directory\n");
1307                                 return -1;
1308                         }
1309                 } else {
1310                         if (unlink(d->d_name) < 0)
1311                                 FILE_OP_ERROR(d->d_name, "unlink");
1312                 }
1313         }
1314
1315         closedir(dp);
1316
1317         if (chdir(prev_dir) < 0) {
1318                 FILE_OP_ERROR(prev_dir, "chdir");
1319                 g_free(prev_dir);
1320                 return -1;
1321         }
1322
1323         g_free(prev_dir);
1324
1325         if (rmdir(dir) < 0) {
1326                 FILE_OP_ERROR(dir, "rmdir");
1327                 return -1;
1328         }
1329
1330         return 0;
1331 }
1332
1333 #if 0
1334 /* this seems to be slower than the stdio version... */
1335 gint copy_file(const gchar *src, const gchar *dest)
1336 {
1337         gint src_fd, dest_fd;
1338         gint n_read;
1339         gint n_write;
1340         gchar buf[BUFSIZ];
1341         gchar *dest_bak = NULL;
1342
1343         if ((src_fd = open(src, O_RDONLY)) < 0) {
1344                 FILE_OP_ERROR(src, "open");
1345                 return -1;
1346         }
1347
1348         if (is_file_exist(dest)) {
1349                 dest_bak = g_strconcat(dest, ".bak", NULL);
1350                 if (rename(dest, dest_bak) < 0) {
1351                         FILE_OP_ERROR(dest, "rename");
1352                         close(src_fd);
1353                         g_free(dest_bak);
1354                         return -1;
1355                 }
1356         }
1357
1358         if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
1359                 FILE_OP_ERROR(dest, "open");
1360                 close(src_fd);
1361                 if (dest_bak) {
1362                         if (rename(dest_bak, dest) < 0)
1363                                 FILE_OP_ERROR(dest_bak, "rename");
1364                         g_free(dest_bak);
1365                 }
1366                 return -1;
1367         }
1368
1369         while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
1370                 gint len = n_read;
1371                 gchar *bufp = buf;
1372
1373                 while (len > 0) {
1374                         n_write = write(dest_fd, bufp, len);
1375                         if (n_write <= 0) {
1376                                 g_warning(_("writing to %s failed.\n"), dest);
1377                                 close(dest_fd);
1378                                 close(src_fd);
1379                                 unlink(dest);
1380                                 if (dest_bak) {
1381                                         if (rename(dest_bak, dest) < 0)
1382                                                 FILE_OP_ERROR(dest_bak, "rename");
1383                                         g_free(dest_bak);
1384                                 }
1385                                 return -1;
1386                         }
1387                         len -= n_write;
1388                         bufp += n_write;
1389                 }
1390         }
1391
1392         close(src_fd);
1393         close(dest_fd);
1394
1395         if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
1396                 g_warning(_("File copy from %s to %s failed.\n"), src, dest);
1397                 unlink(dest);
1398                 if (dest_bak) {
1399                         if (rename(dest_bak, dest) < 0)
1400                                 FILE_OP_ERROR(dest_bak, "rename");
1401                         g_free(dest_bak);
1402                 }
1403                 return -1;
1404         }
1405         g_free(dest_bak);
1406
1407         return 0;
1408 }
1409 #endif
1410
1411 gint copy_file(const gchar *src, const gchar *dest)
1412 {
1413         FILE *src_fp, *dest_fp;
1414         gint n_read;
1415         gchar buf[BUFSIZ];
1416         gchar *dest_bak = NULL;
1417         gboolean err = FALSE;
1418
1419         if ((src_fp = fopen(src, "r")) == NULL) {
1420                 FILE_OP_ERROR(src, "fopen");
1421                 return -1;
1422         }
1423         if (is_file_exist(dest)) {
1424                 dest_bak = g_strconcat(dest, ".bak", NULL);
1425                 if (rename(dest, dest_bak) < 0) {
1426                         FILE_OP_ERROR(dest, "rename");
1427                         fclose(src_fp);
1428                         g_free(dest_bak);
1429                         return -1;
1430                 }
1431         }
1432
1433         if ((dest_fp = fopen(dest, "w")) == NULL) {
1434                 FILE_OP_ERROR(dest, "fopen");
1435                 fclose(src_fp);
1436                 if (dest_bak) {
1437                         if (rename(dest_bak, dest) < 0)
1438                                 FILE_OP_ERROR(dest_bak, "rename");
1439                         g_free(dest_bak);
1440                 }
1441                 return -1;
1442         }
1443
1444         if (change_file_mode_rw(dest_fp, dest) < 0) {
1445                 FILE_OP_ERROR(dest, "chmod");
1446                 g_warning(_("can't change file mode\n"));
1447         }
1448
1449         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
1450                 if (n_read < sizeof(buf) && ferror(src_fp))
1451                         break;
1452                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1453                         g_warning(_("writing to %s failed.\n"), dest);
1454                         fclose(dest_fp);
1455                         fclose(src_fp);
1456                         unlink(dest);
1457                         if (dest_bak) {
1458                                 if (rename(dest_bak, dest) < 0)
1459                                         FILE_OP_ERROR(dest_bak, "rename");
1460                                 g_free(dest_bak);
1461                         }
1462                         return -1;
1463                 }
1464         }
1465
1466         if (ferror(src_fp)) {
1467                 FILE_OP_ERROR(src, "fread");
1468                 err = TRUE;
1469         }
1470         fclose(src_fp);
1471         if (fclose(dest_fp) == EOF) {
1472                 FILE_OP_ERROR(dest, "fclose");
1473                 err = TRUE;
1474         }
1475
1476         if (err) {
1477                 unlink(dest);
1478                 if (dest_bak) {
1479                         if (rename(dest_bak, dest) < 0)
1480                                 FILE_OP_ERROR(dest_bak, "rename");
1481                         g_free(dest_bak);
1482                 }
1483                 return -1;
1484         }
1485
1486         g_free(dest_bak);
1487
1488         return 0;
1489 }
1490
1491 gint change_file_mode_rw(FILE *fp, const gchar *file)
1492 {
1493 #if HAVE_FCHMOD
1494         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
1495 #else
1496         return chmod(file, S_IRUSR|S_IWUSR);
1497 #endif
1498 }
1499
1500 FILE *my_tmpfile(void)
1501 {
1502 #if HAVE_MKSTEMP
1503         const gchar suffix[] = ".XXXXXX";
1504         const gchar *tmpdir;
1505         guint tmplen;
1506         const gchar *progname;
1507         guint proglen;
1508         gchar *fname;
1509         gint fd;
1510         FILE *fp;
1511
1512         tmpdir = g_get_tmp_dir();
1513         tmplen = strlen(tmpdir);
1514         progname = g_get_prgname();
1515         proglen = strlen(progname);
1516         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
1517                 return tmpfile());
1518
1519         memcpy(fname, tmpdir, tmplen);
1520         fname[tmplen] = G_DIR_SEPARATOR;
1521         memcpy(fname + tmplen + 1, progname, proglen);
1522         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
1523
1524         fd = mkstemp(fname);
1525         if (fd < 0)
1526                 return tmpfile();
1527
1528         unlink(fname);
1529
1530         fp = fdopen(fd, "w+b");
1531         if (!fp)
1532                 close(fd);
1533         else
1534                 return fp;
1535 #endif /* HAVE_MKSTEMP */
1536
1537         return tmpfile();
1538 }
1539
1540 gint execute_async(gchar *const argv[])
1541 {
1542         pid_t pid;
1543
1544         if ((pid = fork()) < 0) {
1545                 perror("fork");
1546                 return -1;
1547         }
1548
1549         if (pid == 0) {                 /* child process */
1550                 pid_t gch_pid;
1551
1552                 if ((gch_pid = fork()) < 0) {
1553                         perror("fork");
1554                         _exit(1);
1555                 }
1556
1557                 if (gch_pid == 0) {     /* grandchild process */
1558                         execvp(argv[0], argv);
1559
1560                         perror("execvp");
1561                         _exit(1);
1562                 }
1563
1564                 _exit(0);
1565         }
1566
1567         waitpid(pid, NULL, 0);
1568
1569         return 0;
1570 }
1571
1572 gint execute_command_line(const gchar *cmdline)
1573 {
1574         gchar **argv;
1575         gint i;
1576         gint ret;
1577
1578         argv = strsplit_with_quote(cmdline, " ", 0);
1579
1580         for (i = 0; argv[i] != NULL; i++) {
1581                 gchar *str = argv[i];
1582
1583                 if (str[0] == '\'' || str[0] == '\"') {
1584                         gint len;
1585
1586                         len = strlen(str);
1587                         if (str[len - 1] == str[0]) {
1588                                 str[len - 1] = '\0';
1589                                 memmove(str, str + 1, len - 1);
1590                         }
1591                 }
1592         }
1593
1594         ret = execute_async(argv);
1595         g_strfreev(argv);
1596
1597         return ret;
1598 }
1599
1600 gint open_uri(const gchar *uri, const gchar *cmdline)
1601 {
1602         static gchar *default_cmdline = "netscape -remote openURL(%s,raise)";
1603         gchar buf[BUFFSIZE];
1604         gchar *p;
1605
1606         g_return_val_if_fail(uri != NULL, -1);
1607
1608         if (cmdline &&
1609             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1610             !strchr(p + 2, '%'))
1611                 g_snprintf(buf, sizeof(buf), cmdline, uri);
1612         else {
1613                 if (cmdline)
1614                         g_warning(_("Open URI command line is invalid: `%s'"),
1615                                   cmdline);
1616                 g_snprintf(buf, sizeof(buf), default_cmdline, uri);
1617         }
1618
1619         execute_command_line(buf);
1620
1621         return 0;
1622 }
1623
1624 time_t remote_tzoffset_sec(const gchar *zone)
1625 {
1626         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
1627         gchar zone3[4];
1628         gchar *p;
1629         gchar c;
1630         gint iustz;
1631         gint h, m;
1632         time_t remoteoffset;
1633
1634         strncpy(zone3, zone, 3);
1635         zone3[3] = '\0';
1636         remoteoffset = 0;
1637
1638         if (sscanf(zone, "%c%2d%2d", &c, &h, &m) == 3 &&
1639             (c == '+' || c == '-')) {
1640                 remoteoffset = ((h * 60) + m) * 60;
1641                 if (c == '-')
1642                         remoteoffset = -remoteoffset;
1643         } else if (!strncmp(zone, "UT" , 2) ||
1644                    !strncmp(zone, "GMT", 2)) {
1645                 remoteoffset = 0;
1646         } else if (strlen(zone3) == 3 &&
1647                    (p = strstr(ustzstr, zone3)) != NULL &&
1648                    (p - ustzstr) % 3 == 0) {
1649                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
1650                 remoteoffset = iustz * 3600;
1651         } else if (strlen(zone3) == 1) {
1652                 switch (zone[0]) {
1653                 case 'Z': remoteoffset =   0; break;
1654                 case 'A': remoteoffset =  -1; break;
1655                 case 'B': remoteoffset =  -2; break;
1656                 case 'C': remoteoffset =  -3; break;
1657                 case 'D': remoteoffset =  -4; break;
1658                 case 'E': remoteoffset =  -5; break;
1659                 case 'F': remoteoffset =  -6; break;
1660                 case 'G': remoteoffset =  -7; break;
1661                 case 'H': remoteoffset =  -8; break;
1662                 case 'I': remoteoffset =  -9; break;
1663                 case 'K': remoteoffset = -10; break; /* J is not used */
1664                 case 'L': remoteoffset = -11; break;
1665                 case 'M': remoteoffset = -12; break;
1666                 case 'N': remoteoffset =   1; break;
1667                 case 'O': remoteoffset =   2; break;
1668                 case 'P': remoteoffset =   3; break;
1669                 case 'Q': remoteoffset =   4; break;
1670                 case 'R': remoteoffset =   5; break;
1671                 case 'S': remoteoffset =   6; break;
1672                 case 'T': remoteoffset =   7; break;
1673                 case 'U': remoteoffset =   8; break;
1674                 case 'V': remoteoffset =   9; break;
1675                 case 'W': remoteoffset =  10; break;
1676                 case 'X': remoteoffset =  11; break;
1677                 case 'Y': remoteoffset =  12; break;
1678                 default:  remoteoffset =   0; break;
1679                 }
1680                 remoteoffset = remoteoffset * 3600;
1681         }
1682
1683         return remoteoffset;
1684 }
1685
1686 time_t tzoffset_sec(time_t *now)
1687 {
1688         struct tm gmt, *lt;
1689         gint off;
1690
1691         gmt = *gmtime(now);
1692         lt = localtime(now);
1693
1694         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1695
1696         if (lt->tm_year < gmt.tm_year)
1697                 off -= 24 * 60;
1698         else if (lt->tm_year > gmt.tm_year)
1699                 off += 24 * 60;
1700         else if (lt->tm_yday < gmt.tm_yday)
1701                 off -= 24 * 60;
1702         else if (lt->tm_yday > gmt.tm_yday)
1703                 off += 24 * 60;
1704
1705         if (off >= 24 * 60)             /* should be impossible */
1706                 off = 23 * 60 + 59;     /* if not, insert silly value */
1707         if (off <= -24 * 60)
1708                 off = -(23 * 60 + 59);
1709         if (off > 12 * 60)
1710                 off -= 24 * 60;
1711         if (off < -12 * 60)
1712                 off += 24 * 60;
1713
1714         return off * 60;
1715 }
1716
1717 /* calculate timezone offset */
1718 gchar *tzoffset(time_t *now)
1719 {
1720         static gchar offset_string[6];
1721         struct tm gmt, *lt;
1722         gint off;
1723         gchar sign = '+';
1724
1725         gmt = *gmtime(now);
1726         lt = localtime(now);
1727
1728         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1729
1730         if (lt->tm_year < gmt.tm_year)
1731                 off -= 24 * 60;
1732         else if (lt->tm_year > gmt.tm_year)
1733                 off += 24 * 60;
1734         else if (lt->tm_yday < gmt.tm_yday)
1735                 off -= 24 * 60;
1736         else if (lt->tm_yday > gmt.tm_yday)
1737                 off += 24 * 60;
1738
1739         if (off < 0) {
1740                 sign = '-';
1741                 off = -off;
1742         }
1743
1744         if (off >= 24 * 60)             /* should be impossible */
1745                 off = 23 * 60 + 59;     /* if not, insert silly value */
1746
1747         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
1748
1749         return offset_string;
1750 }
1751
1752 void get_rfc822_date(gchar *buf, gint len)
1753 {
1754         struct tm *lt;
1755         time_t t;
1756         gchar day[4], mon[4];
1757         gint dd, hh, mm, ss, yyyy;
1758
1759         t = time(NULL);
1760         lt = localtime(&t);
1761
1762         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
1763                day, mon, &dd, &hh, &mm, &ss, &yyyy);
1764         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
1765                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
1766 }
1767
1768 void debug_print(const gchar *format, ...)
1769 {
1770         va_list args;
1771         gchar buf[BUFFSIZE];
1772
1773         if (!debug_mode) return;
1774
1775         va_start(args, format);
1776         g_vsnprintf(buf, sizeof(buf), format, args);
1777         va_end(args);
1778
1779         fputs(buf, stdout);
1780 }
1781
1782 void log_print(const gchar *format, ...)
1783 {
1784         va_list args;
1785         gchar buf[BUFFSIZE];
1786
1787         va_start(args, format);
1788         g_vsnprintf(buf, sizeof(buf), format, args);
1789         va_end(args);
1790
1791         if (debug_mode) fputs(buf, stdout);
1792         log_window_append(buf, LOG_NORMAL);
1793         statusbar_puts_all(buf);
1794 }
1795
1796 void log_message(const gchar *format, ...)
1797 {
1798         va_list args;
1799         gchar buf[BUFFSIZE];
1800
1801         va_start(args, format);
1802         g_vsnprintf(buf, sizeof(buf), format, args);
1803         va_end(args);
1804
1805         if (debug_mode) g_message("%s", buf);
1806         log_window_append(buf, LOG_MSG);
1807 }
1808
1809 void log_warning(const gchar *format, ...)
1810 {
1811         va_list args;
1812         gchar buf[BUFFSIZE];
1813
1814         va_start(args, format);
1815         g_vsnprintf(buf, sizeof(buf), format, args);
1816         va_end(args);
1817
1818         g_warning("%s", buf);
1819         log_window_append(buf, LOG_WARN);
1820 }
1821
1822 void log_error(const gchar *format, ...)
1823 {
1824         va_list args;
1825         gchar buf[BUFFSIZE];
1826
1827         va_start(args, format);
1828         g_vsnprintf(buf, sizeof(buf), format, args);
1829         va_end(args);
1830
1831         g_warning("%s", buf);
1832         log_window_append(buf, LOG_ERROR);
1833 }