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