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