correct bug in last commit
[claws.git] / src / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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 "socket.h"
48 #include "statusbar.h"
49 #include "logwindow.h"
50
51 #define BUFFSIZE        8192
52
53 extern gboolean debug_mode;
54
55 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data);
56
57 void list_free_strings(GList *list)
58 {
59         list = g_list_first(list);
60
61         while (list != NULL) {
62                 g_free(list->data);
63                 list = list->next;
64         }
65 }
66
67 void slist_free_strings(GSList *list)
68 {
69         while (list != NULL) {
70                 g_free(list->data);
71                 list = list->next;
72         }
73 }
74
75 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
76 {
77         g_free(key);
78 }
79
80 void hash_free_strings(GHashTable *table)
81 {
82         g_hash_table_foreach(table, hash_free_strings_func, NULL);
83 }
84
85 static void hash_free_value_mem_func(gpointer key, gpointer value,
86                                      gpointer data)
87 {
88         g_free(value);
89 }
90
91 void hash_free_value_mem(GHashTable *table)
92 {
93         g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
94 }
95
96 void ptr_array_free_strings(GPtrArray *array)
97 {
98         gint i;
99         gchar *str;
100
101         g_return_if_fail(array != NULL);
102
103         for (i = 0; i < array->len; i++) {
104                 str = g_ptr_array_index(array, i);
105                 g_free(str);
106         }
107 }
108
109 gint to_number(const gchar *nstr)
110 {
111         register const gchar *p;
112
113         if (*nstr == '\0') return -1;
114
115         for (p = nstr; *p != '\0'; p++)
116                 if (!isdigit(*p)) return -1;
117
118         return atoi(nstr);
119 }
120
121 /* convert integer into string,
122    nstr must be not lower than 11 characters length */
123 gchar *itos_buf(gchar *nstr, gint n)
124 {
125         g_snprintf(nstr, 11, "%d", n);
126         return nstr;
127 }
128
129 /* convert integer into string */
130 gchar *itos(gint n)
131 {
132         static gchar nstr[11];
133
134         return itos_buf(nstr, n);
135 }
136
137 gchar *to_human_readable(off_t size)
138 {
139         static gchar str[10];
140
141         if (size < 1024)
142                 g_snprintf(str, sizeof(str), "%dB", (gint)size);
143         else if (size >> 10 < 1024)
144                 g_snprintf(str, sizeof(str), "%.1fKB", (gfloat)size / (1 << 10));
145         else if (size >> 20 < 1024)
146                 g_snprintf(str, sizeof(str), "%.2fMB", (gfloat)size / (1 << 20));
147         else
148                 g_snprintf(str, sizeof(str), "%.2fGB", (gfloat)size / (1 << 30));
149
150         return str;
151 }
152
153 /* strcmp with NULL-checking */
154 gint strcmp2(const gchar *s1, const gchar *s2)
155 {
156         if (s1 == NULL || s2 == NULL)
157                 return -1;
158         else
159                 return strcmp(s1, s2);
160 }
161 /* strstr with NULL-checking */
162 gchar *strstr2(const gchar *s1, const gchar *s2)
163 {
164         if (s1 == NULL || s2 == NULL)
165                 return NULL;
166         else
167                 return strstr(s1, s2);
168 }
169 /* compare paths */
170 gint path_cmp(const gchar *s1, const gchar *s2)
171 {
172         gint len1, len2;
173
174         if (s1 == NULL || s2 == NULL) return -1;
175         if (*s1 == '\0' || *s2 == '\0') return -1;
176
177         len1 = strlen(s1);
178         len2 = strlen(s2);
179
180         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
181         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
182
183         return strncmp(s1, s2, MAX(len1, len2));
184 }
185
186 /* remove trailing return code */
187 gchar *strretchomp(gchar *str)
188 {
189         register gchar *s;
190
191         if (!*str) return str;
192
193         for (s = str + strlen(str) - 1;
194              s >= str && (*s == '\n' || *s == '\r');
195              s--)
196                 *s = '\0';
197
198         return str;
199 }
200
201 /* remove trailing character */
202 gchar *strtailchomp(gchar *str, gchar tail_char)
203 {
204         register gchar *s;
205
206         if (!*str) return str;
207         if (tail_char == '\0') return str;
208
209         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
210                 *s = '\0';
211
212         return str;
213 }
214
215 /* remove CR (carriage return) */
216 gchar *strcrchomp(gchar *str)
217 {
218         register gchar *s;
219
220         if (!*str) return str;
221
222         s = str + strlen(str) - 1;
223         if (*s == '\n' && s > str && *(s - 1) == '\r') {
224                 *(s - 1) = '\n';
225                 *s = '\0';
226         }
227
228         return str;
229 }
230
231 /* Similar to `strstr' but this function ignores the case of both strings.  */
232 gchar *strcasestr(const gchar *haystack, const gchar *needle)
233 {
234         register size_t haystack_len, needle_len;
235
236         haystack_len = strlen(haystack);
237         needle_len   = strlen(needle);
238
239         if (haystack_len < needle_len || needle_len == 0)
240                 return NULL;
241
242         while (haystack_len >= needle_len) {
243                 if (!strncasecmp(haystack, needle, needle_len))
244                         return (gchar *)haystack;
245                 else {
246                         haystack++;
247                         haystack_len--;
248                 }
249         }
250
251         return NULL;
252 }
253
254 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
255 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
256 {
257         register gchar c;
258         gchar *s = dest;
259
260         do {
261                 if (--n == 0) {
262                         *dest = '\0';
263                         return s;
264                 }
265                 c = *src++;
266                 *dest++ = c;
267         } while (c != '\0');
268
269         /* don't do zero fill */
270         return s;
271 }
272
273 #if !HAVE_ISWALNUM
274 int iswalnum(wint_t wc)
275 {
276         return isalnum((int)wc);
277 }
278 #endif
279
280 #if !HAVE_ISWSPACE
281 int iswspace(wint_t wc)
282 {
283         return isspace((int)wc);
284 }
285 #endif
286
287 #if !HAVE_TOWLOWER
288 wint_t towlower(wint_t wc)
289 {
290         if (wc >= L'A' && wc <= L'Z')
291                 return wc + L'a' - L'A';
292
293         return wc;
294 }
295 #endif
296
297 #if !HAVE_WCSLEN
298 size_t wcslen(const wchar_t *s)
299 {
300         size_t len = 0;
301
302         while (*s != L'\0')
303                 ++len, ++s;
304
305         return len;
306 }
307 #endif
308
309 #if !HAVE_WCSCPY
310 /* Copy SRC to DEST.  */
311 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
312 {
313         wint_t c;
314         wchar_t *s = dest;
315
316         do {
317                 c = *src++;
318                 *dest++ = c;
319         } while (c != L'\0');
320
321         return s;
322 }
323 #endif
324
325 #if !HAVE_WCSNCPY
326 /* Copy no more than N wide-characters of SRC to DEST.  */
327 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
328 {
329         wint_t c;
330         wchar_t *s = dest;
331
332         do {
333                 c = *src++;
334                 *dest++ = c;
335                 if (--n == 0)
336                         return s;
337         } while (c != L'\0');
338
339         /* zero fill */
340         do
341                 *dest++ = L'\0';
342         while (--n > 0);
343
344         return s;
345 }
346 #endif
347
348 /* Duplicate S, returning an identical malloc'd string. */
349 wchar_t *wcsdup(const wchar_t *s)
350 {
351         wchar_t *new_str;
352
353         if (s) {
354                 new_str = g_new(wchar_t, wcslen(s) + 1);
355                 wcscpy(new_str, s);
356         } else
357                 new_str = NULL;
358
359         return new_str;
360 }
361
362 /* Duplicate no more than N wide-characters of S,
363    returning an identical malloc'd string. */
364 wchar_t *wcsndup(const wchar_t *s, size_t n)
365 {
366         wchar_t *new_str;
367
368         if (s) {
369                 new_str = g_new(wchar_t, n + 1);
370                 wcsncpy(new_str, s, n);
371                 new_str[n] = (wchar_t)0;
372         } else
373                 new_str = NULL;
374
375         return new_str;
376 }
377
378 wchar_t *strdup_mbstowcs(const gchar *s)
379 {
380         wchar_t *new_str;
381
382         if (s) {
383                 new_str = g_new(wchar_t, strlen(s) + 1);
384                 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
385                         g_free(new_str);
386                         new_str = NULL;
387                 } else
388                         new_str = g_realloc(new_str,
389                                             sizeof(wchar_t) * (wcslen(new_str) + 1));
390         } else
391                 new_str = NULL;
392
393         return new_str;
394 }
395
396 gchar *strdup_wcstombs(const wchar_t *s)
397 {
398         gchar *new_str;
399         size_t len;
400
401         if (s) {
402                 len = wcslen(s) * MB_CUR_MAX + 1;
403                 new_str = g_new(gchar, len);
404                 if (wcstombs(new_str, s, len) < 0) {
405                         g_free(new_str);
406                         new_str = NULL;
407                 } else
408                         new_str = g_realloc(new_str, strlen(new_str) + 1);
409         } else
410                 new_str = NULL;
411
412         return new_str;
413 }
414
415 /* Compare S1 and S2, ignoring case.  */
416 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
417 {
418         wint_t c1;
419         wint_t c2;
420
421         while (n--) {
422                 c1 = towlower(*s1++);
423                 c2 = towlower(*s2++);
424                 if (c1 != c2)
425                         return c1 - c2;
426                 else if (c1 == 0 && c2 == 0)
427                         break;
428         }
429
430         return 0;
431 }
432
433 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
434 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
435 {
436         register size_t haystack_len, needle_len;
437
438         haystack_len = wcslen(haystack);
439         needle_len   = wcslen(needle);
440
441         if (haystack_len < needle_len || needle_len == 0)
442                 return NULL;
443
444         while (haystack_len >= needle_len) {
445                 if (!wcsncasecmp(haystack, needle, needle_len))
446                         return (wchar_t *)haystack;
447                 else {
448                         haystack++;
449                         haystack_len--;
450                 }
451         }
452
453         return NULL;
454 }
455
456 /* Examine if next block is non-ASCII string */
457 gboolean is_next_nonascii(const wchar_t *s)
458 {
459         const wchar_t *wp;
460
461         /* skip head space */
462         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
463                 ;
464         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
465                 if (*wp > 127)
466                         return TRUE;
467         }
468
469         return FALSE;
470 }
471
472 /* Examine if next block is multi-byte string */
473 gboolean is_next_mbs(const wchar_t *s)
474 {
475         gint mbl;
476         const wchar_t *wp;
477         gchar tmp[MB_LEN_MAX];
478
479         /* skip head space */
480         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
481                 ;
482         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
483                 mbl = wctomb(tmp, *wp);
484                 if (mbl > 1)
485                         return TRUE;
486         }
487
488         return FALSE;
489 }
490
491 wchar_t *find_wspace(const wchar_t *s)
492 {
493         const wchar_t *wp;
494
495         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
496                 ;
497         for (; *wp != (wchar_t)0; wp++) {
498                 if (iswspace(*wp))
499                         return (wchar_t *)wp;
500         }
501
502         return NULL;
503 }
504
505 /* compare subjects */
506 gint subject_compare(const gchar *s1, const gchar *s2)
507 {
508         gchar *str1, *str2;
509
510         if (!s1 || !s2) return -1;
511         if (!*s1 || !*s2) return -1;
512
513         Xstrdup_a(str1, s1, return -1);
514         Xstrdup_a(str2, s2, return -1);
515
516         trim_subject(str1);
517         trim_subject(str2);
518
519         if (!*str1 || !*str2) return -1;
520
521         return strcmp(str1, str2);
522 }
523
524 void trim_subject(gchar *str)
525 {
526         gchar *srcp;
527
528         eliminate_parenthesis(str, '[', ']');
529         eliminate_parenthesis(str, '(', ')');
530         g_strstrip(str);
531
532         while (!strncasecmp(str, "Re:", 3)) {
533                 srcp = str + 3;
534                 while (isspace(*srcp)) srcp++;
535                 memmove(str, srcp, strlen(srcp) + 1);
536         }
537 }
538
539 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
540 {
541         register gchar *srcp, *destp;
542         gint in_brace;
543
544         srcp = destp = str;
545
546         while ((destp = strchr(destp, op))) {
547                 in_brace = 1;
548                 srcp = destp + 1;
549                 while (*srcp) {
550                         if (*srcp == op)
551                                 in_brace++;
552                         else if (*srcp == cl)
553                                 in_brace--;
554                         srcp++;
555                         if (in_brace == 0)
556                                 break;
557                 }
558                 while (isspace(*srcp)) srcp++;
559                 memmove(destp, srcp, strlen(srcp) + 1);
560         }
561 }
562
563 void extract_parenthesis(gchar *str, gchar op, gchar cl)
564 {
565         register gchar *srcp, *destp;
566         gint in_brace;
567
568         srcp = destp = str;
569
570         while ((srcp = strchr(destp, op))) {
571                 if (destp > str)
572                         *destp++ = ' ';
573                 memmove(destp, srcp + 1, strlen(srcp));
574                 in_brace = 1;
575                 while(*destp) {
576                         if (*destp == op)
577                                 in_brace++;
578                         else if (*destp == cl)
579                                 in_brace--;
580
581                         if (in_brace == 0)
582                                 break;
583
584                         destp++;
585                 }
586         }
587         *destp = '\0';
588 }
589
590 void extract_one_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
591                                              gchar op, gchar cl)
592 {
593         register gchar *srcp, *destp;
594         gint in_brace;
595         gboolean in_quote = FALSE;
596
597         srcp = destp = str;
598
599         if ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
600                 memmove(destp, srcp + 1, strlen(srcp));
601                 in_brace = 1;
602                 while(*destp) {
603                         if (*destp == op && !in_quote)
604                                 in_brace++;
605                         else if (*destp == cl && !in_quote)
606                                 in_brace--;
607                         else if (*destp == quote_chr)
608                                 in_quote ^= TRUE;
609
610                         if (in_brace == 0)
611                                 break;
612
613                         destp++;
614                 }
615         }
616         *destp = '\0';
617 }
618
619 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
620                                          gchar op, gchar cl)
621 {
622         register gchar *srcp, *destp;
623         gint in_brace;
624         gboolean in_quote = FALSE;
625
626         srcp = destp = str;
627
628         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
629                 if (destp > str)
630                         *destp++ = ' ';
631                 memmove(destp, srcp + 1, strlen(srcp));
632                 in_brace = 1;
633                 while(*destp) {
634                         if (*destp == op && !in_quote)
635                                 in_brace++;
636                         else if (*destp == cl && !in_quote)
637                                 in_brace--;
638                         else if (*destp == quote_chr)
639                                 in_quote ^= TRUE;
640
641                         if (in_brace == 0)
642                                 break;
643
644                         destp++;
645                 }
646         }
647         *destp = '\0';
648 }
649
650 void eliminate_quote(gchar *str, gchar quote_chr)
651 {
652         register gchar *srcp, *destp;
653
654         srcp = destp = str;
655
656         while ((destp = strchr(destp, quote_chr))) {
657                 if ((srcp = strchr(destp + 1, quote_chr))) {
658                         srcp++;
659                         while (isspace(*srcp)) srcp++;
660                         memmove(destp, srcp, strlen(srcp) + 1);
661                 } else {
662                         *destp = '\0';
663                         break;
664                 }
665         }
666 }
667
668 void extract_quote(gchar *str, gchar quote_chr)
669 {
670         register gchar *p;
671
672         if ((str = strchr(str, quote_chr))) {
673                 p = str;
674                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
675                         memmove(p - 1, p, strlen(p) + 1);
676                         p--;
677                 }
678                 if(p) {
679                         *p = '\0';
680                         memmove(str, str + 1, p - str);
681                 }
682         }
683 }
684
685 void eliminate_address_comment(gchar *str)
686 {
687         register gchar *srcp, *destp;
688         gint in_brace;
689
690         srcp = destp = str;
691
692         while ((destp = strchr(destp, '"'))) {
693                 if ((srcp = strchr(destp + 1, '"'))) {
694                         srcp++;
695                         if (*srcp == '@') {
696                                 destp = srcp + 1;
697                         } else {
698                                 while (isspace(*srcp)) srcp++;
699                                 memmove(destp, srcp, strlen(srcp) + 1);
700                         }
701                 } else {
702                         *destp = '\0';
703                         break;
704                 }
705         }
706
707         srcp = destp = str;
708
709         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
710                 in_brace = 1;
711                 srcp = destp + 1;
712                 while (*srcp) {
713                         if (*srcp == '(')
714                                 in_brace++;
715                         else if (*srcp == ')')
716                                 in_brace--;
717                         srcp++;
718                         if (in_brace == 0)
719                                 break;
720                 }
721                 while (isspace(*srcp)) srcp++;
722                 memmove(destp, srcp, strlen(srcp) + 1);
723         }
724 }
725
726 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
727 {
728         gboolean in_quote = FALSE;
729
730         while (*str) {
731                 if (*str == c && !in_quote)
732                         return (gchar *)str;
733                 if (*str == quote_chr)
734                         in_quote ^= TRUE;
735                 str++;
736         }
737
738         return NULL;
739 }
740
741 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
742 {
743         gboolean in_quote = FALSE;
744         const gchar *p;
745
746         p = str + strlen(str) - 1;
747         while (p >= str) {
748                 if (*p == c && !in_quote)
749                         return (gchar *)p;
750                 if (*p == quote_chr)
751                         in_quote ^= TRUE;
752                 p--;
753         }
754
755         return NULL;
756 }
757
758 void extract_address(gchar *str)
759 {
760         eliminate_address_comment(str);
761         if (strchr_with_skip_quote(str, '"', '<'))
762                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
763         g_strstrip(str);
764 }
765
766 GSList *address_list_append(GSList *addr_list, const gchar *str)
767 {
768         gchar *work;
769         gchar *workp;
770
771         if (!str) return addr_list;
772
773         Xstrdup_a(work, str, return addr_list);
774
775         eliminate_address_comment(work);
776         workp = work;
777
778         while (workp && *workp) {
779                 gchar *p, *next;
780
781                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
782                         *p = '\0';
783                         next = p + 1;
784                 } else
785                         next = NULL;
786
787                 if (strchr_with_skip_quote(workp, '"', '<'))
788                         extract_parenthesis_with_skip_quote
789                                 (workp, '"', '<', '>');
790
791                 g_strstrip(workp);
792                 if (*workp)
793                         addr_list = g_slist_append(addr_list, g_strdup(workp));
794
795                 workp = next;
796         }
797
798         return addr_list;
799 }
800
801 GSList *references_list_append(GSList *msgid_list, const gchar *str)
802 {
803         const gchar *strp;
804
805         if (!str) return msgid_list;
806         strp = str;
807
808         while (strp && *strp) {
809                 const gchar *start, *end;
810                 gchar *msgid;
811
812                 if ((start = strchr(strp, '<')) != NULL) {
813                         end = strchr(start + 1, '>');
814                         if (!end) break;
815                 } else
816                         break;
817
818                 msgid = g_strndup(start + 1, end - start - 1);
819                 g_strstrip(msgid);
820                 if (*msgid)
821                         msgid_list = g_slist_append(msgid_list, msgid);
822                 else
823                         g_free(msgid);
824
825                 strp = end + 1;
826         }
827
828         return msgid_list;
829 }
830
831 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
832 {
833         gchar *work;
834         gchar *workp;
835
836         if (!str) return group_list;
837
838         Xstrdup_a(work, str, return group_list);
839
840         workp = work;
841
842         while (workp && *workp) {
843                 gchar *p, *next;
844
845                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
846                         *p = '\0';
847                         next = p + 1;
848                 } else
849                         next = NULL;
850
851                 g_strstrip(workp);
852                 if (*workp)
853                         group_list = g_slist_append(group_list,
854                                                     g_strdup(workp));
855
856                 workp = next;
857         }
858
859         return group_list;
860 }
861
862 GList *add_history(GList *list, const gchar *str)
863 {
864         GList *old;
865
866         g_return_val_if_fail(str != NULL, list);
867
868         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
869         if (old) {
870                 g_free(old->data);
871                 list = g_list_remove(list, old->data);
872         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
873                 GList *last;
874
875                 last = g_list_last(list);
876                 if (last) {
877                         g_free(last->data);
878                         g_list_remove(list, last->data);
879                 }
880         }
881
882         list = g_list_prepend(list, g_strdup(str));
883
884         return list;
885 }
886
887 void remove_return(gchar *str)
888 {
889         register gchar *p = str;
890
891         while (*p) {
892                 if (*p == '\n' || *p == '\r')
893                         memmove(p, p + 1, strlen(p));
894                 else
895                         p++;
896         }
897 }
898
899 void remove_space(gchar *str)
900 {
901         register gchar *p = str;
902         register gint spc;
903
904         while (*p) {
905                 spc = 0;
906                 while (isspace(*(p + spc)))
907                         spc++;
908                 if (spc)
909                         memmove(p, p + spc, strlen(p + spc) + 1);
910                 else
911                         p++;
912         }
913 }
914
915 void unfold_line(gchar *str)
916 {
917         register gchar *p = str;
918         register gint spc;
919
920         while (*p) {
921                 if (*p == '\n' || *p == '\r') {
922                         *p++ = ' ';
923                         spc = 0;
924                         while (isspace(*(p + spc)))
925                                 spc++;
926                         if (spc)
927                                 memmove(p, p + spc, strlen(p + spc) + 1);
928                 } else
929                         p++;
930         }
931 }
932
933 void subst_char(gchar *str, gchar orig, gchar subst)
934 {
935         register gchar *p = str;
936
937         while (*p) {
938                 if (*p == orig)
939                         *p = subst;
940                 p++;
941         }
942 }
943
944 void subst_chars(gchar *str, gchar *orig, gchar subst)
945 {
946         register gchar *p = str;
947
948         while (*p) {
949                 if (strchr(orig, *p) != NULL)
950                         *p = subst;
951                 p++;
952         }
953 }
954
955 void subst_for_filename(gchar *str)
956 {
957         subst_chars(str, " \t\r\n\"/\\", '_');
958 }
959
960 gboolean is_header_line(const gchar *str)
961 {
962         if (str[0] == ':') return FALSE;
963
964         while (*str != '\0' && *str != ' ') {
965                 if (*str == ':')
966                         return TRUE;
967                 str++;
968         }
969
970         return FALSE;
971 }
972
973 gboolean is_ascii_str(const guchar *str)
974 {
975         while (*str != '\0') {
976                 if (*str != '\t' && *str != ' ' &&
977                     *str != '\r' && *str != '\n' &&
978                     (*str < 32 || *str >= 127))
979                         return FALSE;
980                 str++;
981         }
982
983         return TRUE;
984 }
985
986 gint get_quote_level(const gchar *str, const gchar *quote_chars)
987 {
988         const gchar *first_pos;
989         const gchar *last_pos;
990         const gchar *p = str;
991         gint quote_level = -1;
992
993         /* speed up line processing by only searching to the last '>' */
994         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
995                 /* skip a line if it contains a '<' before the initial '>' */
996                 if (memchr(str, '<', first_pos - str) != NULL)
997                         return -1;
998                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
999         } else
1000                 return -1;
1001
1002         while (p <= last_pos) {
1003                 while (p < last_pos) {
1004                         if (isspace(*p))
1005                                 p++;
1006                         else
1007                                 break;
1008                 }
1009
1010                 if (strchr(quote_chars, *p))
1011                         quote_level++;
1012                 else if (*p != '-' && !isspace(*p) && p <= last_pos) {
1013                         /* any characters are allowed except '-' and space */
1014                         while (*p != '-' 
1015                                && !strchr(quote_chars, *p) 
1016                                && !isspace(*p) 
1017                                && p < last_pos)
1018                                 p++;
1019                         if (strchr(quote_chars, *p))
1020                                 quote_level++;
1021                         else
1022                                 break;
1023                 }
1024
1025                 p++;
1026         }
1027
1028         return quote_level;
1029 }
1030
1031 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars) 
1032 {
1033         gchar * position = NULL;
1034         gchar * tmp_pos = NULL;
1035         int i;
1036
1037         if (quote_chars == NULL)
1038                 return FALSE;
1039         
1040         for (i = 0; i < strlen(quote_chars); i++) {
1041                 tmp_pos = strchr (str,  quote_chars[i]);
1042                 if(position == NULL 
1043                    || (tmp_pos != NULL && position >= tmp_pos) )
1044                         position = tmp_pos;
1045         }
1046         return position; 
1047 }
1048
1049 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars) 
1050 {
1051         gchar * position = NULL;
1052         gchar * tmp_pos = NULL;
1053         int i;
1054
1055         if (quote_chars == NULL)
1056                 return FALSE;
1057         
1058         for (i = 0; i < strlen(quote_chars); i++) {
1059                 tmp_pos = strrchr (str, quote_chars[i]);
1060                 if(position == NULL 
1061                    || (tmp_pos != NULL && position <= tmp_pos) )
1062                         position = tmp_pos;
1063         }
1064         return position; 
1065 }
1066
1067 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1068 {
1069         register guint haystack_len, needle_len;
1070         gboolean in_squote = FALSE, in_dquote = FALSE;
1071
1072         haystack_len = strlen(haystack);
1073         needle_len   = strlen(needle);
1074
1075         if (haystack_len < needle_len || needle_len == 0)
1076                 return NULL;
1077
1078         while (haystack_len >= needle_len) {
1079                 if (!in_squote && !in_dquote &&
1080                     !strncmp(haystack, needle, needle_len))
1081                         return (gchar *)haystack;
1082
1083                 /* 'foo"bar"' -> foo"bar"
1084                    "foo'bar'" -> foo'bar' */
1085                 if (*haystack == '\'') {
1086                         if (in_squote)
1087                                 in_squote = FALSE;
1088                         else if (!in_dquote)
1089                                 in_squote = TRUE;
1090                 } else if (*haystack == '\"') {
1091                         if (in_dquote)
1092                                 in_dquote = FALSE;
1093                         else if (!in_squote)
1094                                 in_dquote = TRUE;
1095                 }
1096
1097                 haystack++;
1098                 haystack_len--;
1099         }
1100
1101         return NULL;
1102 }
1103
1104 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1105 {
1106         const gchar *p;
1107         gchar quote_chr = '"';
1108         gint in_brace;
1109         gboolean in_quote = FALSE;
1110
1111         p = str;
1112
1113         if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1114                 p++;
1115                 in_brace = 1;
1116                 while (*p) {
1117                         if (*p == op && !in_quote)
1118                                 in_brace++;
1119                         else if (*p == cl && !in_quote)
1120                                 in_brace--;
1121                         else if (*p == quote_chr)
1122                                 in_quote ^= TRUE;
1123
1124                         if (in_brace == 0)
1125                                 return (gchar *)p;
1126
1127                         p++;
1128                 }
1129         }
1130
1131         return NULL;
1132 }
1133
1134 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1135                              gint max_tokens)
1136 {
1137         GSList *string_list = NULL, *slist;
1138         gchar **str_array;
1139         const gchar *s_op, *s_cl;
1140         guint i, n = 1;
1141
1142         g_return_val_if_fail(str != NULL, NULL);
1143
1144         if (max_tokens < 1)
1145                 max_tokens = G_MAXINT;
1146
1147         s_op = strchr_with_skip_quote(str, '"', op);
1148         if (!s_op) return NULL;
1149         str = s_op;
1150         s_cl = strchr_parenthesis_close(str, op, cl);
1151         if (s_cl) {
1152                 do {
1153                         guint len;
1154                         gchar *new_string;
1155
1156                         str++;
1157                         len = s_cl - str;
1158                         new_string = g_new(gchar, len + 1);
1159                         strncpy(new_string, str, len);
1160                         new_string[len] = 0;
1161                         string_list = g_slist_prepend(string_list, new_string);
1162                         n++;
1163                         str = s_cl + 1;
1164
1165                         while (*str && isspace(*str)) str++;
1166                         if (*str != op) {
1167                                 string_list = g_slist_prepend(string_list,
1168                                                               g_strdup(""));
1169                                 n++;
1170                                 s_op = strchr_with_skip_quote(str, '"', op);
1171                                 if (!--max_tokens || !s_op) break;
1172                                 str = s_op;
1173                         } else
1174                                 s_op = str;
1175                         s_cl = strchr_parenthesis_close(str, op, cl);
1176                 } while (--max_tokens && s_cl);
1177         }
1178
1179         str_array = g_new(gchar*, n);
1180
1181         i = n - 1;
1182
1183         str_array[i--] = NULL;
1184         for (slist = string_list; slist; slist = slist->next)
1185                 str_array[i--] = slist->data;
1186
1187         g_slist_free(string_list);
1188
1189         return str_array;
1190 }
1191
1192 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1193                             gint max_tokens)
1194 {
1195         GSList *string_list = NULL, *slist;
1196         gchar **str_array, *s, *new_str;
1197         guint i, n = 1, len;
1198
1199         g_return_val_if_fail(str != NULL, NULL);
1200         g_return_val_if_fail(delim != NULL, NULL);
1201
1202         if (max_tokens < 1)
1203                 max_tokens = G_MAXINT;
1204
1205         s = strstr_with_skip_quote(str, delim);
1206         if (s) {
1207                 guint delimiter_len = strlen(delim);
1208
1209                 do {
1210                         len = s - str;
1211                         new_str = g_strndup(str, len);
1212
1213                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1214                                 if (new_str[len - 1] == new_str[0]) {
1215                                         new_str[len - 1] = '\0';
1216                                         memmove(new_str, new_str + 1, len - 1);
1217                                 }
1218                         }
1219                         string_list = g_slist_prepend(string_list, new_str);
1220                         n++;
1221                         str = s + delimiter_len;
1222                         s = strstr_with_skip_quote(str, delim);
1223                 } while (--max_tokens && s);
1224         }
1225
1226         if (*str) {
1227                 new_str = g_strdup(str);
1228                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1229                         len = strlen(str);
1230                         if (new_str[len - 1] == new_str[0]) {
1231                                 new_str[len - 1] = '\0';
1232                                 memmove(new_str, new_str + 1, len - 1);
1233                         }
1234                 }
1235                 string_list = g_slist_prepend(string_list, new_str);
1236                 n++;
1237         }
1238
1239         str_array = g_new(gchar*, n);
1240
1241         i = n - 1;
1242
1243         str_array[i--] = NULL;
1244         for (slist = string_list; slist; slist = slist->next)
1245                 str_array[i--] = slist->data;
1246
1247         g_slist_free(string_list);
1248
1249         return str_array;
1250 }
1251
1252 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1253 {
1254         gchar *abbrev_group;
1255         gchar *ap;
1256         const gchar *p = group;
1257         gint  count = 0;
1258
1259         abbrev_group = ap = g_malloc(strlen(group) + 1);
1260
1261         while (*p) {
1262                 while (*p == '.')
1263                         *ap++ = *p++;
1264
1265                 if ((strlen( p) + count) > len && strchr(p, '.')) {
1266                         *ap++ = *p++;
1267                         while (*p != '.') p++;
1268                 } else {
1269                         strcpy( ap, p);
1270                         return abbrev_group;
1271                 }
1272                 count = count + 2;
1273         }
1274
1275         *ap = '\0';
1276         return abbrev_group;
1277 }
1278
1279 gchar *trim_string(const gchar *str, gint len)
1280 {
1281         const gchar *p = str;
1282         gint mb_len;
1283         gchar *new_str;
1284         gint new_len = 0;
1285
1286         if (!str) return NULL;
1287         if (strlen(str) <= len)
1288                 return g_strdup(str);
1289
1290         while (*p != '\0') {
1291                 mb_len = mblen(p, MB_LEN_MAX);
1292                 if (mb_len == 0)
1293                         break;
1294                 else if (mb_len < 0)
1295                         return g_strdup(str);
1296                 else if (new_len + mb_len > len)
1297                         break;
1298                 else
1299                         new_len += mb_len;
1300                 p += mb_len;
1301         }
1302
1303         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1304         return g_strconcat(new_str, "...", NULL);
1305 }
1306
1307 GList *uri_list_extract_filenames(const gchar *uri_list)
1308 {
1309         GList *result = NULL;
1310         const gchar *p, *q;
1311         gchar *file;
1312
1313         p = uri_list;
1314
1315         while (p) {
1316                 if (*p != '#') {
1317                         while (isspace(*p)) p++;
1318                         if (!strncmp(p, "file:", 5)) {
1319                                 p += 5;
1320                                 q = p;
1321                                 while (*q && *q != '\n' && *q != '\r') q++;
1322
1323                                 if (q > p) {
1324                                         q--;
1325                                         while (q > p && isspace(*q)) q--;
1326                                         file = g_malloc(q - p + 2);
1327                                         strncpy(file, p, q - p + 1);
1328                                         file[q - p + 1] = '\0';
1329                                         result = g_list_append(result,file);
1330                                 }
1331                         }
1332                 }
1333                 p = strchr(p, '\n');
1334                 if (p) p++;
1335         }
1336
1337         return result;
1338 }
1339
1340 #define HEX_TO_INT(val, hex) \
1341 { \
1342         gchar c = hex; \
1343  \
1344         if ('0' <= c && c <= '9') { \
1345                 val = c - '0'; \
1346         } else if ('a' <= c && c <= 'f') { \
1347                 val = c - 'a' + 10; \
1348         } else if ('A' <= c && c <= 'F') { \
1349                 val = c - 'A' + 10; \
1350         } else { \
1351                 val = 0; \
1352         } \
1353 }
1354
1355 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1356                      gchar **subject, gchar **body)
1357 {
1358         gchar *tmp_mailto;
1359         gchar *p;
1360
1361         Xstrdup_a(tmp_mailto, mailto, return -1);
1362
1363         if (!strncmp(tmp_mailto, "mailto:", 7))
1364                 tmp_mailto += 7;
1365
1366         p = strchr(tmp_mailto, '?');
1367         if (p) {
1368                 *p = '\0';
1369                 p++;
1370         }
1371
1372         if (to && !*to)
1373                 *to = g_strdup(tmp_mailto);
1374
1375         while (p) {
1376                 gchar *field, *value;
1377
1378                 field = p;
1379
1380                 p = strchr(p, '=');
1381                 if (!p) break;
1382                 *p = '\0';
1383                 p++;
1384
1385                 value = p;
1386
1387                 p = strchr(p, '&');
1388                 if (p) {
1389                         *p = '\0';
1390                         p++;
1391                 }
1392
1393                 if (*value == '\0') continue;
1394
1395                 if (cc && !*cc && !g_strcasecmp(field, "cc")) {
1396                         *cc = g_strdup(value);
1397                 } else if (bcc && !*bcc && !g_strcasecmp(field, "bcc")) {
1398                         *bcc = g_strdup(value);
1399                 } else if (subject && !*subject &&
1400                            !g_strcasecmp(field, "subject")) {
1401                         *subject = g_malloc(strlen(value) + 1);
1402                         decode_uri(*subject, value);
1403                 } else if (body && !*body && !g_strcasecmp(field, "body")) {
1404                         *body = g_malloc(strlen(value) + 1);
1405                         decode_uri(*body, value);
1406                 }
1407         }
1408
1409         return 0;
1410 }
1411
1412 /*
1413  * We need this wrapper around g_get_home_dir(), so that
1414  * we can fix some Windoze things here.  Should be done in glibc of course
1415  * but as long as we are not able to do our own extensions to glibc, we do
1416  * it here.
1417  */
1418 gchar *get_home_dir(void)
1419 {
1420 #if HAVE_DOSISH_SYSTEM
1421     static gchar *home_dir;
1422
1423     if (!home_dir) {
1424         home_dir = read_w32_registry_string(NULL,
1425                                             "Software\\Sylpheed", "HomeDir" );
1426         if (!home_dir || !*home_dir) {
1427             if (getenv ("HOMEDRIVE") && getenv("HOMEPATH")) {
1428                 const char *s = g_get_home_dir();
1429                 if (s && *s)
1430                     home_dir = g_strdup (s);
1431             }
1432             if (!home_dir || !*home_dir) 
1433                 home_dir = g_strdup ("c:\\sylpheed");
1434         }
1435         debug_print("initialized home_dir to `%s'\n", home_dir);
1436     }
1437     return home_dir;
1438 #else /* standard glib */
1439     return g_get_home_dir();
1440 #endif
1441 }
1442
1443 gchar *get_rc_dir(void)
1444 {
1445         static gchar *rc_dir = NULL;
1446
1447         if (!rc_dir)
1448                 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1449                                      RC_DIR, NULL);
1450
1451         return rc_dir;
1452 }
1453
1454 gchar *get_news_cache_dir(void)
1455 {
1456         static gchar *news_cache_dir = NULL;
1457
1458         if (!news_cache_dir)
1459                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1460                                              NEWS_CACHE_DIR, NULL);
1461
1462         return news_cache_dir;
1463 }
1464
1465 gchar *get_imap_cache_dir(void)
1466 {
1467         static gchar *imap_cache_dir = NULL;
1468
1469         if (!imap_cache_dir)
1470                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1471                                              IMAP_CACHE_DIR, NULL);
1472
1473         return imap_cache_dir;
1474 }
1475
1476 gchar *get_mbox_cache_dir(void)
1477 {
1478         static gchar *mbox_cache_dir = NULL;
1479
1480         if (!mbox_cache_dir)
1481                 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1482                                              MBOX_CACHE_DIR, NULL);
1483
1484         return mbox_cache_dir;
1485 }
1486
1487 gchar *get_mime_tmp_dir(void)
1488 {
1489         static gchar *mime_tmp_dir = NULL;
1490
1491         if (!mime_tmp_dir)
1492                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1493                                            MIME_TMP_DIR, NULL);
1494
1495         return mime_tmp_dir;
1496 }
1497
1498 gchar *get_template_dir(void)
1499 {
1500         static gchar *template_dir = NULL;
1501
1502         if (!template_dir)
1503                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1504                                            TEMPLATE_DIR, NULL);
1505
1506         return template_dir;
1507 }
1508
1509 gchar *get_header_cache_dir(void)
1510 {
1511         static gchar *header_dir = NULL;
1512
1513         if (!header_dir)
1514                 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1515                                          HEADER_CACHE_DIR, NULL);
1516
1517         return header_dir;
1518 }
1519
1520 gchar *get_tmp_dir(void)
1521 {
1522         static gchar *tmp_dir = NULL;
1523
1524         if (!tmp_dir)
1525                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1526                                       TMP_DIR, NULL);
1527
1528         return tmp_dir;
1529 }
1530
1531 gchar *get_tmp_file(void)
1532 {
1533         gchar *tmp_file;
1534         static guint32 id = 0;
1535
1536         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1537                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
1538
1539         return tmp_file;
1540 }
1541
1542 gchar *get_domain_name(void)
1543 {
1544         static gchar *domain_name = NULL;
1545
1546         if (!domain_name) {
1547                 gchar buf[128] = "";
1548                 struct hostent *hp;
1549
1550                 if (gethostname(buf, sizeof(buf)) < 0) {
1551                         perror("gethostname");
1552                         domain_name = "unknown";
1553                 } else {
1554                         buf[sizeof(buf) - 1] = '\0';
1555                         if ((hp = my_gethostbyname(buf)) == NULL) {
1556                                 perror("gethostbyname");
1557                                 domain_name = g_strdup(buf);
1558                         } else {
1559                                 domain_name = g_strdup(hp->h_name);
1560                         }
1561                 }
1562
1563                 debug_print("domain name = %s\n", domain_name);
1564         }
1565
1566         return domain_name;
1567 }
1568
1569 off_t get_file_size(const gchar *file)
1570 {
1571         struct stat s;
1572
1573         if (stat(file, &s) < 0) {
1574                 FILE_OP_ERROR(file, "stat");
1575                 return -1;
1576         }
1577
1578         return s.st_size;
1579 }
1580
1581 off_t get_file_size_as_crlf(const gchar *file)
1582 {
1583         FILE *fp;
1584         off_t size = 0;
1585         gchar buf[BUFFSIZE];
1586
1587         if ((fp = fopen(file, "rb")) == NULL) {
1588                 FILE_OP_ERROR(file, "fopen");
1589                 return -1;
1590         }
1591
1592         while (fgets(buf, sizeof(buf), fp) != NULL) {
1593                 strretchomp(buf);
1594                 size += strlen(buf) + 2;
1595         }
1596
1597         if (ferror(fp)) {
1598                 FILE_OP_ERROR(file, "fgets");
1599                 size = -1;
1600         }
1601
1602         fclose(fp);
1603
1604         return size;
1605 }
1606
1607 off_t get_left_file_size(FILE *fp)
1608 {
1609         glong pos;
1610         glong end;
1611         off_t size;
1612
1613         if ((pos = ftell(fp)) < 0) {
1614                 perror("ftell");
1615                 return -1;
1616         }
1617         if (fseek(fp, 0L, SEEK_END) < 0) {
1618                 perror("fseek");
1619                 return -1;
1620         }
1621         if ((end = ftell(fp)) < 0) {
1622                 perror("fseek");
1623                 return -1;
1624         }
1625         size = end - pos;
1626         if (fseek(fp, pos, SEEK_SET) < 0) {
1627                 perror("fseek");
1628                 return -1;
1629         }
1630
1631         return size;
1632 }
1633
1634 gboolean file_exist(const gchar *file, gboolean allow_fifo)
1635 {
1636         struct stat s;
1637
1638         if (file == NULL)
1639                 return FALSE;
1640
1641         if (stat(file, &s) < 0) {
1642                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1643                 return FALSE;
1644         }
1645
1646         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1647                 return TRUE;
1648
1649         return FALSE;
1650 }
1651
1652 gboolean is_dir_exist(const gchar *dir)
1653 {
1654         struct stat s;
1655
1656         if (dir == NULL)
1657                 return FALSE;
1658
1659         if (stat(dir, &s) < 0) {
1660                 if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1661                 return FALSE;
1662         }
1663
1664         if (S_ISDIR(s.st_mode))
1665                 return TRUE;
1666
1667         return FALSE;
1668 }
1669
1670 gboolean is_file_entry_exist(const gchar *file)
1671 {
1672         struct stat s;
1673
1674         if (file == NULL)
1675                 return FALSE;
1676
1677         if (stat(file, &s) < 0) {
1678                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1679                 return FALSE;
1680         }
1681
1682         return TRUE;
1683 }
1684
1685 gint change_dir(const gchar *dir)
1686 {
1687         gchar *prevdir = NULL;
1688
1689         if (debug_mode)
1690                 prevdir = g_get_current_dir();
1691
1692         if (chdir(dir) < 0) {
1693                 FILE_OP_ERROR(dir, "chdir");
1694                 if (debug_mode) g_free(prevdir);
1695                 return -1;
1696         } else if (debug_mode) {
1697                 gchar *cwd;
1698
1699                 cwd = g_get_current_dir();
1700                 if (strcmp(prevdir, cwd) != 0)
1701                         g_print("current dir: %s\n", cwd);
1702                 g_free(cwd);
1703                 g_free(prevdir);
1704         }
1705
1706         return 0;
1707 }
1708
1709 gint make_dir(const gchar *dir)
1710 {
1711         if (mkdir(dir, S_IRWXU) < 0) {
1712                 FILE_OP_ERROR(dir, "mkdir");
1713                 return -1;
1714         }
1715         if (chmod(dir, S_IRWXU) < 0)
1716                 FILE_OP_ERROR(dir, "chmod");
1717
1718         return 0;
1719 }
1720
1721 gint make_dir_hier(const gchar *dir)
1722 {
1723         gchar *parent_dir;
1724         const gchar *p;
1725
1726         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1727                 parent_dir = g_strndup(dir, p - dir);
1728                 if (*parent_dir != '\0') {
1729                         if (!is_dir_exist(parent_dir)) {
1730                                 if (make_dir(parent_dir) < 0) {
1731                                         g_free(parent_dir);
1732                                         return -1;
1733                                 }
1734                         }
1735                 }
1736                 g_free(parent_dir);
1737         }
1738
1739         if (!is_dir_exist(dir)) {
1740                 if (make_dir(dir) < 0)
1741                         return -1;
1742         }
1743
1744         return 0;
1745 }
1746
1747 gint remove_all_files(const gchar *dir)
1748 {
1749         DIR *dp;
1750         struct dirent *d;
1751         gchar *prev_dir;
1752
1753         prev_dir = g_get_current_dir();
1754
1755         if (chdir(dir) < 0) {
1756                 FILE_OP_ERROR(dir, "chdir");
1757                 g_free(prev_dir);
1758                 return -1;
1759         }
1760
1761         if ((dp = opendir(".")) == NULL) {
1762                 FILE_OP_ERROR(dir, "opendir");
1763                 g_free(prev_dir);
1764                 return -1;
1765         }
1766
1767         while ((d = readdir(dp)) != NULL) {
1768                 if (!strcmp(d->d_name, ".") ||
1769                     !strcmp(d->d_name, ".."))
1770                         continue;
1771
1772                 if (unlink(d->d_name) < 0)
1773                         FILE_OP_ERROR(d->d_name, "unlink");
1774         }
1775
1776         closedir(dp);
1777
1778         if (chdir(prev_dir) < 0) {
1779                 FILE_OP_ERROR(prev_dir, "chdir");
1780                 g_free(prev_dir);
1781                 return -1;
1782         }
1783
1784         g_free(prev_dir);
1785
1786         return 0;
1787 }
1788
1789 gint remove_numbered_files(const gchar *dir, guint first, guint last)
1790 {
1791         DIR *dp;
1792         struct dirent *d;
1793         gchar *prev_dir;
1794         gint fileno;
1795
1796         prev_dir = g_get_current_dir();
1797
1798         if (chdir(dir) < 0) {
1799                 FILE_OP_ERROR(dir, "chdir");
1800                 g_free(prev_dir);
1801                 return -1;
1802         }
1803
1804         if ((dp = opendir(".")) == NULL) {
1805                 FILE_OP_ERROR(dir, "opendir");
1806                 g_free(prev_dir);
1807                 return -1;
1808         }
1809
1810         while ((d = readdir(dp)) != NULL) {
1811                 fileno = to_number(d->d_name);
1812                 if (fileno >= 0 && first <= fileno && fileno <= last) {
1813                         if (is_dir_exist(d->d_name))
1814                                 continue;
1815                         if (unlink(d->d_name) < 0)
1816                                 FILE_OP_ERROR(d->d_name, "unlink");
1817                 }
1818         }
1819
1820         closedir(dp);
1821
1822         if (chdir(prev_dir) < 0) {
1823                 FILE_OP_ERROR(prev_dir, "chdir");
1824                 g_free(prev_dir);
1825                 return -1;
1826         }
1827
1828         g_free(prev_dir);
1829
1830         return 0;
1831 }
1832
1833 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
1834 {
1835         DIR *dp;
1836         struct dirent *d;
1837         gchar *prev_dir;
1838         gint fileno;
1839
1840         prev_dir = g_get_current_dir();
1841
1842         if (chdir(dir) < 0) {
1843                 FILE_OP_ERROR(dir, "chdir");
1844                 g_free(prev_dir);
1845                 return -1;
1846         }
1847
1848         if ((dp = opendir(".")) == NULL) {
1849                 FILE_OP_ERROR(dir, "opendir");
1850                 g_free(prev_dir);
1851                 return -1;
1852         }
1853
1854         while ((d = readdir(dp)) != NULL) {
1855                 fileno = to_number(d->d_name);
1856                 if (fileno >= 0 && (g_slist_find(numberlist, GINT_TO_POINTER(fileno)) == NULL)) {
1857                         debug_print("removing unwanted file %d from %s\n", fileno, dir);
1858                         if (is_dir_exist(d->d_name))
1859                                 continue;
1860                         if (unlink(d->d_name) < 0)
1861                                 FILE_OP_ERROR(d->d_name, "unlink");
1862                 }
1863         }
1864
1865         closedir(dp);
1866
1867         if (chdir(prev_dir) < 0) {
1868                 FILE_OP_ERROR(prev_dir, "chdir");
1869                 g_free(prev_dir);
1870                 return -1;
1871         }
1872
1873         g_free(prev_dir);
1874
1875         return 0;
1876 }
1877
1878 gint remove_all_numbered_files(const gchar *dir)
1879 {
1880         return remove_numbered_files(dir, 0, UINT_MAX);
1881 }
1882
1883 gint remove_expired_files(const gchar *dir, guint hours)
1884 {
1885         DIR *dp;
1886         struct dirent *d;
1887         struct stat s;
1888         gchar *prev_dir;
1889         gint fileno;
1890         time_t mtime, now, expire_time;
1891
1892         prev_dir = g_get_current_dir();
1893
1894         if (chdir(dir) < 0) {
1895                 FILE_OP_ERROR(dir, "chdir");
1896                 g_free(prev_dir);
1897                 return -1;
1898         }
1899
1900         if ((dp = opendir(".")) == NULL) {
1901                 FILE_OP_ERROR(dir, "opendir");
1902                 g_free(prev_dir);
1903                 return -1;
1904         }
1905
1906         now = time(NULL);
1907         expire_time = hours * 60 * 60;
1908
1909         while ((d = readdir(dp)) != NULL) {
1910                 fileno = to_number(d->d_name);
1911                 if (fileno >= 0) {
1912                         if (stat(d->d_name, &s) < 0) {
1913                                 FILE_OP_ERROR(d->d_name, "stat");
1914                                 continue;
1915                         }
1916                         if (S_ISDIR(s.st_mode))
1917                                 continue;
1918                         mtime = MAX(s.st_mtime, s.st_atime);
1919                         if (now - mtime > expire_time) {
1920                                 if (unlink(d->d_name) < 0)
1921                                         FILE_OP_ERROR(d->d_name, "unlink");
1922                         }
1923                 }
1924         }
1925
1926         closedir(dp);
1927
1928         if (chdir(prev_dir) < 0) {
1929                 FILE_OP_ERROR(prev_dir, "chdir");
1930                 g_free(prev_dir);
1931                 return -1;
1932         }
1933
1934         g_free(prev_dir);
1935
1936         return 0;
1937 }
1938
1939 gint remove_dir_recursive(const gchar *dir)
1940 {
1941         struct stat s;
1942         DIR *dp;
1943         struct dirent *d;
1944         gchar *prev_dir;
1945
1946         /* g_print("dir = %s\n", dir); */
1947
1948         if (stat(dir, &s) < 0) {
1949                 FILE_OP_ERROR(dir, "stat");
1950                 if (ENOENT == errno) return 0;
1951                 return -1;
1952         }
1953
1954         if (!S_ISDIR(s.st_mode)) {
1955                 if (unlink(dir) < 0) {
1956                         FILE_OP_ERROR(dir, "unlink");
1957                         return -1;
1958                 }
1959
1960                 return 0;
1961         }
1962
1963         prev_dir = g_get_current_dir();
1964         /* g_print("prev_dir = %s\n", prev_dir); */
1965
1966         if (!path_cmp(prev_dir, dir)) {
1967                 g_free(prev_dir);
1968                 if (chdir("..") < 0) {
1969                         FILE_OP_ERROR(dir, "chdir");
1970                         return -1;
1971                 }
1972                 prev_dir = g_get_current_dir();
1973         }
1974
1975         if (chdir(dir) < 0) {
1976                 FILE_OP_ERROR(dir, "chdir");
1977                 g_free(prev_dir);
1978                 return -1;
1979         }
1980
1981         if ((dp = opendir(".")) == NULL) {
1982                 FILE_OP_ERROR(dir, "opendir");
1983                 chdir(prev_dir);
1984                 g_free(prev_dir);
1985                 return -1;
1986         }
1987
1988         /* remove all files in the directory */
1989         while ((d = readdir(dp)) != NULL) {
1990                 if (!strcmp(d->d_name, ".") ||
1991                     !strcmp(d->d_name, ".."))
1992                         continue;
1993
1994                 if (stat(d->d_name, &s) < 0) {
1995                         FILE_OP_ERROR(d->d_name, "stat");
1996                         continue;
1997                 }
1998
1999                 /* g_print("removing %s\n", d->d_name); */
2000
2001                 if (S_ISDIR(s.st_mode)) {
2002                         if (remove_dir_recursive(d->d_name) < 0) {
2003                                 g_warning("can't remove directory\n");
2004                                 return -1;
2005                         }
2006                 } else {
2007                         if (unlink(d->d_name) < 0)
2008                                 FILE_OP_ERROR(d->d_name, "unlink");
2009                 }
2010         }
2011
2012         closedir(dp);
2013
2014         if (chdir(prev_dir) < 0) {
2015                 FILE_OP_ERROR(prev_dir, "chdir");
2016                 g_free(prev_dir);
2017                 return -1;
2018         }
2019
2020         g_free(prev_dir);
2021
2022         if (rmdir(dir) < 0) {
2023                 FILE_OP_ERROR(dir, "rmdir");
2024                 return -1;
2025         }
2026
2027         return 0;
2028 }
2029
2030 #if 0
2031 /* this seems to be slower than the stdio version... */
2032 gint copy_file(const gchar *src, const gchar *dest)
2033 {
2034         gint src_fd, dest_fd;
2035         gint n_read;
2036         gint n_write;
2037         gchar buf[BUFSIZ];
2038         gchar *dest_bak = NULL;
2039
2040         if ((src_fd = open(src, O_RDONLY)) < 0) {
2041                 FILE_OP_ERROR(src, "open");
2042                 return -1;
2043         }
2044
2045         if (is_file_exist(dest)) {
2046                 dest_bak = g_strconcat(dest, ".bak", NULL);
2047                 if (rename(dest, dest_bak) < 0) {
2048                         FILE_OP_ERROR(dest, "rename");
2049                         close(src_fd);
2050                         g_free(dest_bak);
2051                         return -1;
2052                 }
2053         }
2054
2055         if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
2056                 FILE_OP_ERROR(dest, "open");
2057                 close(src_fd);
2058                 if (dest_bak) {
2059                         if (rename(dest_bak, dest) < 0)
2060                                 FILE_OP_ERROR(dest_bak, "rename");
2061                         g_free(dest_bak);
2062                 }
2063                 return -1;
2064         }
2065
2066         while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
2067                 gint len = n_read;
2068                 gchar *bufp = buf;
2069
2070                 while (len > 0) {
2071                         n_write = write(dest_fd, bufp, len);
2072                         if (n_write <= 0) {
2073                                 g_warning(_("writing to %s failed.\n"), dest);
2074                                 close(dest_fd);
2075                                 close(src_fd);
2076                                 unlink(dest);
2077                                 if (dest_bak) {
2078                                         if (rename(dest_bak, dest) < 0)
2079                                                 FILE_OP_ERROR(dest_bak, "rename");
2080                                         g_free(dest_bak);
2081                                 }
2082                                 return -1;
2083                         }
2084                         len -= n_write;
2085                         bufp += n_write;
2086                 }
2087         }
2088
2089         close(src_fd);
2090         close(dest_fd);
2091
2092         if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
2093                 g_warning(_("File copy from %s to %s failed.\n"), src, dest);
2094                 unlink(dest);
2095                 if (dest_bak) {
2096                         if (rename(dest_bak, dest) < 0)
2097                                 FILE_OP_ERROR(dest_bak, "rename");
2098                         g_free(dest_bak);
2099                 }
2100                 return -1;
2101         }
2102         g_free(dest_bak);
2103
2104         return 0;
2105 }
2106 #endif
2107
2108
2109 /*
2110  * Append src file body to the tail of dest file.
2111  * Now keep_backup has no effects.
2112  */
2113 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2114 {
2115         FILE *src_fp, *dest_fp;
2116         gint n_read;
2117         gchar buf[BUFSIZ];
2118
2119         gboolean err = FALSE;
2120
2121         if ((src_fp = fopen(src, "rb")) == NULL) {
2122                 FILE_OP_ERROR(src, "fopen");
2123                 return -1;
2124         }
2125         
2126         if ((dest_fp = fopen(dest, "ab")) == NULL) {
2127                 FILE_OP_ERROR(dest, "fopen");
2128                 fclose(src_fp);
2129                 return -1;
2130         }
2131
2132         if (change_file_mode_rw(dest_fp, dest) < 0) {
2133                 FILE_OP_ERROR(dest, "chmod");
2134                 g_warning(_("can't change file mode\n"));
2135         }
2136
2137         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2138                 if (n_read < sizeof(buf) && ferror(src_fp))
2139                         break;
2140                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2141                         g_warning(_("writing to %s failed.\n"), dest);
2142                         fclose(dest_fp);
2143                         fclose(src_fp);
2144                         unlink(dest);
2145                         return -1;
2146                 }
2147         }
2148
2149         if (ferror(src_fp)) {
2150                 FILE_OP_ERROR(src, "fread");
2151                 err = TRUE;
2152         }
2153         fclose(src_fp);
2154         if (fclose(dest_fp) == EOF) {
2155                 FILE_OP_ERROR(dest, "fclose");
2156                 err = TRUE;
2157         }
2158
2159         if (err) {
2160                 unlink(dest);
2161                 return -1;
2162         }
2163
2164         return 0;
2165 }
2166
2167 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2168 {
2169         FILE *src_fp, *dest_fp;
2170         gint n_read;
2171         gchar buf[BUFSIZ];
2172         gchar *dest_bak = NULL;
2173         gboolean err = FALSE;
2174
2175         if ((src_fp = fopen(src, "rb")) == NULL) {
2176                 FILE_OP_ERROR(src, "fopen");
2177                 return -1;
2178         }
2179         if (is_file_exist(dest)) {
2180                 dest_bak = g_strconcat(dest, ".bak", NULL);
2181                 if (rename(dest, dest_bak) < 0) {
2182                         FILE_OP_ERROR(dest, "rename");
2183                         fclose(src_fp);
2184                         g_free(dest_bak);
2185                         return -1;
2186                 }
2187         }
2188
2189         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2190                 FILE_OP_ERROR(dest, "fopen");
2191                 fclose(src_fp);
2192                 if (dest_bak) {
2193                         if (rename(dest_bak, dest) < 0)
2194                                 FILE_OP_ERROR(dest_bak, "rename");
2195                         g_free(dest_bak);
2196                 }
2197                 return -1;
2198         }
2199
2200         if (change_file_mode_rw(dest_fp, dest) < 0) {
2201                 FILE_OP_ERROR(dest, "chmod");
2202                 g_warning(_("can't change file mode\n"));
2203         }
2204
2205         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2206                 if (n_read < sizeof(buf) && ferror(src_fp))
2207                         break;
2208                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2209                         g_warning(_("writing to %s failed.\n"), dest);
2210                         fclose(dest_fp);
2211                         fclose(src_fp);
2212                         unlink(dest);
2213                         if (dest_bak) {
2214                                 if (rename(dest_bak, dest) < 0)
2215                                         FILE_OP_ERROR(dest_bak, "rename");
2216                                 g_free(dest_bak);
2217                         }
2218                         return -1;
2219                 }
2220         }
2221
2222         if (ferror(src_fp)) {
2223                 FILE_OP_ERROR(src, "fread");
2224                 err = TRUE;
2225         }
2226         fclose(src_fp);
2227         if (fclose(dest_fp) == EOF) {
2228                 FILE_OP_ERROR(dest, "fclose");
2229                 err = TRUE;
2230         }
2231
2232         if (err) {
2233                 unlink(dest);
2234                 if (dest_bak) {
2235                         if (rename(dest_bak, dest) < 0)
2236                                 FILE_OP_ERROR(dest_bak, "rename");
2237                         g_free(dest_bak);
2238                 }
2239                 return -1;
2240         }
2241
2242         if (keep_backup == FALSE && dest_bak)
2243                 unlink(dest_bak);
2244
2245         g_free(dest_bak);
2246
2247         return 0;
2248 }
2249
2250 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2251 {
2252         if (overwrite == FALSE && is_file_exist(dest)) {
2253                 g_warning("move_file(): file %s already exists.", dest);
2254                 return -1;
2255         }
2256
2257         if (rename(src, dest) == 0) return 0;
2258
2259         if (EXDEV != errno) {
2260                 FILE_OP_ERROR(src, "rename");
2261                 return -1;
2262         }
2263
2264         if (copy_file(src, dest, FALSE) < 0) return -1;
2265
2266         unlink(src);
2267
2268         return 0;
2269 }
2270
2271 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2272 {
2273         FILE *dest_fp;
2274         gint n_read;
2275         gint bytes_left, to_read;
2276         gchar buf[BUFSIZ];
2277         gboolean err = FALSE;
2278
2279         if (fseek(fp, offset, SEEK_SET) < 0) {
2280                 perror("fseek");
2281                 return -1;
2282         }
2283
2284         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2285                 FILE_OP_ERROR(dest, "fopen");
2286                 return -1;
2287         }
2288
2289         if (change_file_mode_rw(dest_fp, dest) < 0) {
2290                 FILE_OP_ERROR(dest, "chmod");
2291                 g_warning("can't change file mode\n");
2292         }
2293
2294         bytes_left = length;
2295         to_read = MIN(bytes_left, sizeof(buf));
2296
2297         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2298                 if (n_read < to_read && ferror(fp))
2299                         break;
2300                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2301                         g_warning(_("writing to %s failed.\n"), dest);
2302                         fclose(dest_fp);
2303                         unlink(dest);
2304                         return -1;
2305                 }
2306                 bytes_left -= n_read;
2307                 if (bytes_left == 0)
2308                         break;
2309                 to_read = MIN(bytes_left, sizeof(buf));
2310         }
2311
2312         if (ferror(fp)) {
2313                 perror("fread");
2314                 err = TRUE;
2315         }
2316         if (fclose(dest_fp) == EOF) {
2317                 FILE_OP_ERROR(dest, "fclose");
2318                 err = TRUE;
2319         }
2320
2321         if (err) {
2322                 unlink(dest);
2323                 return -1;
2324         }
2325
2326         return 0;
2327 }
2328
2329 /* convert line endings into CRLF. If the last line doesn't end with
2330  * linebreak, add it.
2331  */
2332 gint canonicalize_file(const gchar *src, const gchar *dest)
2333 {
2334         FILE *src_fp, *dest_fp;
2335         gchar buf[BUFFSIZE];
2336         gint len;
2337         gboolean err = FALSE;
2338         gboolean last_linebreak = FALSE;
2339
2340         if ((src_fp = fopen(src, "rb")) == NULL) {
2341                 FILE_OP_ERROR(src, "fopen");
2342                 return -1;
2343         }
2344
2345         if ((dest_fp = fopen(dest, "wb")) == NULL) {
2346                 FILE_OP_ERROR(dest, "fopen");
2347                 fclose(src_fp);
2348                 return -1;
2349         }
2350
2351         if (change_file_mode_rw(dest_fp, dest) < 0) {
2352                 FILE_OP_ERROR(dest, "chmod");
2353                 g_warning("can't change file mode\n");
2354         }
2355
2356         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2357                 gint r = 0;
2358
2359                 len = strlen(buf);
2360                 if (len == 0) break;
2361                 last_linebreak = FALSE;
2362
2363                 if (buf[len - 1] != '\n') {
2364                         last_linebreak = TRUE;
2365                         r = fputs(buf, dest_fp);
2366                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2367                         r = fputs(buf, dest_fp);
2368                 } else {
2369                         if (len > 1) {
2370                                 r = fwrite(buf, len - 1, 1, dest_fp);
2371                                 if (r != 1)
2372                                         r = EOF;
2373                         }
2374                         if (r != EOF)
2375                                 r = fputs("\r\n", dest_fp);
2376                 }
2377
2378                 if (r == EOF) {
2379                         g_warning("writing to %s failed.\n", dest);
2380                         fclose(dest_fp);
2381                         fclose(src_fp);
2382                         unlink(dest);
2383                         return -1;
2384                 }
2385         }
2386
2387         if (last_linebreak == TRUE) {
2388                 if (fputs("\r\n", dest_fp) == EOF)
2389                         err = TRUE;
2390         }
2391
2392         if (ferror(src_fp)) {
2393                 FILE_OP_ERROR(src, "fread");
2394                 err = TRUE;
2395         }
2396         fclose(src_fp);
2397         if (fclose(dest_fp) == EOF) {
2398                 FILE_OP_ERROR(dest, "fclose");
2399                 err = TRUE;
2400         }
2401
2402         if (err) {
2403                 unlink(dest);
2404                 return -1;
2405         }
2406
2407         return 0;
2408 }
2409
2410 gint canonicalize_file_replace(const gchar *file)
2411 {
2412         gchar *tmp_file;
2413
2414         tmp_file = get_tmp_file();
2415
2416         if (canonicalize_file(file, tmp_file) < 0) {
2417                 g_free(tmp_file);
2418                 return -1;
2419         }
2420
2421         if (move_file(tmp_file, file, TRUE) < 0) {
2422                 g_warning("can't replace %s .\n", file);
2423                 unlink(tmp_file);
2424                 g_free(tmp_file);
2425                 return -1;
2426         }
2427
2428         g_free(tmp_file);
2429         return 0;
2430 }
2431
2432 gint change_file_mode_rw(FILE *fp, const gchar *file)
2433 {
2434 #if HAVE_FCHMOD
2435         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2436 #else
2437         return chmod(file, S_IRUSR|S_IWUSR);
2438 #endif
2439 }
2440
2441 FILE *my_tmpfile(void)
2442 {
2443 #if HAVE_MKSTEMP
2444         const gchar suffix[] = ".XXXXXX";
2445         const gchar *tmpdir;
2446         guint tmplen;
2447         const gchar *progname;
2448         guint proglen;
2449         gchar *fname;
2450         gint fd;
2451         FILE *fp;
2452
2453         tmpdir = get_tmp_dir();
2454         tmplen = strlen(tmpdir);
2455         progname = g_get_prgname();
2456         proglen = strlen(progname);
2457         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2458                 return tmpfile());
2459
2460         memcpy(fname, tmpdir, tmplen);
2461         fname[tmplen] = G_DIR_SEPARATOR;
2462         memcpy(fname + tmplen + 1, progname, proglen);
2463         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2464
2465         fd = mkstemp(fname);
2466         if (fd < 0)
2467                 return tmpfile();
2468
2469         unlink(fname);
2470
2471         fp = fdopen(fd, "w+b");
2472         if (!fp)
2473                 close(fd);
2474         else
2475                 return fp;
2476 #endif /* HAVE_MKSTEMP */
2477
2478         return tmpfile();
2479 }
2480
2481 FILE *str_open_as_stream(const gchar *str)
2482 {
2483         FILE *fp;
2484         size_t len;
2485
2486         g_return_val_if_fail(str != NULL, NULL);
2487
2488         fp = my_tmpfile();
2489         if (!fp) {
2490                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2491                 return NULL;
2492         }
2493
2494         len = strlen(str);
2495         if (len == 0) return fp;
2496
2497         if (fwrite(str, len, 1, fp) != 1) {
2498                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2499                 fclose(fp);
2500                 return NULL;
2501         }
2502
2503         rewind(fp);
2504         return fp;
2505 }
2506
2507 gint str_write_to_file(const gchar *str, const gchar *file)
2508 {
2509         FILE *fp;
2510         size_t len;
2511
2512         g_return_val_if_fail(str != NULL, -1);
2513         g_return_val_if_fail(file != NULL, -1);
2514
2515         if ((fp = fopen(file, "wb")) == NULL) {
2516                 FILE_OP_ERROR(file, "fopen");
2517                 return -1;
2518         }
2519
2520         len = strlen(str);
2521         if (len == 0) {
2522                 fclose(fp);
2523                 return 0;
2524         }
2525
2526         if (fwrite(str, len, 1, fp) != 1) {
2527                 FILE_OP_ERROR(file, "fwrite");
2528                 fclose(fp);
2529                 unlink(file);
2530                 return -1;
2531         }
2532
2533         if (fclose(fp) == EOF) {
2534                 FILE_OP_ERROR(file, "fclose");
2535                 unlink(file);
2536                 return -1;
2537         }
2538
2539         return 0;
2540 }
2541
2542 gchar *file_read_to_str(const gchar *file)
2543 {
2544         GByteArray *array;
2545         FILE *fp;
2546         gchar buf[BUFSIZ];
2547         gint n_read;
2548         gchar *str;
2549
2550         g_return_val_if_fail(file != NULL, NULL);
2551
2552         if ((fp = fopen(file, "rb")) == NULL) {
2553                 FILE_OP_ERROR(file, "fopen");
2554                 return NULL;
2555         }
2556
2557         array = g_byte_array_new();
2558
2559         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
2560                 if (n_read < sizeof(buf) && ferror(fp))
2561                         break;
2562                 g_byte_array_append(array, buf, n_read);
2563         }
2564
2565         if (ferror(fp)) {
2566                 FILE_OP_ERROR(file, "fread");
2567                 fclose(fp);
2568                 g_byte_array_free(array, TRUE);
2569                 return NULL;
2570         }
2571
2572         fclose(fp);
2573
2574         buf[0] = '\0';
2575         g_byte_array_append(array, buf, 1);
2576         str = (gchar *)array->data;
2577         g_byte_array_free(array, FALSE);
2578
2579         return str;
2580 }
2581
2582 gint execute_async(gchar *const argv[])
2583 {
2584         pid_t pid;
2585
2586         if ((pid = fork()) < 0) {
2587                 perror("fork");
2588                 return -1;
2589         }
2590
2591         if (pid == 0) {                 /* child process */
2592                 pid_t gch_pid;
2593
2594                 if ((gch_pid = fork()) < 0) {
2595                         perror("fork");
2596                         _exit(1);
2597                 }
2598
2599                 if (gch_pid == 0) {     /* grandchild process */
2600                         execvp(argv[0], argv);
2601
2602                         perror("execvp");
2603                         _exit(1);
2604                 }
2605
2606                 _exit(0);
2607         }
2608
2609         waitpid(pid, NULL, 0);
2610
2611         return 0;
2612 }
2613
2614 gint execute_sync(gchar *const argv[])
2615 {
2616         pid_t pid;
2617
2618         if ((pid = fork()) < 0) {
2619                 perror("fork");
2620                 return -1;
2621         }
2622
2623         if (pid == 0) {         /* child process */
2624                 execvp(argv[0], argv);
2625
2626                 perror("execvp");
2627                 _exit(1);
2628         }
2629
2630         waitpid(pid, NULL, 0);
2631
2632         return 0;
2633 }
2634
2635 gint execute_command_line(const gchar *cmdline, gboolean async)
2636 {
2637         gchar **argv;
2638         gint ret;
2639
2640         argv = strsplit_with_quote(cmdline, " ", 0);
2641
2642         if (async)
2643                 ret = execute_async(argv);
2644         else
2645                 ret = execute_sync(argv);
2646         g_strfreev(argv);
2647
2648         return ret;
2649 }
2650
2651 static gint is_unchanged_uri_char(char c)
2652 {
2653         switch (c) {
2654                 case '(':
2655                 case ')':
2656                 case ',':
2657                         return 0;
2658                 default:
2659                         return 1;
2660         }
2661 }
2662
2663 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
2664 {
2665         int i;
2666         int k;
2667
2668         k = 0;
2669         for(i = 0; i < strlen(uri) ; i++) {
2670                 if (is_unchanged_uri_char(uri[i])) {
2671                         if (k + 2 >= bufsize)
2672                                 break;
2673                         encoded_uri[k++] = uri[i];
2674                 }
2675                 else {
2676                         char * hexa = "0123456789ABCDEF";
2677                         
2678                         if (k + 4 >= bufsize)
2679                                 break;
2680                         encoded_uri[k++] = '%';
2681                         encoded_uri[k++] = hexa[uri[i] / 16];
2682                         encoded_uri[k++] = hexa[uri[i] % 16];
2683                 }
2684         }
2685         encoded_uri[k] = 0;
2686 }
2687
2688 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped 
2689  * characters
2690  */
2691 static gint axtoi(const gchar *hexstr)
2692 {
2693         gint hi, lo, result;
2694        
2695         hi = hexstr[0];
2696         if ('0' <= hi && hi <= '9') {
2697                 hi -= '0';
2698         } else
2699                 if ('a' <= hi && hi <= 'f') {
2700                         hi -= ('a' - 10);
2701                 } else
2702                         if ('A' <= hi && hi <= 'F') {
2703                                 hi -= ('A' - 10);
2704                         }
2705
2706         lo = hexstr[1];
2707         if ('0' <= lo && lo <= '9') {
2708                 lo -= '0';
2709         } else
2710                 if ('a' <= lo && lo <= 'f') {
2711                         lo -= ('a'-10);
2712                 } else
2713                         if ('A' <= lo && lo <= 'F') {
2714                                 lo -= ('A' - 10);
2715                         }
2716         result = lo + (16 * hi);
2717         return result;
2718 }
2719
2720
2721 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
2722  * plusses, and escape characters are used)
2723  */
2724
2725 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
2726 {
2727         const gchar *encoded;
2728         gchar *decoded;
2729
2730         encoded = encoded_uri;
2731         decoded = decoded_uri;
2732
2733         while (*encoded) {
2734                 if (*encoded == '%') {
2735                         encoded++;
2736                         if (isxdigit(encoded[0])
2737                             && isxdigit(encoded[1])) {
2738                                 *decoded = (gchar) axtoi(encoded);
2739                                 decoded++;
2740                                 encoded += 2;
2741                         }
2742                 }
2743                 else if (*encoded == '+') {
2744                         *decoded = ' ';
2745                         decoded++;
2746                         encoded++;
2747                 }
2748                 else {
2749                         *decoded = *encoded;
2750                         decoded++;
2751                         encoded++;
2752                 }
2753         }
2754
2755         *decoded = '\0';
2756 }
2757
2758
2759 gint open_uri(const gchar *uri, const gchar *cmdline)
2760 {
2761         static gchar *default_cmdline = "netscape -remote openURL(%s,raise)";
2762         gchar buf[BUFFSIZE];
2763         gchar *p;
2764         gchar encoded_uri[BUFFSIZE];
2765         
2766         g_return_val_if_fail(uri != NULL, -1);
2767
2768         /* an option to choose whether to use encode_uri or not ? */
2769         encode_uri(encoded_uri, BUFFSIZE, uri);
2770         
2771         if (cmdline &&
2772             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
2773             !strchr(p + 2, '%'))
2774                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
2775         else {
2776                 if (cmdline)
2777                         g_warning(_("Open URI command line is invalid: `%s'"),
2778                                   cmdline);
2779                 g_snprintf(buf, sizeof(buf), default_cmdline, encoded_uri);
2780         }
2781         
2782         execute_command_line(buf, TRUE);
2783
2784         return 0;
2785 }
2786
2787 time_t remote_tzoffset_sec(const gchar *zone)
2788 {
2789         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
2790         gchar zone3[4];
2791         gchar *p;
2792         gchar c;
2793         gint iustz;
2794         gint offset;
2795         time_t remoteoffset;
2796
2797         strncpy(zone3, zone, 3);
2798         zone3[3] = '\0';
2799         remoteoffset = 0;
2800
2801         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
2802             (c == '+' || c == '-')) {
2803                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
2804                 if (c == '-')
2805                         remoteoffset = -remoteoffset;
2806         } else if (!strncmp(zone, "UT" , 2) ||
2807                    !strncmp(zone, "GMT", 2)) {
2808                 remoteoffset = 0;
2809         } else if (strlen(zone3) == 3 &&
2810                    (p = strstr(ustzstr, zone3)) != NULL &&
2811                    (p - ustzstr) % 3 == 0) {
2812                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
2813                 remoteoffset = iustz * 3600;
2814         } else if (strlen(zone3) == 1) {
2815                 switch (zone[0]) {
2816                 case 'Z': remoteoffset =   0; break;
2817                 case 'A': remoteoffset =  -1; break;
2818                 case 'B': remoteoffset =  -2; break;
2819                 case 'C': remoteoffset =  -3; break;
2820                 case 'D': remoteoffset =  -4; break;
2821                 case 'E': remoteoffset =  -5; break;
2822                 case 'F': remoteoffset =  -6; break;
2823                 case 'G': remoteoffset =  -7; break;
2824                 case 'H': remoteoffset =  -8; break;
2825                 case 'I': remoteoffset =  -9; break;
2826                 case 'K': remoteoffset = -10; break; /* J is not used */
2827                 case 'L': remoteoffset = -11; break;
2828                 case 'M': remoteoffset = -12; break;
2829                 case 'N': remoteoffset =   1; break;
2830                 case 'O': remoteoffset =   2; break;
2831                 case 'P': remoteoffset =   3; break;
2832                 case 'Q': remoteoffset =   4; break;
2833                 case 'R': remoteoffset =   5; break;
2834                 case 'S': remoteoffset =   6; break;
2835                 case 'T': remoteoffset =   7; break;
2836                 case 'U': remoteoffset =   8; break;
2837                 case 'V': remoteoffset =   9; break;
2838                 case 'W': remoteoffset =  10; break;
2839                 case 'X': remoteoffset =  11; break;
2840                 case 'Y': remoteoffset =  12; break;
2841                 default:  remoteoffset =   0; break;
2842                 }
2843                 remoteoffset = remoteoffset * 3600;
2844         }
2845
2846         return remoteoffset;
2847 }
2848
2849 time_t tzoffset_sec(time_t *now)
2850 {
2851         struct tm gmt, *lt;
2852         gint off;
2853
2854         gmt = *gmtime(now);
2855         lt = localtime(now);
2856
2857         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
2858
2859         if (lt->tm_year < gmt.tm_year)
2860                 off -= 24 * 60;
2861         else if (lt->tm_year > gmt.tm_year)
2862                 off += 24 * 60;
2863         else if (lt->tm_yday < gmt.tm_yday)
2864                 off -= 24 * 60;
2865         else if (lt->tm_yday > gmt.tm_yday)
2866                 off += 24 * 60;
2867
2868         if (off >= 24 * 60)             /* should be impossible */
2869                 off = 23 * 60 + 59;     /* if not, insert silly value */
2870         if (off <= -24 * 60)
2871                 off = -(23 * 60 + 59);
2872         if (off > 12 * 60)
2873                 off -= 24 * 60;
2874         if (off < -12 * 60)
2875                 off += 24 * 60;
2876
2877         return off * 60;
2878 }
2879
2880 /* calculate timezone offset */
2881 gchar *tzoffset(time_t *now)
2882 {
2883         static gchar offset_string[6];
2884         struct tm gmt, *lt;
2885         gint off;
2886         gchar sign = '+';
2887
2888         gmt = *gmtime(now);
2889         lt = localtime(now);
2890
2891         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
2892
2893         if (lt->tm_year < gmt.tm_year)
2894                 off -= 24 * 60;
2895         else if (lt->tm_year > gmt.tm_year)
2896                 off += 24 * 60;
2897         else if (lt->tm_yday < gmt.tm_yday)
2898                 off -= 24 * 60;
2899         else if (lt->tm_yday > gmt.tm_yday)
2900                 off += 24 * 60;
2901
2902         if (off < 0) {
2903                 sign = '-';
2904                 off = -off;
2905         }
2906
2907         if (off >= 24 * 60)             /* should be impossible */
2908                 off = 23 * 60 + 59;     /* if not, insert silly value */
2909
2910         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
2911
2912         return offset_string;
2913 }
2914
2915 void get_rfc822_date(gchar *buf, gint len)
2916 {
2917         struct tm *lt;
2918         time_t t;
2919         gchar day[4], mon[4];
2920         gint dd, hh, mm, ss, yyyy;
2921
2922         t = time(NULL);
2923         lt = localtime(&t);
2924
2925         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
2926                day, mon, &dd, &hh, &mm, &ss, &yyyy);
2927         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
2928                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
2929 }
2930
2931 static FILE *log_fp = NULL;
2932
2933 void set_log_file(const gchar *filename)
2934 {
2935         if (log_fp) return;
2936         log_fp = fopen(filename, "wb");
2937         if (!log_fp)
2938                 FILE_OP_ERROR(filename, "fopen");
2939 }
2940
2941 void close_log_file(void)
2942 {
2943         if (log_fp) {
2944                 fclose(log_fp);
2945                 log_fp = NULL;
2946         }
2947 }
2948
2949 static guint log_verbosity_count = 0;
2950
2951 void log_verbosity_set(gboolean verbose)
2952 {
2953         if (verbose)
2954                 log_verbosity_count++;
2955         else if (log_verbosity_count > 0)
2956                 log_verbosity_count--;
2957 }
2958
2959 void debug_print_real(const gchar *format, ...)
2960 {
2961         va_list args;
2962         gchar buf[BUFFSIZE];
2963
2964         if (!debug_mode) return;
2965
2966         va_start(args, format);
2967         g_vsnprintf(buf, sizeof(buf), format, args);
2968         va_end(args);
2969
2970         fputs(buf, stdout);
2971 }
2972
2973 #define TIME_LEN        11
2974
2975 void log_print(const gchar *format, ...)
2976 {
2977         va_list args;
2978         gchar buf[BUFFSIZE + TIME_LEN];
2979         time_t t;
2980
2981         time(&t);
2982         strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
2983
2984         va_start(args, format);
2985         g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
2986         va_end(args);
2987
2988         if (debug_mode) fputs(buf, stdout);
2989         log_window_append(buf, LOG_NORMAL);
2990         if (log_fp) {
2991                 fputs(buf, log_fp);
2992                 fflush(log_fp);
2993         }
2994         if (log_verbosity_count)
2995                 statusbar_puts_all(buf + TIME_LEN);
2996 }
2997
2998 void log_message(const gchar *format, ...)
2999 {
3000         va_list args;
3001         gchar buf[BUFFSIZE + TIME_LEN];
3002         time_t t;
3003
3004         time(&t);
3005         strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3006
3007         va_start(args, format);
3008         g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3009         va_end(args);
3010
3011         if (debug_mode) g_message("%s", buf + TIME_LEN);
3012         log_window_append(buf + TIME_LEN, LOG_MSG);
3013         if (log_fp) {
3014                 fwrite(buf, TIME_LEN, 1, log_fp);
3015                 fputs("* message: ", log_fp);
3016                 fputs(buf + TIME_LEN, log_fp);
3017                 fflush(log_fp);
3018         }
3019         statusbar_puts_all(buf + TIME_LEN);
3020 }
3021
3022 void log_warning(const gchar *format, ...)
3023 {
3024         va_list args;
3025         gchar buf[BUFFSIZE + TIME_LEN];
3026         time_t t;
3027
3028         time(&t);
3029         strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3030
3031         va_start(args, format);
3032         g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3033         va_end(args);
3034
3035         g_warning("%s", buf);
3036         log_window_append(buf + TIME_LEN, LOG_WARN);
3037         if (log_fp) {
3038                 fwrite(buf, TIME_LEN, 1, log_fp);
3039                 fputs("** warning: ", log_fp);
3040                 fputs(buf + TIME_LEN, log_fp);
3041                 fflush(log_fp);
3042         }
3043 }
3044
3045 void log_error(const gchar *format, ...)
3046 {
3047         va_list args;
3048         gchar buf[BUFFSIZE + TIME_LEN];
3049         time_t t;
3050
3051         time(&t);
3052         strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3053
3054         va_start(args, format);
3055         g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3056         va_end(args);
3057
3058         g_warning("%s", buf);
3059         log_window_append(buf + TIME_LEN, LOG_ERROR);
3060         if (log_fp) {
3061                 fwrite(buf, TIME_LEN, 1, log_fp);
3062                 fputs("*** error: ", log_fp);
3063                 fputs(buf + TIME_LEN, log_fp);
3064                 fflush(log_fp);
3065         }
3066 }
3067
3068
3069 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3070 {
3071         if (subject == NULL)
3072                 subject = "";
3073
3074         if (g_strncasecmp(subject, "Re: ", 4) == 0)
3075                 return g_hash_table_lookup(subject_table, subject + 4);
3076         else
3077                 return g_hash_table_lookup(subject_table, subject);
3078 }
3079
3080 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3081                           void * data)
3082 {
3083         if (subject == NULL)
3084                 return;
3085         if (* subject == 0)
3086                 return;
3087         if (g_strcasecmp(subject, "Re:") == 0)
3088                 return;
3089         if (g_strcasecmp(subject, "Re: ") == 0)
3090                 return;
3091
3092         if (g_strncasecmp(subject, "Re: ", 4) == 0)
3093                 g_hash_table_insert(subject_table, subject + 4, data);
3094         else
3095                 g_hash_table_insert(subject_table, subject, data);
3096 }
3097
3098 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3099 {
3100         if (subject == NULL)
3101                 return;
3102
3103         if (g_strncasecmp(subject, "Re: ", 4) == 0)
3104                 g_hash_table_remove(subject_table, subject + 4);
3105         else
3106                 g_hash_table_remove(subject_table, subject);
3107 }
3108
3109 gboolean subject_is_reply(const gchar *subject)
3110 {
3111         /* XXX: just simply here so someone can handle really
3112          * advanced Re: detection like "Re[4]", "ANTW:" or
3113          * Re: Re: Re: Re: Re: Re: Re: Re:" stuff. */
3114         if (subject == NULL) return FALSE;
3115         else return 0 == g_strncasecmp(subject, "Re: ", 4);
3116 }
3117
3118 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3119 {
3120         int fd;
3121         
3122         *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3123         fd = mkstemp(*filename);
3124
3125         return fdopen(fd, "w+");
3126 }