Statistics
| Revision:

root / src / utils.c @ 1

History | View | Annotate | Download (58.5 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <stdio.h>
28
#include <string.h>
29
#include <ctype.h>
30
#include <errno.h>
31

    
32
#if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
33
#  include <wchar.h>
34
#  include <wctype.h>
35
#endif
36
#include <stdlib.h>
37
#include <sys/stat.h>
38
#include <unistd.h>
39
#include <stdarg.h>
40
#include <sys/types.h>
41
#include <sys/wait.h>
42
#include <dirent.h>
43
#include <time.h>
44

    
45
#include "intl.h"
46
#include "utils.h"
47
#include "socket.h"
48
#include "statusbar.h"
49
#include "logwindow.h"
50

    
51
#define BUFFSIZE        8192
52

    
53
extern gboolean debug_mode;
54

    
55
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data);
56

    
57
void list_free_strings(GList *list)
58
{
59
        list = g_list_first(list);
60

    
61
        while (list != NULL) {
62
                g_free(list->data);
63
                list = list->next;
64
        }
65
}
66

    
67
void slist_free_strings(GSList *list)
68
{
69
        while (list != NULL) {
70
                g_free(list->data);
71
                list = list->next;
72
        }
73
}
74

    
75
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
76
{
77
        g_free(key);
78
}
79

    
80
void hash_free_strings(GHashTable *table)
81
{
82
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
83
}
84

    
85
static void hash_free_value_mem_func(gpointer key, gpointer value,
86
                                     gpointer data)
87
{
88
        g_free(value);
89
}
90

    
91
void hash_free_value_mem(GHashTable *table)
92
{
93
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
94
}
95

    
96
gint str_case_equal(gconstpointer v, gconstpointer v2)
97
{
98
        return strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
99
}
100

    
101
guint str_case_hash(gconstpointer key)
102
{
103
        const gchar *p = key;
104
        guint h = *p;
105

    
106
        if (h) {
107
                h = tolower(h);
108
                for (p += 1; *p != '\0'; p++)
109
                        h = (h << 5) - h + tolower(*p);
110
        }
111

    
112
        return h;
113
}
114

    
115
void ptr_array_free_strings(GPtrArray *array)
116
{
117
        gint i;
118
        gchar *str;
119

    
120
        g_return_if_fail(array != NULL);
121

    
122
        for (i = 0; i < array->len; i++) {
123
                str = g_ptr_array_index(array, i);
124
                g_free(str);
125
        }
126
}
127

    
128
gboolean str_find(const gchar *haystack, const gchar *needle)
129
{
130
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
131
}
132

    
133
gboolean str_case_find(const gchar *haystack, const gchar *needle)
134
{
135
        return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
136
}
137

    
138
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
139
{
140
        return strcmp(haystack, needle) == 0;
141
}
142

    
143
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
144
{
145
        return strcasecmp(haystack, needle) == 0;
146
}
147

    
148
gint to_number(const gchar *nstr)
149
{
150
        register const guchar *p;
151

    
152
        if (*nstr == '\0') return -1;
153

    
154
        for (p = nstr; *p != '\0'; p++)
155
                if (!isdigit(*p)) return -1;
156

    
157
        return atoi(nstr);
158
}
159

    
160
/* convert integer into string,
161
   nstr must be not lower than 11 characters length */
162
gchar *itos_buf(gchar *nstr, gint n)
163
{
164
        g_snprintf(nstr, 11, "%d", n);
165
        return nstr;
166
}
167

    
168
/* convert integer into string */
169
gchar *itos(gint n)
170
{
171
        static gchar nstr[11];
172

    
173
        return itos_buf(nstr, n);
174
}
175

    
176
gchar *to_human_readable(off_t size)
177
{
178
        static gchar str[10];
179

    
180
        if (size < 1024)
181
                g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
182
        else if (size >> 10 < 1024)
183
                g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
184
        else if (size >> 20 < 1024)
185
                g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
186
        else
187
                g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
188

    
189
        return str;
190
}
191

    
192
/* strcmp with NULL-checking */
193
gint strcmp2(const gchar *s1, const gchar *s2)
194
{
195
        if (s1 == NULL || s2 == NULL)
196
                return -1;
197
        else
198
                return strcmp(s1, s2);
199
}
200

    
201
/* compare paths */
202
gint path_cmp(const gchar *s1, const gchar *s2)
203
{
204
        gint len1, len2;
205

    
206
        if (s1 == NULL || s2 == NULL) return -1;
207
        if (*s1 == '\0' || *s2 == '\0') return -1;
208

    
209
        len1 = strlen(s1);
210
        len2 = strlen(s2);
211

    
212
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
213
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
214

    
215
        return strncmp(s1, s2, MAX(len1, len2));
216
}
217

    
218
/* remove trailing return code */
219
gchar *strretchomp(gchar *str)
220
{
221
        register gchar *s;
222

    
223
        if (!*str) return str;
224

    
225
        for (s = str + strlen(str) - 1;
226
             s >= str && (*s == '\n' || *s == '\r');
227
             s--)
228
                *s = '\0';
229

    
230
        return str;
231
}
232

    
233
/* remove trailing character */
234
gchar *strtailchomp(gchar *str, gchar tail_char)
235
{
236
        register gchar *s;
237

    
238
        if (!*str) return str;
239
        if (tail_char == '\0') return str;
240

    
241
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
242
                *s = '\0';
243

    
244
        return str;
245
}
246

    
247
/* remove CR (carriage return) */
248
gchar *strcrchomp(gchar *str)
249
{
250
        register gchar *s;
251

    
252
        if (!*str) return str;
253

    
254
        s = str + strlen(str) - 1;
255
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
256
                *(s - 1) = '\n';
257
                *s = '\0';
258
        }
259

    
260
        return str;
261
}
262

    
263
/* Similar to `strstr' but this function ignores the case of both strings.  */
264
gchar *strcasestr(const gchar *haystack, const gchar *needle)
265
{
266
        register size_t haystack_len, needle_len;
267

    
268
        haystack_len = strlen(haystack);
269
        needle_len   = strlen(needle);
270

    
271
        if (haystack_len < needle_len || needle_len == 0)
272
                return NULL;
273

    
274
        while (haystack_len >= needle_len) {
275
                if (!strncasecmp(haystack, needle, needle_len))
276
                        return (gchar *)haystack;
277
                else {
278
                        haystack++;
279
                        haystack_len--;
280
                }
281
        }
282

    
283
        return NULL;
284
}
285

    
286
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
287
                   gconstpointer needle, size_t needlelen)
288
{
289
        const gchar *haystack_ = (const gchar *)haystack;
290
        const gchar *needle_ = (const gchar *)needle;
291
        const gchar *haystack_cur = (const gchar *)haystack;
292

    
293
        if (needlelen == 1)
294
                return memchr(haystack_, *needle_, haystacklen);
295

    
296
        while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
297
               != NULL) {
298
                if (haystacklen - (haystack_cur - haystack_) < needlelen)
299
                        break;
300
                if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
301
                        return (gpointer)haystack_cur;
302
                else
303
                        haystack_cur++;
304
        }
305

    
306
        return NULL;
307
}
308

    
309
/* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
310
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
311
{
312
        register gchar c;
313
        gchar *s = dest;
314

    
315
        do {
316
                if (--n == 0) {
317
                        *dest = '\0';
318
                        return s;
319
                }
320
                c = *src++;
321
                *dest++ = c;
322
        } while (c != '\0');
323

    
324
        /* don't do zero fill */
325
        return s;
326
}
327

    
328
#if !HAVE_ISWALNUM
329
int iswalnum(wint_t wc)
330
{
331
        return isalnum((int)wc);
332
}
333
#endif
334

    
335
#if !HAVE_ISWSPACE
336
int iswspace(wint_t wc)
337
{
338
        return isspace((int)wc);
339
}
340
#endif
341

    
342
#if !HAVE_TOWLOWER
343
wint_t towlower(wint_t wc)
344
{
345
        if (wc >= L'A' && wc <= L'Z')
346
                return wc + L'a' - L'A';
347

    
348
        return wc;
349
}
350
#endif
351

    
352
#if !HAVE_WCSLEN
353
size_t wcslen(const wchar_t *s)
354
{
355
        size_t len = 0;
356

    
357
        while (*s != L'\0')
358
                ++len, ++s;
359

    
360
        return len;
361
}
362
#endif
363

    
364
#if !HAVE_WCSCPY
365
/* Copy SRC to DEST.  */
366
wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
367
{
368
        wint_t c;
369
        wchar_t *s = dest;
370

    
371
        do {
372
                c = *src++;
373
                *dest++ = c;
374
        } while (c != L'\0');
375

    
376
        return s;
377
}
378
#endif
379

    
380
#if !HAVE_WCSNCPY
381
/* Copy no more than N wide-characters of SRC to DEST.  */
382
wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
383
{
384
        wint_t c;
385
        wchar_t *s = dest;
386

    
387
        do {
388
                c = *src++;
389
                *dest++ = c;
390
                if (--n == 0)
391
                        return s;
392
        } while (c != L'\0');
393

    
394
        /* zero fill */
395
        do
396
                *dest++ = L'\0';
397
        while (--n > 0);
398

    
399
        return s;
400
}
401
#endif
402

    
403
/* Duplicate S, returning an identical malloc'd string. */
404
wchar_t *wcsdup(const wchar_t *s)
405
{
406
        wchar_t *new_str;
407

    
408
        if (s) {
409
                new_str = g_new(wchar_t, wcslen(s) + 1);
410
                wcscpy(new_str, s);
411
        } else
412
                new_str = NULL;
413

    
414
        return new_str;
415
}
416

    
417
/* Duplicate no more than N wide-characters of S,
418
   returning an identical malloc'd string. */
419
wchar_t *wcsndup(const wchar_t *s, size_t n)
420
{
421
        wchar_t *new_str;
422

    
423
        if (s) {
424
                new_str = g_new(wchar_t, n + 1);
425
                wcsncpy(new_str, s, n);
426
                new_str[n] = (wchar_t)0;
427
        } else
428
                new_str = NULL;
429

    
430
        return new_str;
431
}
432

    
433
wchar_t *strdup_mbstowcs(const gchar *s)
434
{
435
        wchar_t *new_str;
436

    
437
        if (s) {
438
                new_str = g_new(wchar_t, strlen(s) + 1);
439
                if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
440
                        g_free(new_str);
441
                        new_str = NULL;
442
                } else
443
                        new_str = g_realloc(new_str,
444
                                            sizeof(wchar_t) * (wcslen(new_str) + 1));
445
        } else
446
                new_str = NULL;
447

    
448
        return new_str;
449
}
450

    
451
gchar *strdup_wcstombs(const wchar_t *s)
452
{
453
        gchar *new_str;
454
        size_t len;
455

    
456
        if (s) {
457
                len = wcslen(s) * MB_CUR_MAX + 1;
458
                new_str = g_new(gchar, len);
459
                if (wcstombs(new_str, s, len) < 0) {
460
                        g_free(new_str);
461
                        new_str = NULL;
462
                } else
463
                        new_str = g_realloc(new_str, strlen(new_str) + 1);
464
        } else
465
                new_str = NULL;
466

    
467
        return new_str;
468
}
469

    
470
/* Compare S1 and S2, ignoring case.  */
471
gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
472
{
473
        wint_t c1;
474
        wint_t c2;
475

    
476
        while (n--) {
477
                c1 = towlower(*s1++);
478
                c2 = towlower(*s2++);
479
                if (c1 != c2)
480
                        return c1 - c2;
481
                else if (c1 == 0 && c2 == 0)
482
                        break;
483
        }
484

    
485
        return 0;
486
}
487

    
488
/* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
489
wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
490
{
491
        register size_t haystack_len, needle_len;
492

    
493
        haystack_len = wcslen(haystack);
494
        needle_len   = wcslen(needle);
495

    
496
        if (haystack_len < needle_len || needle_len == 0)
497
                return NULL;
498

    
499
        while (haystack_len >= needle_len) {
500
                if (!wcsncasecmp(haystack, needle, needle_len))
501
                        return (wchar_t *)haystack;
502
                else {
503
                        haystack++;
504
                        haystack_len--;
505
                }
506
        }
507

    
508
        return NULL;
509
}
510

    
511
gint get_mbs_len(const gchar *s)
512
{
513
        const gchar *p = s;
514
        gint mb_len;
515
        gint len = 0;
516

    
517
        if (!p)
518
                return -1;
519

    
520
        while (*p != '\0') {
521
                mb_len = mblen(p, MB_LEN_MAX);
522
                if (mb_len == 0)
523
                        break;
524
                else if (mb_len < 0)
525
                        return -1;
526
                else
527
                        len++;
528

    
529
                p += mb_len;
530
        }
531

    
532
        return len;
533
}
534

    
535
/* Examine if next block is non-ASCII string */
536
gboolean is_next_nonascii(const guchar *s)
537
{
538
        const guchar *p;
539

    
540
        /* skip head space */
541
        for (p = s; *p != '\0' && isspace(*p); p++)
542
                ;
543
        for (; *p != '\0' && !isspace(*p); p++) {
544
                if (*p > 127 || *p < 32)
545
                        return TRUE;
546
        }
547

    
548
        return FALSE;
549
}
550

    
551
gint get_next_word_len(const guchar *s)
552
{
553
        gint len = 0;
554

    
555
        for (; *s != '\0' && !isspace(*s); s++, len++)
556
                ;
557

    
558
        return len;
559
}
560

    
561
/* compare subjects */
562
gint subject_compare(const gchar *s1, const gchar *s2)
563
{
564
        gchar *str1, *str2;
565

    
566
        if (!s1 || !s2) return -1;
567
        if (!*s1 || !*s2) return -1;
568

    
569
        Xstrdup_a(str1, s1, return -1);
570
        Xstrdup_a(str2, s2, return -1);
571

    
572
        trim_subject_for_compare(str1);
573
        trim_subject_for_compare(str2);
574

    
575
        if (!*str1 || !*str2) return -1;
576

    
577
        return strcmp(str1, str2);
578
}
579

    
580
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
581
{
582
        gchar *str1, *str2;
583

    
584
        if (!s1 || !s2) return -1;
585

    
586
        Xstrdup_a(str1, s1, return -1);
587
        Xstrdup_a(str2, s2, return -1);
588

    
589
        trim_subject_for_sort(str1);
590
        trim_subject_for_sort(str2);
591

    
592
        return strcasecmp(str1, str2);
593
}
594

    
595
void trim_subject_for_compare(gchar *str)
596
{
597
        guchar *srcp;
598

    
599
        eliminate_parenthesis(str, '[', ']');
600
        eliminate_parenthesis(str, '(', ')');
601
        g_strstrip(str);
602

    
603
        while (!strncasecmp(str, "Re:", 3)) {
604
                srcp = str + 3;
605
                while (isspace(*srcp)) srcp++;
606
                memmove(str, srcp, strlen(srcp) + 1);
607
        }
608
}
609

    
610
void trim_subject_for_sort(gchar *str)
611
{
612
        guchar *srcp;
613

    
614
        g_strstrip(str);
615

    
616
        while (!strncasecmp(str, "Re:", 3)) {
617
                srcp = str + 3;
618
                while (isspace(*srcp)) srcp++;
619
                memmove(str, srcp, strlen(srcp) + 1);
620
        }
621
}
622

    
623
void trim_subject(gchar *str)
624
{
625
        register guchar *srcp, *destp;
626
        gchar op, cl;
627
        gint in_brace;
628

    
629
        destp = str;
630
        while (!strncasecmp(destp, "Re:", 3)) {
631
                destp += 3;
632
                while (isspace(*destp)) destp++;
633
        }
634

    
635
        if (*destp == '[') {
636
                op = '[';
637
                cl = ']';
638
        } else if (*destp == '(') {
639
                op = '(';
640
                cl = ')';
641
        } else
642
                return;
643

    
644
        srcp = destp + 1;
645
        in_brace = 1;
646
        while (*srcp) {
647
                if (*srcp == op)
648
                        in_brace++;
649
                else if (*srcp == cl)
650
                        in_brace--;
651
                srcp++;
652
                if (in_brace == 0)
653
                        break;
654
        }
655
        while (isspace(*srcp)) srcp++;
656
        memmove(destp, srcp, strlen(srcp) + 1);
657
}
658

    
659
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
660
{
661
        register guchar *srcp, *destp;
662
        gint in_brace;
663

    
664
        srcp = destp = str;
665

    
666
        while ((destp = strchr(destp, op))) {
667
                in_brace = 1;
668
                srcp = destp + 1;
669
                while (*srcp) {
670
                        if (*srcp == op)
671
                                in_brace++;
672
                        else if (*srcp == cl)
673
                                in_brace--;
674
                        srcp++;
675
                        if (in_brace == 0)
676
                                break;
677
                }
678
                while (isspace(*srcp)) srcp++;
679
                memmove(destp, srcp, strlen(srcp) + 1);
680
        }
681
}
682

    
683
void extract_parenthesis(gchar *str, gchar op, gchar cl)
684
{
685
        register gchar *srcp, *destp;
686
        gint in_brace;
687

    
688
        srcp = destp = str;
689

    
690
        while ((srcp = strchr(destp, op))) {
691
                if (destp > str)
692
                        *destp++ = ' ';
693
                memmove(destp, srcp + 1, strlen(srcp));
694
                in_brace = 1;
695
                while(*destp) {
696
                        if (*destp == op)
697
                                in_brace++;
698
                        else if (*destp == cl)
699
                                in_brace--;
700

    
701
                        if (in_brace == 0)
702
                                break;
703

    
704
                        destp++;
705
                }
706
        }
707
        *destp = '\0';
708
}
709

    
710
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
711
                                         gchar op, gchar cl)
712
{
713
        register gchar *srcp, *destp;
714
        gint in_brace;
715
        gboolean in_quote = FALSE;
716

    
717
        srcp = destp = str;
718

    
719
        while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
720
                if (destp > str)
721
                        *destp++ = ' ';
722
                memmove(destp, srcp + 1, strlen(srcp));
723
                in_brace = 1;
724
                while(*destp) {
725
                        if (*destp == op && !in_quote)
726
                                in_brace++;
727
                        else if (*destp == cl && !in_quote)
728
                                in_brace--;
729
                        else if (*destp == quote_chr)
730
                                in_quote ^= TRUE;
731

    
732
                        if (in_brace == 0)
733
                                break;
734

    
735
                        destp++;
736
                }
737
        }
738
        *destp = '\0';
739
}
740

    
741
void eliminate_quote(gchar *str, gchar quote_chr)
742
{
743
        register guchar *srcp, *destp;
744

    
745
        srcp = destp = str;
746

    
747
        while ((destp = strchr(destp, quote_chr))) {
748
                if ((srcp = strchr(destp + 1, quote_chr))) {
749
                        srcp++;
750
                        while (isspace(*srcp)) srcp++;
751
                        memmove(destp, srcp, strlen(srcp) + 1);
752
                } else {
753
                        *destp = '\0';
754
                        break;
755
                }
756
        }
757
}
758

    
759
void extract_quote(gchar *str, gchar quote_chr)
760
{
761
        register gchar *p;
762

    
763
        if ((str = strchr(str, quote_chr))) {
764
                if ((p = strchr(str + 1, quote_chr))) {
765
                        *p = '\0';
766
                        memmove(str, str + 1, p - str);
767
                }
768
        }
769
}
770

    
771
void eliminate_address_comment(gchar *str)
772
{
773
        register guchar *srcp, *destp;
774
        gint in_brace;
775

    
776
        srcp = destp = str;
777

    
778
        while ((destp = strchr(destp, '"'))) {
779
                if ((srcp = strchr(destp + 1, '"'))) {
780
                        srcp++;
781
                        if (*srcp == '@') {
782
                                destp = srcp + 1;
783
                        } else {
784
                                while (isspace(*srcp)) srcp++;
785
                                memmove(destp, srcp, strlen(srcp) + 1);
786
                        }
787
                } else {
788
                        *destp = '\0';
789
                        break;
790
                }
791
        }
792

    
793
        srcp = destp = str;
794

    
795
        while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
796
                in_brace = 1;
797
                srcp = destp + 1;
798
                while (*srcp) {
799
                        if (*srcp == '(')
800
                                in_brace++;
801
                        else if (*srcp == ')')
802
                                in_brace--;
803
                        srcp++;
804
                        if (in_brace == 0)
805
                                break;
806
                }
807
                while (isspace(*srcp)) srcp++;
808
                memmove(destp, srcp, strlen(srcp) + 1);
809
        }
810
}
811

    
812
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
813
{
814
        gboolean in_quote = FALSE;
815

    
816
        while (*str) {
817
                if (*str == c && !in_quote)
818
                        return (gchar *)str;
819
                if (*str == quote_chr)
820
                        in_quote ^= TRUE;
821
                str++;
822
        }
823

    
824
        return NULL;
825
}
826

    
827
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
828
{
829
        gboolean in_quote = FALSE;
830
        const gchar *p;
831

    
832
        p = str + strlen(str) - 1;
833
        while (p >= str) {
834
                if (*p == c && !in_quote)
835
                        return (gchar *)p;
836
                if (*p == quote_chr)
837
                        in_quote ^= TRUE;
838
                p--;
839
        }
840

    
841
        return NULL;
842
}
843

    
844
void extract_address(gchar *str)
845
{
846
        eliminate_address_comment(str);
847
        if (strchr_with_skip_quote(str, '"', '<'))
848
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
849
        g_strstrip(str);
850
}
851

    
852
void extract_list_id_str(gchar *str)
853
{
854
        if (strchr_with_skip_quote(str, '"', '<'))
855
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
856
        g_strstrip(str);
857
}
858

    
859
GSList *address_list_append(GSList *addr_list, const gchar *str)
860
{
861
        gchar *work;
862
        gchar *workp;
863

    
864
        if (!str) return addr_list;
865

    
866
        Xstrdup_a(work, str, return addr_list);
867

    
868
        eliminate_address_comment(work);
869
        workp = work;
870

    
871
        while (workp && *workp) {
872
                gchar *p, *next;
873

    
874
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
875
                        *p = '\0';
876
                        next = p + 1;
877
                } else
878
                        next = NULL;
879

    
880
                if (strchr_with_skip_quote(workp, '"', '<'))
881
                        extract_parenthesis_with_skip_quote
882
                                (workp, '"', '<', '>');
883

    
884
                g_strstrip(workp);
885
                if (*workp)
886
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
887

    
888
                workp = next;
889
        }
890

    
891
        return addr_list;
892
}
893

    
894
GSList *references_list_append(GSList *msgid_list, const gchar *str)
895
{
896
        const gchar *strp;
897

    
898
        if (!str) return msgid_list;
899
        strp = str;
900

    
901
        while (strp && *strp) {
902
                const gchar *start, *end;
903
                gchar *msgid;
904

    
905
                if ((start = strchr(strp, '<')) != NULL) {
906
                        end = strchr(start + 1, '>');
907
                        if (!end) break;
908
                } else
909
                        break;
910

    
911
                msgid = g_strndup(start + 1, end - start - 1);
912
                g_strstrip(msgid);
913
                if (*msgid)
914
                        msgid_list = g_slist_append(msgid_list, msgid);
915
                else
916
                        g_free(msgid);
917

    
918
                strp = end + 1;
919
        }
920

    
921
        return msgid_list;
922
}
923

    
924
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
925
{
926
        gchar *work;
927
        gchar *workp;
928

    
929
        if (!str) return group_list;
930

    
931
        Xstrdup_a(work, str, return group_list);
932

    
933
        workp = work;
934

    
935
        while (workp && *workp) {
936
                gchar *p, *next;
937

    
938
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
939
                        *p = '\0';
940
                        next = p + 1;
941
                } else
942
                        next = NULL;
943

    
944
                g_strstrip(workp);
945
                if (*workp)
946
                        group_list = g_slist_append(group_list,
947
                                                    g_strdup(workp));
948

    
949
                workp = next;
950
        }
951

    
952
        return group_list;
953
}
954

    
955
GList *add_history(GList *list, const gchar *str)
956
{
957
        GList *old;
958

    
959
        g_return_val_if_fail(str != NULL, list);
960

    
961
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
962
        if (old) {
963
                g_free(old->data);
964
                list = g_list_remove(list, old->data);
965
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
966
                GList *last;
967

    
968
                last = g_list_last(list);
969
                if (last) {
970
                        g_free(last->data);
971
                        g_list_remove(list, last->data);
972
                }
973
        }
974

    
975
        list = g_list_prepend(list, g_strdup(str));
976

    
977
        return list;
978
}
979

    
980
void remove_return(gchar *str)
981
{
982
        register gchar *p = str;
983

    
984
        while (*p) {
985
                if (*p == '\n' || *p == '\r')
986
                        memmove(p, p + 1, strlen(p));
987
                else
988
                        p++;
989
        }
990
}
991

    
992
void remove_space(gchar *str)
993
{
994
        register guchar *p = str;
995
        register gint spc;
996

    
997
        while (*p) {
998
                spc = 0;
999
                while (isspace(*(p + spc)))
1000
                        spc++;
1001
                if (spc)
1002
                        memmove(p, p + spc, strlen(p + spc) + 1);
1003
                else
1004
                        p++;
1005
        }
1006
}
1007

    
1008
void unfold_line(gchar *str)
1009
{
1010
        register guchar *p = str;
1011
        register gint spc;
1012

    
1013
        while (*p) {
1014
                if (*p == '\n' || *p == '\r') {
1015
                        *p++ = ' ';
1016
                        spc = 0;
1017
                        while (isspace(*(p + spc)))
1018
                                spc++;
1019
                        if (spc)
1020
                                memmove(p, p + spc, strlen(p + spc) + 1);
1021
                } else
1022
                        p++;
1023
        }
1024
}
1025

    
1026
void subst_char(gchar *str, gchar orig, gchar subst)
1027
{
1028
        register gchar *p = str;
1029

    
1030
        while (*p) {
1031
                if (*p == orig)
1032
                        *p = subst;
1033
                p++;
1034
        }
1035
}
1036

    
1037
void subst_chars(gchar *str, gchar *orig, gchar subst)
1038
{
1039
        register gchar *p = str;
1040

    
1041
        while (*p) {
1042
                if (strchr(orig, *p) != NULL)
1043
                        *p = subst;
1044
                p++;
1045
        }
1046
}
1047

    
1048
void subst_for_filename(gchar *str)
1049
{
1050
        subst_chars(str, " \t\r\n\"'/\\", '_');
1051
}
1052

    
1053
gboolean is_header_line(const gchar *str)
1054
{
1055
        if (str[0] == ':') return FALSE;
1056

    
1057
        while (*str != '\0' && *str != ' ') {
1058
                if (*str == ':')
1059
                        return TRUE;
1060
                str++;
1061
        }
1062

    
1063
        return FALSE;
1064
}
1065

    
1066
gboolean is_ascii_str(const guchar *str)
1067
{
1068
        while (*str != '\0') {
1069
                if (*str != '\t' && *str != ' ' &&
1070
                    *str != '\r' && *str != '\n' &&
1071
                    (*str < 32 || *str >= 127))
1072
                        return FALSE;
1073
                str++;
1074
        }
1075

    
1076
        return TRUE;
1077
}
1078

    
1079
gint get_quote_level(const gchar *str)
1080
{
1081
        const guchar *first_pos;
1082
        const guchar *last_pos;
1083
        const guchar *p = str;
1084
        gint quote_level = -1;
1085

    
1086
        /* speed up line processing by only searching to the last '>' */
1087
        if ((first_pos = strchr(str, '>')) != NULL) {
1088
                /* skip a line if it contains a '<' before the initial '>' */
1089
                if (memchr(str, '<', first_pos - (const guchar *)str) != NULL)
1090
                        return -1;
1091
                last_pos = strrchr(first_pos, '>');
1092
        } else
1093
                return -1;
1094

    
1095
        while (p <= last_pos) {
1096
                while (p < last_pos) {
1097
                        if (isspace(*p))
1098
                                p++;
1099
                        else
1100
                                break;
1101
                }
1102

    
1103
                if (*p == '>')
1104
                        quote_level++;
1105
                else if (*p != '-' && !isspace(*p) && p <= last_pos) {
1106
                        /* any characters are allowed except '-' and space */
1107
                        while (*p != '-' && *p != '>' && !isspace(*p) &&
1108
                               p < last_pos)
1109
                                p++;
1110
                        if (*p == '>')
1111
                                quote_level++;
1112
                        else
1113
                                break;
1114
                }
1115

    
1116
                p++;
1117
        }
1118

    
1119
        return quote_level;
1120
}
1121

    
1122
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1123
{
1124
        register guint haystack_len, needle_len;
1125
        gboolean in_squote = FALSE, in_dquote = FALSE;
1126

    
1127
        haystack_len = strlen(haystack);
1128
        needle_len   = strlen(needle);
1129

    
1130
        if (haystack_len < needle_len || needle_len == 0)
1131
                return NULL;
1132

    
1133
        while (haystack_len >= needle_len) {
1134
                if (!in_squote && !in_dquote &&
1135
                    !strncmp(haystack, needle, needle_len))
1136
                        return (gchar *)haystack;
1137

    
1138
                /* 'foo"bar"' -> foo"bar"
1139
                   "foo'bar'" -> foo'bar' */
1140
                if (*haystack == '\'') {
1141
                        if (in_squote)
1142
                                in_squote = FALSE;
1143
                        else if (!in_dquote)
1144
                                in_squote = TRUE;
1145
                } else if (*haystack == '\"') {
1146
                        if (in_dquote)
1147
                                in_dquote = FALSE;
1148
                        else if (!in_squote)
1149
                                in_dquote = TRUE;
1150
                }
1151

    
1152
                haystack++;
1153
                haystack_len--;
1154
        }
1155

    
1156
        return NULL;
1157
}
1158

    
1159
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1160
{
1161
        const gchar *p;
1162
        gchar quote_chr = '"';
1163
        gint in_brace;
1164
        gboolean in_quote = FALSE;
1165

    
1166
        p = str;
1167

    
1168
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1169
                p++;
1170
                in_brace = 1;
1171
                while (*p) {
1172
                        if (*p == op && !in_quote)
1173
                                in_brace++;
1174
                        else if (*p == cl && !in_quote)
1175
                                in_brace--;
1176
                        else if (*p == quote_chr)
1177
                                in_quote ^= TRUE;
1178

    
1179
                        if (in_brace == 0)
1180
                                return (gchar *)p;
1181

    
1182
                        p++;
1183
                }
1184
        }
1185

    
1186
        return NULL;
1187
}
1188

    
1189
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1190
                             gint max_tokens)
1191
{
1192
        GSList *string_list = NULL, *slist;
1193
        gchar **str_array;
1194
        const gchar *s_op, *s_cl;
1195
        guint i, n = 1;
1196

    
1197
        g_return_val_if_fail(str != NULL, NULL);
1198

    
1199
        if (max_tokens < 1)
1200
                max_tokens = G_MAXINT;
1201

    
1202
        s_op = strchr_with_skip_quote(str, '"', op);
1203
        if (!s_op) return NULL;
1204
        str = s_op;
1205
        s_cl = strchr_parenthesis_close(str, op, cl);
1206
        if (s_cl) {
1207
                do {
1208
                        guint len;
1209
                        gchar *new_string;
1210

    
1211
                        str++;
1212
                        len = s_cl - str;
1213
                        new_string = g_new(gchar, len + 1);
1214
                        strncpy(new_string, str, len);
1215
                        new_string[len] = 0;
1216
                        string_list = g_slist_prepend(string_list, new_string);
1217
                        n++;
1218
                        str = s_cl + 1;
1219

    
1220
                        while (*str && isspace(*(guchar *)str)) str++;
1221
                        if (*str != op) {
1222
                                string_list = g_slist_prepend(string_list,
1223
                                                              g_strdup(""));
1224
                                n++;
1225
                                s_op = strchr_with_skip_quote(str, '"', op);
1226
                                if (!--max_tokens || !s_op) break;
1227
                                str = s_op;
1228
                        } else
1229
                                s_op = str;
1230
                        s_cl = strchr_parenthesis_close(str, op, cl);
1231
                } while (--max_tokens && s_cl);
1232
        }
1233

    
1234
        str_array = g_new(gchar*, n);
1235

    
1236
        i = n - 1;
1237

    
1238
        str_array[i--] = NULL;
1239
        for (slist = string_list; slist; slist = slist->next)
1240
                str_array[i--] = slist->data;
1241

    
1242
        g_slist_free(string_list);
1243

    
1244
        return str_array;
1245
}
1246

    
1247
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1248
                            gint max_tokens)
1249
{
1250
        GSList *string_list = NULL, *slist;
1251
        gchar **str_array, *s, *new_str;
1252
        guint i, n = 1, len;
1253

    
1254
        g_return_val_if_fail(str != NULL, NULL);
1255
        g_return_val_if_fail(delim != NULL, NULL);
1256

    
1257
        if (max_tokens < 1)
1258
                max_tokens = G_MAXINT;
1259

    
1260
        s = strstr_with_skip_quote(str, delim);
1261
        if (s) {
1262
                guint delimiter_len = strlen(delim);
1263

    
1264
                do {
1265
                        len = s - str;
1266
                        new_str = g_strndup(str, len);
1267

    
1268
                        if (new_str[0] == '\'' || new_str[0] == '\"') {
1269
                                if (new_str[len - 1] == new_str[0]) {
1270
                                        new_str[len - 1] = '\0';
1271
                                        memmove(new_str, new_str + 1, len - 1);
1272
                                }
1273
                        }
1274
                        string_list = g_slist_prepend(string_list, new_str);
1275
                        n++;
1276
                        str = s + delimiter_len;
1277
                        s = strstr_with_skip_quote(str, delim);
1278
                } while (--max_tokens && s);
1279
        }
1280

    
1281
        if (*str) {
1282
                new_str = g_strdup(str);
1283
                if (new_str[0] == '\'' || new_str[0] == '\"') {
1284
                        len = strlen(str);
1285
                        if (new_str[len - 1] == new_str[0]) {
1286
                                new_str[len - 1] = '\0';
1287
                                memmove(new_str, new_str + 1, len - 1);
1288
                        }
1289
                }
1290
                string_list = g_slist_prepend(string_list, new_str);
1291
                n++;
1292
        }
1293

    
1294
        str_array = g_new(gchar*, n);
1295

    
1296
        i = n - 1;
1297

    
1298
        str_array[i--] = NULL;
1299
        for (slist = string_list; slist; slist = slist->next)
1300
                str_array[i--] = slist->data;
1301

    
1302
        g_slist_free(string_list);
1303

    
1304
        return str_array;
1305
}
1306

    
1307
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1308
{
1309
        gchar *abbrev_group;
1310
        gchar *ap;
1311
        const gchar *p = group;
1312
        const gchar *last;
1313

    
1314
        last = group + strlen(group);
1315
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1316

    
1317
        while (*p) {
1318
                while (*p == '.')
1319
                        *ap++ = *p++;
1320
                if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1321
                        *ap++ = *p++;
1322
                        while (*p != '.') p++;
1323
                } else {
1324
                        strcpy(ap, p);
1325
                        return abbrev_group;
1326
                }
1327
        }
1328

    
1329
        *ap = '\0';
1330
        return abbrev_group;
1331
}
1332

    
1333
gchar *trim_string(const gchar *str, gint len)
1334
{
1335
        const gchar *p = str;
1336
        gint mb_len;
1337
        gchar *new_str;
1338
        gint new_len = 0;
1339

    
1340
        if (!str) return NULL;
1341
        if (strlen(str) <= len)
1342
                return g_strdup(str);
1343

    
1344
        while (*p != '\0') {
1345
                mb_len = mblen(p, MB_LEN_MAX);
1346
                if (mb_len == 0)
1347
                        break;
1348
                else if (mb_len < 0)
1349
                        return g_strdup(str);
1350
                else if (new_len + mb_len > len)
1351
                        break;
1352

    
1353
                new_len += mb_len;
1354
                p += mb_len;
1355
        }
1356

    
1357
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1358
        return g_strconcat(new_str, "...", NULL);
1359
}
1360

    
1361
gchar *trim_string_before(const gchar *str, gint len)
1362
{
1363
        const gchar *p = str;
1364
        gint mb_len;
1365
        gint new_len;
1366

    
1367
        if (!str) return NULL;
1368
        if ((new_len = strlen(str)) <= len)
1369
                return g_strdup(str);
1370

    
1371
        while (*p != '\0') {
1372
                mb_len = mblen(p, MB_LEN_MAX);
1373
                if (mb_len == 0)
1374
                        break;
1375
                else if (mb_len < 0)
1376
                        return g_strdup(str);
1377

    
1378
                new_len -= mb_len;
1379
                p += mb_len;
1380

    
1381
                if (new_len <= len)
1382
                        break;
1383
        }
1384

    
1385
        return g_strconcat("...", p, NULL);
1386
}
1387

    
1388
GList *uri_list_extract_filenames(const gchar *uri_list)
1389
{
1390
        GList *result = NULL;
1391
        const guchar *p, *q;
1392
        gchar *file;
1393

    
1394
        p = uri_list;
1395

    
1396
        while (p) {
1397
                if (*p != '#') {
1398
                        while (isspace(*p)) p++;
1399
                        if (!strncmp(p, "file:", 5)) {
1400
                                p += 5;
1401
                                q = p;
1402
                                while (*q && *q != '\n' && *q != '\r') q++;
1403

    
1404
                                if (q > p) {
1405
                                        q--;
1406
                                        while (q > p && isspace(*q)) q--;
1407
                                        file = g_malloc(q - p + 2);
1408
                                        strncpy(file, p, q - p + 1);
1409
                                        file[q - p + 1] = '\0';
1410
                                        result = g_list_append(result,file);
1411
                                }
1412
                        }
1413
                }
1414
                p = strchr(p, '\n');
1415
                if (p) p++;
1416
        }
1417

    
1418
        return result;
1419
}
1420

    
1421
#define HEX_TO_INT(val, hex) \
1422
{ \
1423
        gchar c = hex; \
1424
 \
1425
        if ('0' <= c && c <= '9') { \
1426
                val = c - '0'; \
1427
        } else if ('a' <= c && c <= 'f') { \
1428
                val = c - 'a' + 10; \
1429
        } else if ('A' <= c && c <= 'F') { \
1430
                val = c - 'A' + 10; \
1431
        } else { \
1432
                val = 0; \
1433
        } \
1434
}
1435

    
1436
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1437
 * characters.
1438
 */
1439
static gint axtoi(const gchar *hex_str)
1440
{
1441
        gint hi, lo;
1442

    
1443
        HEX_TO_INT(hi, hex_str[0]);
1444
        HEX_TO_INT(lo, hex_str[1]);
1445

    
1446
        return (hi << 4) + lo;
1447
}
1448

    
1449
gboolean is_uri_string(const gchar *str)
1450
{
1451
        return (g_strncasecmp(str, "http://", 7) == 0 ||
1452
                g_strncasecmp(str, "https://", 8) == 0 ||
1453
                g_strncasecmp(str, "ftp://", 6) == 0 ||
1454
                g_strncasecmp(str, "www.", 4) == 0);
1455
}
1456

    
1457
gchar *get_uri_path(const gchar *uri)
1458
{
1459
        if (g_strncasecmp(uri, "http://", 7) == 0)
1460
                return (gchar *)(uri + 7);
1461
        else if (g_strncasecmp(uri, "https://", 8) == 0)
1462
                return (gchar *)(uri + 8);
1463
        else if (g_strncasecmp(uri, "ftp://", 6) == 0)
1464
                return (gchar *)(uri + 6);
1465
        else
1466
                return (gchar *)uri;
1467
}
1468

    
1469
/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1470
 * plusses, and escape characters are used)
1471
 */
1472
void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1473
{
1474
        gchar *dec = decoded_uri;
1475
        const gchar *enc = encoded_uri;
1476

    
1477
        while (*enc) {
1478
                if (*enc == '%') {
1479
                        enc++;
1480
                        if (isxdigit((guchar)enc[0]) &&
1481
                            isxdigit((guchar)enc[1])) {
1482
                                *dec = axtoi(enc);
1483
                                dec++;
1484
                                enc += 2;
1485
                        }
1486
                } else {
1487
                        if (*enc == '+')
1488
                                *dec = ' ';
1489
                        else
1490
                                *dec = *enc;
1491
                        dec++;
1492
                        enc++;
1493
                }
1494
        }
1495

    
1496
        *dec = '\0';
1497
}
1498

    
1499
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1500
                     gchar **subject, gchar **body)
1501
{
1502
        gchar *tmp_mailto;
1503
        gchar *p;
1504

    
1505
        Xstrdup_a(tmp_mailto, mailto, return -1);
1506

    
1507
        if (!strncmp(tmp_mailto, "mailto:", 7))
1508
                tmp_mailto += 7;
1509

    
1510
        p = strchr(tmp_mailto, '?');
1511
        if (p) {
1512
                *p = '\0';
1513
                p++;
1514
        }
1515

    
1516
        if (to && !*to)
1517
                *to = g_strdup(tmp_mailto);
1518

    
1519
        while (p) {
1520
                gchar *field, *value;
1521

    
1522
                field = p;
1523

    
1524
                p = strchr(p, '=');
1525
                if (!p) break;
1526
                *p = '\0';
1527
                p++;
1528

    
1529
                value = p;
1530

    
1531
                p = strchr(p, '&');
1532
                if (p) {
1533
                        *p = '\0';
1534
                        p++;
1535
                }
1536

    
1537
                if (*value == '\0') continue;
1538

    
1539
                if (cc && !*cc && !g_strcasecmp(field, "cc")) {
1540
                        *cc = g_strdup(value);
1541
                } else if (bcc && !*bcc && !g_strcasecmp(field, "bcc")) {
1542
                        *bcc = g_strdup(value);
1543
                } else if (subject && !*subject &&
1544
                           !g_strcasecmp(field, "subject")) {
1545
                        *subject = g_malloc(strlen(value) + 1);
1546
                        decode_uri(*subject, value);
1547
                } else if (body && !*body && !g_strcasecmp(field, "body")) {
1548
                        *body = g_malloc(strlen(value) + 1);
1549
                        decode_uri(*body, value);
1550
                }
1551
        }
1552

    
1553
        return 0;
1554
}
1555

    
1556
/*
1557
 * We need this wrapper around g_get_home_dir(), so that
1558
 * we can fix some Windoze things here.  Should be done in glibc of course
1559
 * but as long as we are not able to do our own extensions to glibc, we do 
1560
 * it here.
1561
 */
1562
const gchar *get_home_dir(void)
1563
{
1564
#if HAVE_DOSISH_SYSTEM
1565
    static gchar *home_dir;
1566

    
1567
    if (!home_dir) {
1568
        home_dir = read_w32_registry_string(NULL,
1569
                                            "Software\\Sylpheed", "HomeDir" );
1570
        if (!home_dir || !*home_dir) {
1571
            if (getenv ("HOMEDRIVE") && getenv("HOMEPATH")) {
1572
                const char *s = g_get_home_dir();
1573
                if (s && *s)
1574
                    home_dir = g_strdup (s);
1575
            }
1576
            if (!home_dir || !*home_dir) 
1577
                home_dir = g_strdup ("c:\\sylpheed");
1578
        }
1579
        debug_print("initialized home_dir to `%s'\n", home_dir);
1580
    }
1581
    return home_dir;
1582
#else /* standard glib */
1583
    return g_get_home_dir();
1584
#endif
1585
}
1586

    
1587
const gchar *get_rc_dir(void)
1588
{
1589
        static gchar *rc_dir = NULL;
1590

    
1591
        if (!rc_dir)
1592
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593
                                     RC_DIR, NULL);
1594

    
1595
        return rc_dir;
1596
}
1597

    
1598
const gchar *get_news_cache_dir(void)
1599
{
1600
        static gchar *news_cache_dir = NULL;
1601

    
1602
        if (!news_cache_dir)
1603
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1604
                                             NEWS_CACHE_DIR, NULL);
1605

    
1606
        return news_cache_dir;
1607
}
1608

    
1609
const gchar *get_imap_cache_dir(void)
1610
{
1611
        static gchar *imap_cache_dir = NULL;
1612

    
1613
        if (!imap_cache_dir)
1614
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1615
                                             IMAP_CACHE_DIR, NULL);
1616

    
1617
        return imap_cache_dir;
1618
}
1619

    
1620
const gchar *get_mime_tmp_dir(void)
1621
{
1622
        static gchar *mime_tmp_dir = NULL;
1623

    
1624
        if (!mime_tmp_dir)
1625
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1626
                                           MIME_TMP_DIR, NULL);
1627

    
1628
        return mime_tmp_dir;
1629
}
1630

    
1631
const gchar *get_template_dir(void)
1632
{
1633
        static gchar *template_dir = NULL;
1634

    
1635
        if (!template_dir)
1636
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1637
                                           TEMPLATE_DIR, NULL);
1638

    
1639
        return template_dir;
1640
}
1641

    
1642
const gchar *get_tmp_dir(void)
1643
{
1644
        static gchar *tmp_dir = NULL;
1645

    
1646
        if (!tmp_dir)
1647
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1648
                                      TMP_DIR, NULL);
1649

    
1650
        return tmp_dir;
1651
}
1652

    
1653
gchar *get_tmp_file(void)
1654
{
1655
        gchar *tmp_file;
1656
        static guint32 id = 0;
1657

    
1658
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1659
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
1660

    
1661
        return tmp_file;
1662
}
1663

    
1664
const gchar *get_domain_name(void)
1665
{
1666
        static gchar *domain_name = NULL;
1667

    
1668
        if (!domain_name) {
1669
                gchar buf[128] = "";
1670
                struct hostent *hp;
1671

    
1672
                if (gethostname(buf, sizeof(buf)) < 0) {
1673
                        perror("gethostname");
1674
                        domain_name = "unknown";
1675
                } else {
1676
                        buf[sizeof(buf) - 1] = '\0';
1677
                        if ((hp = my_gethostbyname(buf)) == NULL) {
1678
                                perror("gethostbyname");
1679
                                domain_name = g_strdup(buf);
1680
                        } else {
1681
                                domain_name = g_strdup(hp->h_name);
1682
                        }
1683
                }
1684

    
1685
                debug_print("domain name = %s\n", domain_name);
1686
        }
1687

    
1688
        return domain_name;
1689
}
1690

    
1691
off_t get_file_size(const gchar *file)
1692
{
1693
        struct stat s;
1694

    
1695
        if (stat(file, &s) < 0) {
1696
                FILE_OP_ERROR(file, "stat");
1697
                return -1;
1698
        }
1699

    
1700
        return s.st_size;
1701
}
1702

    
1703
off_t get_file_size_as_crlf(const gchar *file)
1704
{
1705
        FILE *fp;
1706
        off_t size = 0;
1707
        gchar buf[BUFFSIZE];
1708

    
1709
        if ((fp = fopen(file, "rb")) == NULL) {
1710
                FILE_OP_ERROR(file, "fopen");
1711
                return -1;
1712
        }
1713

    
1714
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1715
                strretchomp(buf);
1716
                size += strlen(buf) + 2;
1717
        }
1718

    
1719
        if (ferror(fp)) {
1720
                FILE_OP_ERROR(file, "fgets");
1721
                size = -1;
1722
        }
1723

    
1724
        fclose(fp);
1725

    
1726
        return size;
1727
}
1728

    
1729
off_t get_left_file_size(FILE *fp)
1730
{
1731
        glong pos;
1732
        glong end;
1733
        off_t size;
1734

    
1735
        if ((pos = ftell(fp)) < 0) {
1736
                perror("ftell");
1737
                return -1;
1738
        }
1739
        if (fseek(fp, 0L, SEEK_END) < 0) {
1740
                perror("fseek");
1741
                return -1;
1742
        }
1743
        if ((end = ftell(fp)) < 0) {
1744
                perror("fseek");
1745
                return -1;
1746
        }
1747
        size = end - pos;
1748
        if (fseek(fp, pos, SEEK_SET) < 0) {
1749
                perror("fseek");
1750
                return -1;
1751
        }
1752

    
1753
        return size;
1754
}
1755

    
1756
gboolean file_exist(const gchar *file, gboolean allow_fifo)
1757
{
1758
        struct stat s;
1759

    
1760
        if (file == NULL)
1761
                return FALSE;
1762

    
1763
        if (stat(file, &s) < 0) {
1764
                if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1765
                return FALSE;
1766
        }
1767

    
1768
        if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1769
                return TRUE;
1770

    
1771
        return FALSE;
1772
}
1773

    
1774
gboolean is_dir_exist(const gchar *dir)
1775
{
1776
        struct stat s;
1777

    
1778
        if (dir == NULL)
1779
                return FALSE;
1780

    
1781
        if (stat(dir, &s) < 0) {
1782
                if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1783
                return FALSE;
1784
        }
1785

    
1786
        if (S_ISDIR(s.st_mode))
1787
                return TRUE;
1788

    
1789
        return FALSE;
1790
}
1791

    
1792
gboolean is_file_entry_exist(const gchar *file)
1793
{
1794
        struct stat s;
1795

    
1796
        if (file == NULL)
1797
                return FALSE;
1798

    
1799
        if (stat(file, &s) < 0) {
1800
                if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1801
                return FALSE;
1802
        }
1803

    
1804
        return TRUE;
1805
}
1806

    
1807
gboolean dirent_is_regular_file(struct dirent *d)
1808
{
1809
        struct stat s;
1810

    
1811
#ifdef HAVE_DIRENT_D_TYPE
1812
        if (d->d_type == DT_REG)
1813
                return TRUE;
1814
        else if (d->d_type != DT_UNKNOWN)
1815
                return FALSE;
1816
#endif
1817

    
1818
        return (stat(d->d_name, &s) == 0 && S_ISREG(s.st_mode));
1819
}
1820

    
1821
gboolean dirent_is_directory(struct dirent *d)
1822
{
1823
        struct stat s;
1824

    
1825
#ifdef HAVE_DIRENT_D_TYPE
1826
        if (d->d_type == DT_DIR)
1827
                return TRUE;
1828
        else if (d->d_type != DT_UNKNOWN)
1829
                return FALSE;
1830
#endif
1831

    
1832
        return (stat(d->d_name, &s) == 0 && S_ISDIR(s.st_mode));
1833
}
1834

    
1835
gint change_dir(const gchar *dir)
1836
{
1837
        gchar *prevdir = NULL;
1838

    
1839
        if (debug_mode)
1840
                prevdir = g_get_current_dir();
1841

    
1842
        if (chdir(dir) < 0) {
1843
                FILE_OP_ERROR(dir, "chdir");
1844
                if (debug_mode) g_free(prevdir);
1845
                return -1;
1846
        } else if (debug_mode) {
1847
                gchar *cwd;
1848

    
1849
                cwd = g_get_current_dir();
1850
                if (strcmp(prevdir, cwd) != 0)
1851
                        g_print("current dir: %s\n", cwd);
1852
                g_free(cwd);
1853
                g_free(prevdir);
1854
        }
1855

    
1856
        return 0;
1857
}
1858

    
1859
gint make_dir(const gchar *dir)
1860
{
1861
        if (mkdir(dir, S_IRWXU) < 0) {
1862
                FILE_OP_ERROR(dir, "mkdir");
1863
                return -1;
1864
        }
1865
        if (chmod(dir, S_IRWXU) < 0)
1866
                FILE_OP_ERROR(dir, "chmod");
1867

    
1868
        return 0;
1869
}
1870

    
1871
gint make_dir_hier(const gchar *dir)
1872
{
1873
        gchar *parent_dir;
1874
        const gchar *p;
1875

    
1876
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1877
                parent_dir = g_strndup(dir, p - dir);
1878
                if (*parent_dir != '\0') {
1879
                        if (!is_dir_exist(parent_dir)) {
1880
                                if (make_dir(parent_dir) < 0) {
1881
                                        g_free(parent_dir);
1882
                                        return -1;
1883
                                }
1884
                        }
1885
                }
1886
                g_free(parent_dir);
1887
        }
1888

    
1889
        if (!is_dir_exist(dir)) {
1890
                if (make_dir(dir) < 0)
1891
                        return -1;
1892
        }
1893

    
1894
        return 0;
1895
}
1896

    
1897
gint remove_all_files(const gchar *dir)
1898
{
1899
        DIR *dp;
1900
        struct dirent *d;
1901
        gchar *prev_dir;
1902

    
1903
        prev_dir = g_get_current_dir();
1904

    
1905
        if (chdir(dir) < 0) {
1906
                FILE_OP_ERROR(dir, "chdir");
1907
                g_free(prev_dir);
1908
                return -1;
1909
        }
1910

    
1911
        if ((dp = opendir(".")) == NULL) {
1912
                FILE_OP_ERROR(dir, "opendir");
1913
                g_free(prev_dir);
1914
                return -1;
1915
        }
1916

    
1917
        while ((d = readdir(dp)) != NULL) {
1918
                if (!strcmp(d->d_name, ".") ||
1919
                    !strcmp(d->d_name, ".."))
1920
                        continue;
1921

    
1922
                if (unlink(d->d_name) < 0)
1923
                        FILE_OP_ERROR(d->d_name, "unlink");
1924
        }
1925

    
1926
        closedir(dp);
1927

    
1928
        if (chdir(prev_dir) < 0) {
1929
                FILE_OP_ERROR(prev_dir, "chdir");
1930
                g_free(prev_dir);
1931
                return -1;
1932
        }
1933

    
1934
        g_free(prev_dir);
1935

    
1936
        return 0;
1937
}
1938

    
1939
gint remove_numbered_files(const gchar *dir, guint first, guint last)
1940
{
1941
        DIR *dp;
1942
        struct dirent *d;
1943
        gchar *prev_dir;
1944
        gint fileno;
1945

    
1946
        prev_dir = g_get_current_dir();
1947

    
1948
        if (chdir(dir) < 0) {
1949
                FILE_OP_ERROR(dir, "chdir");
1950
                g_free(prev_dir);
1951
                return -1;
1952
        }
1953

    
1954
        if ((dp = opendir(".")) == NULL) {
1955
                FILE_OP_ERROR(dir, "opendir");
1956
                g_free(prev_dir);
1957
                return -1;
1958
        }
1959

    
1960
        while ((d = readdir(dp)) != NULL) {
1961
                fileno = to_number(d->d_name);
1962
                if (fileno >= 0 && first <= fileno && fileno <= last) {
1963
                        if (is_dir_exist(d->d_name))
1964
                                continue;
1965
                        if (unlink(d->d_name) < 0)
1966
                                FILE_OP_ERROR(d->d_name, "unlink");
1967
                }
1968
        }
1969

    
1970
        closedir(dp);
1971

    
1972
        if (chdir(prev_dir) < 0) {
1973
                FILE_OP_ERROR(prev_dir, "chdir");
1974
                g_free(prev_dir);
1975
                return -1;
1976
        }
1977

    
1978
        g_free(prev_dir);
1979

    
1980
        return 0;
1981
}
1982

    
1983
gint remove_all_numbered_files(const gchar *dir)
1984
{
1985
        return remove_numbered_files(dir, 0, UINT_MAX);
1986
}
1987

    
1988
gint remove_expired_files(const gchar *dir, guint hours)
1989
{
1990
        DIR *dp;
1991
        struct dirent *d;
1992
        struct stat s;
1993
        gchar *prev_dir;
1994
        gint fileno;
1995
        time_t mtime, now, expire_time;
1996

    
1997
        prev_dir = g_get_current_dir();
1998

    
1999
        if (chdir(dir) < 0) {
2000
                FILE_OP_ERROR(dir, "chdir");
2001
                g_free(prev_dir);
2002
                return -1;
2003
        }
2004

    
2005
        if ((dp = opendir(".")) == NULL) {
2006
                FILE_OP_ERROR(dir, "opendir");
2007
                g_free(prev_dir);
2008
                return -1;
2009
        }
2010

    
2011
        now = time(NULL);
2012
        expire_time = hours * 60 * 60;
2013

    
2014
        while ((d = readdir(dp)) != NULL) {
2015
                fileno = to_number(d->d_name);
2016
                if (fileno >= 0) {
2017
                        if (stat(d->d_name, &s) < 0) {
2018
                                FILE_OP_ERROR(d->d_name, "stat");
2019
                                continue;
2020
                        }
2021
                        if (S_ISDIR(s.st_mode))
2022
                                continue;
2023
                        mtime = MAX(s.st_mtime, s.st_atime);
2024
                        if (now - mtime > expire_time) {
2025
                                if (unlink(d->d_name) < 0)
2026
                                        FILE_OP_ERROR(d->d_name, "unlink");
2027
                        }
2028
                }
2029
        }
2030

    
2031
        closedir(dp);
2032

    
2033
        if (chdir(prev_dir) < 0) {
2034
                FILE_OP_ERROR(prev_dir, "chdir");
2035
                g_free(prev_dir);
2036
                return -1;
2037
        }
2038

    
2039
        g_free(prev_dir);
2040

    
2041
        return 0;
2042
}
2043

    
2044
gint remove_dir_recursive(const gchar *dir)
2045
{
2046
        struct stat s;
2047
        DIR *dp;
2048
        struct dirent *d;
2049
        gchar *prev_dir;
2050

    
2051
        /* g_print("dir = %s\n", dir); */
2052

    
2053
        if (stat(dir, &s) < 0) {
2054
                FILE_OP_ERROR(dir, "stat");
2055
                if (ENOENT == errno) return 0;
2056
                return -1;
2057
        }
2058

    
2059
        if (!S_ISDIR(s.st_mode)) {
2060
                if (unlink(dir) < 0) {
2061
                        FILE_OP_ERROR(dir, "unlink");
2062
                        return -1;
2063
                }
2064

    
2065
                return 0;
2066
        }
2067

    
2068
        prev_dir = g_get_current_dir();
2069
        /* g_print("prev_dir = %s\n", prev_dir); */
2070

    
2071
        if (!path_cmp(prev_dir, dir)) {
2072
                g_free(prev_dir);
2073
                if (chdir("..") < 0) {
2074
                        FILE_OP_ERROR(dir, "chdir");
2075
                        return -1;
2076
                }
2077
                prev_dir = g_get_current_dir();
2078
        }
2079

    
2080
        if (chdir(dir) < 0) {
2081
                FILE_OP_ERROR(dir, "chdir");
2082
                g_free(prev_dir);
2083
                return -1;
2084
        }
2085

    
2086
        if ((dp = opendir(".")) == NULL) {
2087
                FILE_OP_ERROR(dir, "opendir");
2088
                chdir(prev_dir);
2089
                g_free(prev_dir);
2090
                return -1;
2091
        }
2092

    
2093
        /* remove all files in the directory */
2094
        while ((d = readdir(dp)) != NULL) {
2095
                if (!strcmp(d->d_name, ".") ||
2096
                    !strcmp(d->d_name, ".."))
2097
                        continue;
2098

    
2099
                /* g_print("removing %s\n", d->d_name); */
2100

    
2101
                if (dirent_is_directory(d)) {
2102
                        if (remove_dir_recursive(d->d_name) < 0) {
2103
                                g_warning("can't remove directory\n");
2104
                                return -1;
2105
                        }
2106
                } else {
2107
                        if (unlink(d->d_name) < 0)
2108
                                FILE_OP_ERROR(d->d_name, "unlink");
2109
                }
2110
        }
2111

    
2112
        closedir(dp);
2113

    
2114
        if (chdir(prev_dir) < 0) {
2115
                FILE_OP_ERROR(prev_dir, "chdir");
2116
                g_free(prev_dir);
2117
                return -1;
2118
        }
2119

    
2120
        g_free(prev_dir);
2121

    
2122
        if (rmdir(dir) < 0) {
2123
                FILE_OP_ERROR(dir, "rmdir");
2124
                return -1;
2125
        }
2126

    
2127
        return 0;
2128
}
2129

    
2130
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2131
{
2132
        FILE *src_fp, *dest_fp;
2133
        gint n_read;
2134
        gchar buf[BUFSIZ];
2135
        gchar *dest_bak = NULL;
2136
        gboolean err = FALSE;
2137

    
2138
        if ((src_fp = fopen(src, "rb")) == NULL) {
2139
                FILE_OP_ERROR(src, "fopen");
2140
                return -1;
2141
        }
2142
        if (is_file_exist(dest)) {
2143
                dest_bak = g_strconcat(dest, ".bak", NULL);
2144
                if (rename(dest, dest_bak) < 0) {
2145
                        FILE_OP_ERROR(dest, "rename");
2146
                        fclose(src_fp);
2147
                        g_free(dest_bak);
2148
                        return -1;
2149
                }
2150
        }
2151

    
2152
        if ((dest_fp = fopen(dest, "wb")) == NULL) {
2153
                FILE_OP_ERROR(dest, "fopen");
2154
                fclose(src_fp);
2155
                if (dest_bak) {
2156
                        if (rename(dest_bak, dest) < 0)
2157
                                FILE_OP_ERROR(dest_bak, "rename");
2158
                        g_free(dest_bak);
2159
                }
2160
                return -1;
2161
        }
2162

    
2163
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2164
                FILE_OP_ERROR(dest, "chmod");
2165
                g_warning(_("can't change file mode\n"));
2166
        }
2167

    
2168
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2169
                if (n_read < sizeof(buf) && ferror(src_fp))
2170
                        break;
2171
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2172
                        g_warning(_("writing to %s failed.\n"), dest);
2173
                        fclose(dest_fp);
2174
                        fclose(src_fp);
2175
                        unlink(dest);
2176
                        if (dest_bak) {
2177
                                if (rename(dest_bak, dest) < 0)
2178
                                        FILE_OP_ERROR(dest_bak, "rename");
2179
                                g_free(dest_bak);
2180
                        }
2181
                        return -1;
2182
                }
2183
        }
2184

    
2185
        if (ferror(src_fp)) {
2186
                FILE_OP_ERROR(src, "fread");
2187
                err = TRUE;
2188
        }
2189
        fclose(src_fp);
2190
        if (fclose(dest_fp) == EOF) {
2191
                FILE_OP_ERROR(dest, "fclose");
2192
                err = TRUE;
2193
        }
2194

    
2195
        if (err) {
2196
                unlink(dest);
2197
                if (dest_bak) {
2198
                        if (rename(dest_bak, dest) < 0)
2199
                                FILE_OP_ERROR(dest_bak, "rename");
2200
                        g_free(dest_bak);
2201
                }
2202
                return -1;
2203
        }
2204

    
2205
        if (keep_backup == FALSE && dest_bak)
2206
                unlink(dest_bak);
2207

    
2208
        g_free(dest_bak);
2209

    
2210
        return 0;
2211
}
2212

    
2213
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2214
{
2215
        if (overwrite == FALSE && is_file_exist(dest)) {
2216
                g_warning("move_file(): file %s already exists.", dest);
2217
                return -1;
2218
        }
2219

    
2220
        if (rename(src, dest) == 0) return 0;
2221

    
2222
        if (EXDEV != errno) {
2223
                FILE_OP_ERROR(src, "rename");
2224
                return -1;
2225
        }
2226

    
2227
        if (copy_file(src, dest, FALSE) < 0) return -1;
2228

    
2229
        unlink(src);
2230

    
2231
        return 0;
2232
}
2233

    
2234
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2235
{
2236
        FILE *dest_fp;
2237
        gint n_read;
2238
        gint bytes_left, to_read;
2239
        gchar buf[BUFSIZ];
2240
        gboolean err = FALSE;
2241

    
2242
        if (fseek(fp, offset, SEEK_SET) < 0) {
2243
                perror("fseek");
2244
                return -1;
2245
        }
2246

    
2247
        if ((dest_fp = fopen(dest, "wb")) == NULL) {
2248
                FILE_OP_ERROR(dest, "fopen");
2249
                return -1;
2250
        }
2251

    
2252
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2253
                FILE_OP_ERROR(dest, "chmod");
2254
                g_warning("can't change file mode\n");
2255
        }
2256

    
2257
        bytes_left = length;
2258
        to_read = MIN(bytes_left, sizeof(buf));
2259

    
2260
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2261
                if (n_read < to_read && ferror(fp))
2262
                        break;
2263
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2264
                        g_warning(_("writing to %s failed.\n"), dest);
2265
                        fclose(dest_fp);
2266
                        unlink(dest);
2267
                        return -1;
2268
                }
2269
                bytes_left -= n_read;
2270
                if (bytes_left == 0)
2271
                        break;
2272
                to_read = MIN(bytes_left, sizeof(buf));
2273
        }
2274

    
2275
        if (ferror(fp)) {
2276
                perror("fread");
2277
                err = TRUE;
2278
        }
2279
        if (fclose(dest_fp) == EOF) {
2280
                FILE_OP_ERROR(dest, "fclose");
2281
                err = TRUE;
2282
        }
2283

    
2284
        if (err) {
2285
                unlink(dest);
2286
                return -1;
2287
        }
2288

    
2289
        return 0;
2290
}
2291

    
2292
/* convert line endings into CRLF. If the last line doesn't end with
2293
 * linebreak, add it.
2294
 */
2295
gchar *canonicalize_str(const gchar *str)
2296
{
2297
        const gchar *p;
2298
        guint new_len = 0;
2299
        gchar *out, *outp;
2300

    
2301
        for (p = str; *p != '\0'; ++p) {
2302
                if (*p != '\r') {
2303
                        ++new_len;
2304
                        if (*p == '\n')
2305
                                ++new_len;
2306
                }
2307
        }
2308
        if (p == str || *(p - 1) != '\n')
2309
                new_len += 2;
2310

    
2311
        out = outp = g_malloc(new_len + 1);
2312
        for (p = str; *p != '\0'; ++p) {
2313
                if (*p != '\r') {
2314
                        if (*p == '\n')
2315
                                *outp++ = '\r';
2316
                        *outp++ = *p;
2317
                }
2318
        }
2319
        if (p == str || *(p - 1) != '\n') {
2320
                *outp++ = '\r';
2321
                *outp++ = '\n';
2322
        }
2323
        *outp = '\0';
2324

    
2325
        return out;
2326
}
2327

    
2328
gint canonicalize_file(const gchar *src, const gchar *dest)
2329
{
2330
        FILE *src_fp, *dest_fp;
2331
        gchar buf[BUFFSIZE];
2332
        gint len;
2333
        gboolean err = FALSE;
2334
        gboolean last_linebreak = FALSE;
2335

    
2336
        if ((src_fp = fopen(src, "rb")) == NULL) {
2337
                FILE_OP_ERROR(src, "fopen");
2338
                return -1;
2339
        }
2340

    
2341
        if ((dest_fp = fopen(dest, "wb")) == NULL) {
2342
                FILE_OP_ERROR(dest, "fopen");
2343
                fclose(src_fp);
2344
                return -1;
2345
        }
2346

    
2347
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2348
                FILE_OP_ERROR(dest, "chmod");
2349
                g_warning("can't change file mode\n");
2350
        }
2351

    
2352
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2353
                gint r = 0;
2354

    
2355
                len = strlen(buf);
2356
                if (len == 0) break;
2357
                last_linebreak = FALSE;
2358

    
2359
                if (buf[len - 1] != '\n') {
2360
                        last_linebreak = TRUE;
2361
                        r = fputs(buf, dest_fp);
2362
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2363
                        r = fputs(buf, dest_fp);
2364
                } else {
2365
                        if (len > 1) {
2366
                                r = fwrite(buf, len - 1, 1, dest_fp);
2367
                                if (r != 1)
2368
                                        r = EOF;
2369
                        }
2370
                        if (r != EOF)
2371
                                r = fputs("\r\n", dest_fp);
2372
                }
2373

    
2374
                if (r == EOF) {
2375
                        g_warning("writing to %s failed.\n", dest);
2376
                        fclose(dest_fp);
2377
                        fclose(src_fp);
2378
                        unlink(dest);
2379
                        return -1;
2380
                }
2381
        }
2382

    
2383
        if (last_linebreak == TRUE) {
2384
                if (fputs("\r\n", dest_fp) == EOF)
2385
                        err = TRUE;
2386
        }
2387

    
2388
        if (ferror(src_fp)) {
2389
                FILE_OP_ERROR(src, "fgets");
2390
                err = TRUE;
2391
        }
2392
        fclose(src_fp);
2393
        if (fclose(dest_fp) == EOF) {
2394
                FILE_OP_ERROR(dest, "fclose");
2395
                err = TRUE;
2396
        }
2397

    
2398
        if (err) {
2399
                unlink(dest);
2400
                return -1;
2401
        }
2402

    
2403
        return 0;
2404
}
2405

    
2406
gint canonicalize_file_replace(const gchar *file)
2407
{
2408
        gchar *tmp_file;
2409

    
2410
        tmp_file = get_tmp_file();
2411

    
2412
        if (canonicalize_file(file, tmp_file) < 0) {
2413
                g_free(tmp_file);
2414
                return -1;
2415
        }
2416

    
2417
        if (move_file(tmp_file, file, TRUE) < 0) {
2418
                g_warning("can't replace %s .\n", file);
2419
                unlink(tmp_file);
2420
                g_free(tmp_file);
2421
                return -1;
2422
        }
2423

    
2424
        g_free(tmp_file);
2425
        return 0;
2426
}
2427

    
2428
gint uncanonicalize_file(const gchar *src, const gchar *dest)
2429
{
2430
        FILE *src_fp, *dest_fp;
2431
        gchar buf[BUFFSIZE];
2432
        gboolean err = FALSE;
2433

    
2434
        if ((src_fp = fopen(src, "rb")) == NULL) {
2435
                FILE_OP_ERROR(src, "fopen");
2436
                return -1;
2437
        }
2438

    
2439
        if ((dest_fp = fopen(dest, "wb")) == NULL) {
2440
                FILE_OP_ERROR(dest, "fopen");
2441
                fclose(src_fp);
2442
                return -1;
2443
        }
2444

    
2445
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2446
                FILE_OP_ERROR(dest, "chmod");
2447
                g_warning("can't change file mode\n");
2448
        }
2449

    
2450
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2451
                strcrchomp(buf);
2452
                if (fputs(buf, dest_fp) == EOF) {
2453
                        g_warning("writing to %s failed.\n", dest);
2454
                        fclose(dest_fp);
2455
                        fclose(src_fp);
2456
                        unlink(dest);
2457
                        return -1;
2458
                }
2459
        }
2460

    
2461
        if (ferror(src_fp)) {
2462
                FILE_OP_ERROR(src, "fgets");
2463
                err = TRUE;
2464
        }
2465
        fclose(src_fp);
2466
        if (fclose(dest_fp) == EOF) {
2467
                FILE_OP_ERROR(dest, "fclose");
2468
                err = TRUE;
2469
        }
2470

    
2471
        if (err) {
2472
                unlink(dest);
2473
                return -1;
2474
        }
2475

    
2476
        return 0;
2477
}
2478

    
2479
gint uncanonicalize_file_replace(const gchar *file)
2480
{
2481
        gchar *tmp_file;
2482

    
2483
        tmp_file = get_tmp_file();
2484

    
2485
        if (uncanonicalize_file(file, tmp_file) < 0) {
2486
                g_free(tmp_file);
2487
                return -1;
2488
        }
2489

    
2490
        if (move_file(tmp_file, file, TRUE) < 0) {
2491
                g_warning("can't replace %s .\n", file);
2492
                unlink(tmp_file);
2493
                g_free(tmp_file);
2494
                return -1;
2495
        }
2496

    
2497
        g_free(tmp_file);
2498
        return 0;
2499
}
2500

    
2501
gchar *normalize_newlines(const gchar *str)
2502
{
2503
        const gchar *p = str;
2504
        gchar *out, *outp;
2505

    
2506
        out = outp = g_malloc(strlen(str) + 1);
2507
        for (p = str; *p != '\0'; ++p) {
2508
                if (*p == '\r') {
2509
                        if (*(p + 1) != '\n')
2510
                                *outp++ = '\n';
2511
                } else
2512
                        *outp++ = *p;
2513
        }
2514

    
2515
        *outp = '\0';
2516

    
2517
        return out;
2518
}
2519

    
2520
gchar *get_outgoing_rfc2822_str(FILE *fp)
2521
{
2522
        gchar buf[BUFFSIZE];
2523
        GString *str;
2524
        gchar *ret;
2525

    
2526
        str = g_string_new(NULL);
2527

    
2528
        /* output header part */
2529
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2530
                strretchomp(buf);
2531
                if (!g_strncasecmp(buf, "Bcc:", 4)) {
2532
                        gint next;
2533

    
2534
                        for (;;) {
2535
                                next = fgetc(fp);
2536
                                if (next == EOF)
2537
                                        break;
2538
                                else if (next != ' ' && next != '\t') {
2539
                                        ungetc(next, fp);
2540
                                        break;
2541
                                }
2542
                                if (fgets(buf, sizeof(buf), fp) == NULL)
2543
                                        break;
2544
                        }
2545
                } else {
2546
                        g_string_append(str, buf);
2547
                        g_string_append(str, "\r\n");
2548
                        if (buf[0] == '\0')
2549
                                break;
2550
                }
2551
        }
2552

    
2553
        /* output body part */
2554
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2555
                strretchomp(buf);
2556
                if (buf[0] == '.')
2557
                        g_string_append_c(str, '.');
2558
                g_string_append(str, buf);
2559
                g_string_append(str, "\r\n");
2560
        }
2561

    
2562
        ret = str->str;
2563
        g_string_free(str, FALSE);
2564

    
2565
        return ret;
2566
}
2567

    
2568
/*
2569
 * Create a new boundary in a way that it is very unlikely that this
2570
 * will occur in the following text.  It would be easy to ensure
2571
 * uniqueness if everything is either quoted-printable or base64
2572
 * encoded (note that conversion is allowed), but because MIME bodies
2573
 * may be nested, it may happen that the same boundary has already
2574
 * been used. We avoid scanning the message for conflicts and hope the
2575
 * best.
2576
 *
2577
 *   boundary := 0*69<bchars> bcharsnospace
2578
 *   bchars := bcharsnospace / " "
2579
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2580
 *                    "+" / "_" / "," / "-" / "." /
2581
 *                    "/" / ":" / "=" / "?"
2582
 *
2583
 * some special characters removed because of buggy MTAs
2584
 */
2585

    
2586
gchar *generate_mime_boundary(const gchar *prefix)
2587
{
2588
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2589
                             "abcdefghijklmnopqrstuvwxyz"
2590
                             "1234567890+_./=";
2591
        gchar buf_uniq[17];
2592
        gchar buf_date[64];
2593
        gint i;
2594
        gint pid;
2595

    
2596
        pid = getpid();
2597

    
2598
        /* We make the boundary depend on the pid, so that all running
2599
         * processes generate different values even when they have been
2600
         * started within the same second and srandom(time(NULL)) has been
2601
         * used.  I can't see whether this is really an advantage but it
2602
         * doesn't do any harm.
2603
         */
2604
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2605
                buf_uniq[i] = tbl[(random() ^ pid) % (sizeof(tbl) - 1)];
2606
        buf_uniq[i] = '\0';
2607

    
2608
        get_rfc822_date(buf_date, sizeof(buf_date));
2609
        subst_char(buf_date, ' ', '_');
2610
        subst_char(buf_date, ',', '_');
2611
        subst_char(buf_date, ':', '_');
2612

    
2613
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
2614
                               buf_date, buf_uniq);
2615
}
2616

    
2617
gint change_file_mode_rw(FILE *fp, const gchar *file)
2618
{
2619
#if HAVE_FCHMOD
2620
        return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2621
#else
2622
        return chmod(file, S_IRUSR|S_IWUSR);
2623
#endif
2624
}
2625

    
2626
FILE *my_tmpfile(void)
2627
{
2628
#if HAVE_MKSTEMP
2629
        const gchar suffix[] = ".XXXXXX";
2630
        const gchar *tmpdir;
2631
        guint tmplen;
2632
        const gchar *progname;
2633
        guint proglen;
2634
        gchar *fname;
2635
        gint fd;
2636
        FILE *fp;
2637

    
2638
        tmpdir = get_tmp_dir();
2639
        tmplen = strlen(tmpdir);
2640
        progname = g_get_prgname();
2641
        proglen = strlen(progname);
2642
        Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2643
                return tmpfile());
2644

    
2645
        memcpy(fname, tmpdir, tmplen);
2646
        fname[tmplen] = G_DIR_SEPARATOR;
2647
        memcpy(fname + tmplen + 1, progname, proglen);
2648
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2649

    
2650
        fd = mkstemp(fname);
2651
        if (fd < 0)
2652
                return tmpfile();
2653

    
2654
        unlink(fname);
2655

    
2656
        fp = fdopen(fd, "w+b");
2657
        if (!fp)
2658
                close(fd);
2659
        else
2660
                return fp;
2661
#endif /* HAVE_MKSTEMP */
2662

    
2663
        return tmpfile();
2664
}
2665

    
2666
FILE *str_open_as_stream(const gchar *str)
2667
{
2668
        FILE *fp;
2669
        size_t len;
2670

    
2671
        g_return_val_if_fail(str != NULL, NULL);
2672

    
2673
        fp = my_tmpfile();
2674
        if (!fp) {
2675
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2676
                return NULL;
2677
        }
2678

    
2679
        len = strlen(str);
2680
        if (len == 0) return fp;
2681

    
2682
        if (fwrite(str, len, 1, fp) != 1) {
2683
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
2684
                fclose(fp);
2685
                return NULL;
2686
        }
2687

    
2688
        rewind(fp);
2689
        return fp;
2690
}
2691

    
2692
gint str_write_to_file(const gchar *str, const gchar *file)
2693
{
2694
        FILE *fp;
2695
        size_t len;
2696

    
2697
        g_return_val_if_fail(str != NULL, -1);
2698
        g_return_val_if_fail(file != NULL, -1);
2699

    
2700
        if ((fp = fopen(file, "wb")) == NULL) {
2701
                FILE_OP_ERROR(file, "fopen");
2702
                return -1;
2703
        }
2704

    
2705
        len = strlen(str);
2706
        if (len == 0) {
2707
                fclose(fp);
2708
                return 0;
2709
        }
2710

    
2711
        if (fwrite(str, len, 1, fp) != 1) {
2712
                FILE_OP_ERROR(file, "fwrite");
2713
                fclose(fp);
2714
                unlink(file);
2715
                return -1;
2716
        }
2717

    
2718
        if (fclose(fp) == EOF) {
2719
                FILE_OP_ERROR(file, "fclose");
2720
                unlink(file);
2721
                return -1;
2722
        }
2723

    
2724
        return 0;
2725
}
2726

    
2727
gchar *file_read_to_str(const gchar *file)
2728
{
2729
        FILE *fp;
2730
        gchar *str;
2731

    
2732
        g_return_val_if_fail(file != NULL, NULL);
2733

    
2734
        if ((fp = fopen(file, "rb")) == NULL) {
2735
                FILE_OP_ERROR(file, "fopen");
2736
                return NULL;
2737
        }
2738

    
2739
        str = file_read_stream_to_str(fp);
2740

    
2741
        fclose(fp);
2742

    
2743
        return str;
2744
}
2745

    
2746
gchar *file_read_stream_to_str(FILE *fp)
2747
{
2748
        GByteArray *array;
2749
        gchar buf[BUFSIZ];
2750
        gint n_read;
2751
        gchar *str;
2752

    
2753
        g_return_val_if_fail(fp != NULL, NULL);
2754

    
2755
        array = g_byte_array_new();
2756

    
2757
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
2758
                if (n_read < sizeof(buf) && ferror(fp))
2759
                        break;
2760
                g_byte_array_append(array, buf, n_read);
2761
        }
2762

    
2763
        if (ferror(fp)) {
2764
                FILE_OP_ERROR("file stream", "fread");
2765
                g_byte_array_free(array, TRUE);
2766
                return NULL;
2767
        }
2768

    
2769
        buf[0] = '\0';
2770
        g_byte_array_append(array, buf, 1);
2771
        str = (gchar *)array->data;
2772
        g_byte_array_free(array, FALSE);
2773

    
2774
        return str;
2775
}
2776

    
2777
gint execute_async(gchar *const argv[])
2778
{
2779
        pid_t pid;
2780
        gint status;
2781

    
2782
        if ((pid = fork()) < 0) {
2783
                perror("fork");
2784
                return -1;
2785
        }
2786

    
2787
        if (pid == 0) {                        /* child process */
2788
                pid_t gch_pid;
2789

    
2790
                if ((gch_pid = fork()) < 0) {
2791
                        perror("fork");
2792
                        _exit(1);
2793
                }
2794

    
2795
                if (gch_pid == 0) {        /* grandchild process */
2796
                        execvp(argv[0], argv);
2797

    
2798
                        perror("execvp");
2799
                        _exit(1);
2800
                }
2801

    
2802
                _exit(0);
2803
        }
2804

    
2805
        waitpid(pid, &status, 0);
2806

    
2807
        if (WIFEXITED(status))
2808
                return WEXITSTATUS(status);
2809
        else
2810
                return -1;
2811
}
2812

    
2813
gint execute_sync(gchar *const argv[])
2814
{
2815
        pid_t pid;
2816
        gint status;
2817

    
2818
        if ((pid = fork()) < 0) {
2819
                perror("fork");
2820
                return -1;
2821
        }
2822

    
2823
        if (pid == 0) {                /* child process */
2824
                execvp(argv[0], argv);
2825

    
2826
                perror("execvp");
2827
                _exit(1);
2828
        }
2829

    
2830
        waitpid(pid, &status, 0);
2831

    
2832
        if (WIFEXITED(status))
2833
                return WEXITSTATUS(status);
2834
        else
2835
                return -1;
2836
}
2837

    
2838
gint execute_command_line(const gchar *cmdline, gboolean async)
2839
{
2840
        gchar **argv;
2841
        gint ret;
2842

    
2843
        debug_print("executing: %s\n", cmdline);
2844

    
2845
        argv = strsplit_with_quote(cmdline, " ", 0);
2846

    
2847
        if (async)
2848
                ret = execute_async(argv);
2849
        else
2850
                ret = execute_sync(argv);
2851

    
2852
        g_strfreev(argv);
2853

    
2854
        return ret;
2855
}
2856

    
2857
gchar *get_command_output(const gchar *cmdline)
2858
{
2859
        gchar buf[BUFFSIZE];
2860
        FILE *fp;
2861
        GString *str;
2862
        gchar *ret;
2863

    
2864
        g_return_val_if_fail(cmdline != NULL, NULL);
2865

    
2866
        if ((fp = popen(cmdline, "r")) == NULL) {
2867
                FILE_OP_ERROR(cmdline, "popen");
2868
                return NULL;
2869
        }
2870

    
2871
        str = g_string_new("");
2872

    
2873
        while (fgets(buf, sizeof(buf), fp) != NULL)
2874
                g_string_append(str, buf);
2875

    
2876
        pclose(fp);
2877

    
2878
        ret = str->str;
2879
        g_string_free(str, FALSE);
2880

    
2881
        return ret;
2882
}
2883

    
2884
gint open_uri(const gchar *uri, const gchar *cmdline)
2885
{
2886
        gchar buf[BUFFSIZE];
2887
        gchar *p;
2888

    
2889
        g_return_val_if_fail(uri != NULL, -1);
2890

    
2891
        if (cmdline &&
2892
            (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
2893
            !strchr(p + 2, '%'))
2894
                g_snprintf(buf, sizeof(buf), cmdline, uri);
2895
        else {
2896
                if (cmdline)
2897
                        g_warning("Open URI command line is invalid "
2898
                                  "(there must be only one '%%s'): %s",
2899
                                  cmdline);
2900
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
2901
        }
2902

    
2903
        execute_command_line(buf, TRUE);
2904

    
2905
        return 0;
2906
}
2907

    
2908
time_t remote_tzoffset_sec(const gchar *zone)
2909
{
2910
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
2911
        gchar zone3[4];
2912
        gchar *p;
2913
        gchar c;
2914
        gint iustz;
2915
        gint offset;
2916
        time_t remoteoffset;
2917

    
2918
        strncpy(zone3, zone, 3);
2919
        zone3[3] = '\0';
2920
        remoteoffset = 0;
2921

    
2922
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
2923
            (c == '+' || c == '-')) {
2924
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
2925
                if (c == '-')
2926
                        remoteoffset = -remoteoffset;
2927
        } else if (!strncmp(zone, "UT" , 2) ||
2928
                   !strncmp(zone, "GMT", 2)) {
2929
                remoteoffset = 0;
2930
        } else if (strlen(zone3) == 3) {
2931
                for (p = ustzstr; *p != '\0'; p += 3) {
2932
                        if (!strncasecmp(p, zone3, 3)) {
2933
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
2934
                                remoteoffset = iustz * 3600;
2935
                                break;
2936
                        }
2937
                }
2938
                if (*p == '\0')
2939
                        return -1;
2940
        } else if (strlen(zone3) == 1) {
2941
                switch (zone[0]) {
2942
                case 'Z': remoteoffset =   0; break;
2943
                case 'A': remoteoffset =  -1; break;
2944
                case 'B': remoteoffset =  -2; break;
2945
                case 'C': remoteoffset =  -3; break;
2946
                case 'D': remoteoffset =  -4; break;
2947
                case 'E': remoteoffset =  -5; break;
2948
                case 'F': remoteoffset =  -6; break;
2949
                case 'G': remoteoffset =  -7; break;
2950
                case 'H': remoteoffset =  -8; break;
2951
                case 'I': remoteoffset =  -9; break;
2952
                case 'K': remoteoffset = -10; break; /* J is not used */
2953
                case 'L': remoteoffset = -11; break;
2954
                case 'M': remoteoffset = -12; break;
2955
                case 'N': remoteoffset =   1; break;
2956
                case 'O': remoteoffset =   2; break;
2957
                case 'P': remoteoffset =   3; break;
2958
                case 'Q': remoteoffset =   4; break;
2959
                case 'R': remoteoffset =   5; break;
2960
                case 'S': remoteoffset =   6; break;
2961
                case 'T': remoteoffset =   7; break;
2962
                case 'U': remoteoffset =   8; break;
2963
                case 'V': remoteoffset =   9; break;
2964
                case 'W': remoteoffset =  10; break;
2965
                case 'X': remoteoffset =  11; break;
2966
                case 'Y': remoteoffset =  12; break;
2967
                default:  remoteoffset =   0; break;
2968
                }
2969
                remoteoffset = remoteoffset * 3600;
2970
        } else
2971
                return -1;
2972

    
2973
        return remoteoffset;
2974
}
2975

    
2976
time_t tzoffset_sec(time_t *now)
2977
{
2978
        struct tm gmt, *lt;
2979
        gint off;
2980

    
2981
        gmt = *gmtime(now);
2982
        lt = localtime(now);
2983

    
2984
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
2985

    
2986
        if (lt->tm_year < gmt.tm_year)
2987
                off -= 24 * 60;
2988
        else if (lt->tm_year > gmt.tm_year)
2989
                off += 24 * 60;
2990
        else if (lt->tm_yday < gmt.tm_yday)
2991
                off -= 24 * 60;
2992
        else if (lt->tm_yday > gmt.tm_yday)
2993
                off += 24 * 60;
2994

    
2995
        if (off >= 24 * 60)                /* should be impossible */
2996
                off = 23 * 60 + 59;        /* if not, insert silly value */
2997
        if (off <= -24 * 60)
2998
                off = -(23 * 60 + 59);
2999

    
3000
        return off * 60;
3001
}
3002

    
3003
/* calculate timezone offset */
3004
gchar *tzoffset(time_t *now)
3005
{
3006
        static gchar offset_string[6];
3007
        struct tm gmt, *lt;
3008
        gint off;
3009
        gchar sign = '+';
3010

    
3011
        gmt = *gmtime(now);
3012
        lt = localtime(now);
3013

    
3014
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3015

    
3016
        if (lt->tm_year < gmt.tm_year)
3017
                off -= 24 * 60;
3018
        else if (lt->tm_year > gmt.tm_year)
3019
                off += 24 * 60;
3020
        else if (lt->tm_yday < gmt.tm_yday)
3021
                off -= 24 * 60;
3022
        else if (lt->tm_yday > gmt.tm_yday)
3023
                off += 24 * 60;
3024

    
3025
        if (off < 0) {
3026
                sign = '-';
3027
                off = -off;
3028
        }
3029

    
3030
        if (off >= 24 * 60)                /* should be impossible */
3031
                off = 23 * 60 + 59;        /* if not, insert silly value */
3032

    
3033
        sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3034

    
3035
        return offset_string;
3036
}
3037

    
3038
void get_rfc822_date(gchar *buf, gint len)
3039
{
3040
        struct tm *lt;
3041
        time_t t;
3042
        gchar day[4], mon[4];
3043
        gint dd, hh, mm, ss, yyyy;
3044

    
3045
        t = time(NULL);
3046
        lt = localtime(&t);
3047

    
3048
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3049
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
3050
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3051
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3052
}
3053

    
3054
static FILE *log_fp = NULL;
3055

    
3056
void set_log_file(const gchar *filename)
3057
{
3058
        if (log_fp) return;
3059
        log_fp = fopen(filename, "wb");
3060
        if (!log_fp)
3061
                FILE_OP_ERROR(filename, "fopen");
3062
}
3063

    
3064
void close_log_file(void)
3065
{
3066
        if (log_fp) {
3067
                fclose(log_fp);
3068
                log_fp = NULL;
3069
        }
3070
}
3071

    
3072
static guint log_verbosity_count = 0;
3073

    
3074
void log_verbosity_set(gboolean verbose)
3075
{
3076
        if (verbose)
3077
                log_verbosity_count++;
3078
        else if (log_verbosity_count > 0)
3079
                log_verbosity_count--;
3080
}
3081

    
3082
void debug_print(const gchar *format, ...)
3083
{
3084
        va_list args;
3085
        gchar buf[BUFFSIZE];
3086

    
3087
        if (!debug_mode) return;
3088

    
3089
        va_start(args, format);
3090
        g_vsnprintf(buf, sizeof(buf), format, args);
3091
        va_end(args);
3092

    
3093
        fputs(buf, stdout);
3094
}
3095

    
3096
#define TIME_LEN        11
3097

    
3098
void log_print(const gchar *format, ...)
3099
{
3100
        va_list args;
3101
        gchar buf[BUFFSIZE + TIME_LEN];
3102
        time_t t;
3103

    
3104
        time(&t);
3105
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3106

    
3107
        va_start(args, format);
3108
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3109
        va_end(args);
3110

    
3111
        if (debug_mode) fputs(buf, stdout);
3112
        log_window_append(buf, LOG_NORMAL);
3113
        if (log_fp) {
3114
                fputs(buf, log_fp);
3115
                fflush(log_fp);
3116
        }
3117
        if (log_verbosity_count)
3118
                statusbar_puts_all(buf + TIME_LEN);
3119
}
3120

    
3121
void log_message(const gchar *format, ...)
3122
{
3123
        va_list args;
3124
        gchar buf[BUFFSIZE + TIME_LEN];
3125
        time_t t;
3126

    
3127
        time(&t);
3128
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3129

    
3130
        va_start(args, format);
3131
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3132
        va_end(args);
3133

    
3134
        if (debug_mode) g_message("%s", buf + TIME_LEN);
3135
        log_window_append(buf + TIME_LEN, LOG_MSG);
3136
        if (log_fp) {
3137
                fwrite(buf, TIME_LEN, 1, log_fp);
3138
                fputs("* message: ", log_fp);
3139
                fputs(buf + TIME_LEN, log_fp);
3140
                fflush(log_fp);
3141
        }
3142
        statusbar_puts_all(buf + TIME_LEN);
3143
}
3144

    
3145
void log_warning(const gchar *format, ...)
3146
{
3147
        va_list args;
3148
        gchar buf[BUFFSIZE + TIME_LEN];
3149
        time_t t;
3150

    
3151
        time(&t);
3152
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3153

    
3154
        va_start(args, format);
3155
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3156
        va_end(args);
3157

    
3158
        g_warning("%s", buf);
3159
        log_window_append(buf + TIME_LEN, LOG_WARN);
3160
        if (log_fp) {
3161
                fwrite(buf, TIME_LEN, 1, log_fp);
3162
                fputs("** warning: ", log_fp);
3163
                fputs(buf + TIME_LEN, log_fp);
3164
                fflush(log_fp);
3165
        }
3166
}
3167

    
3168
void log_error(const gchar *format, ...)
3169
{
3170
        va_list args;
3171
        gchar buf[BUFFSIZE + TIME_LEN];
3172
        time_t t;
3173

    
3174
        time(&t);
3175
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3176

    
3177
        va_start(args, format);
3178
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3179
        va_end(args);
3180

    
3181
        g_warning("%s", buf);
3182
        log_window_append(buf + TIME_LEN, LOG_ERROR);
3183
        if (log_fp) {
3184
                fwrite(buf, TIME_LEN, 1, log_fp);
3185
                fputs("*** error: ", log_fp);
3186
                fputs(buf + TIME_LEN, log_fp);
3187
                fflush(log_fp);
3188
        }
3189
}