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