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