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