Statistics
| Revision:

root / src / utils.c @ 190

History | View | Annotate | Download (59.7 kB)

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