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