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