Statistics
| Branch: | Tag: | Revision:

root / libsylph / utils.c @ 8d7dcace

History | View | Annotate | Download (88.4 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2010 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library 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 GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  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
#include <stdlib.h>
33
#include <sys/stat.h>
34
#include <fcntl.h>
35
#include <unistd.h>
36
#include <stdarg.h>
37
#include <sys/types.h>
38
#if HAVE_SYS_WAIT_H
39
#  include <sys/wait.h>
40
#endif
41
#include <dirent.h>
42
#include <time.h>
43
44
#ifdef G_OS_WIN32
45
#ifndef WINVER
46
#  define WINVER 0x0500
47
#endif
48
#  include <windows.h>
49
#  include <wchar.h>
50
#  include <direct.h>
51
#  include <io.h>
52
#  include <shlobj.h>
53
#endif
54
55
#include "utils.h"
56
57
#define BUFFSIZE        8192
58
59
static gboolean debug_mode = FALSE;
60
61
62
#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
63
gint g_chdir(const gchar *path)
64
{
65
#ifdef G_OS_WIN32
66
        if (G_WIN32_HAVE_WIDECHAR_API()) {
67
                wchar_t *wpath;
68
                gint retval;
69
                gint save_errno;
70
71
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
72
                if (wpath == NULL) {
73
                        errno = EINVAL;
74
                        return -1;
75
                }
76
77
                retval = _wchdir(wpath);
78
                save_errno = errno;
79
80
                g_free(wpath);
81
82
                errno = save_errno;
83
                return retval;
84
        } else {
85
                gchar *cp_path;
86
                gint retval;
87
                gint save_errno;
88
89
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
90
                if (cp_path == NULL) {
91
                        errno = EINVAL;
92
                        return -1;
93
                }
94
95
                retval = chdir(cp_path);
96
                save_errno = errno;
97
98
                g_free(cp_path);
99
100
                errno = save_errno;
101
                return retval;
102
        }
103
#else
104
        return chdir(path);
105
#endif
106
}
107
108
gint g_chmod(const gchar *path, gint mode)
109
{
110
#ifdef G_OS_WIN32
111
        if (G_WIN32_HAVE_WIDECHAR_API()) {
112
                wchar_t *wpath;
113
                gint retval;
114
                gint save_errno;
115
116
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
117
                if (wpath == NULL) {
118
                        errno = EINVAL;
119
                        return -1;
120
                }
121
122
                retval = _wchmod(wpath, mode);
123
                save_errno = errno;
124
125
                g_free(wpath);
126
127
                errno = save_errno;
128
                return retval;
129
        } else {
130
                gchar *cp_path;
131
                gint retval;
132
                gint save_errno;
133
134
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
135
                if (cp_path == NULL) {
136
                        errno = EINVAL;
137
                        return -1;
138
                }
139
140
                retval = chmod(cp_path, mode);
141
                save_errno = errno;
142
143
                g_free(cp_path);
144
145
                errno = save_errno;
146
                return retval;
147
        }
148
#else
149
        return chmod(path, mode);
150
#endif
151
}
152
#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
153
154
#ifndef G_OS_UNIX
155
gint syl_link(const gchar *src, const gchar *dest)
156
{
157
#ifdef G_OS_WIN32
158
        wchar_t *wsrc;
159
        wchar_t *wdest;
160
        gint retval;
161
        gint save_errno;
162
163
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
164
        if (wsrc == NULL) {
165
                errno = EINVAL;
166
                return -1;
167
        }
168
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
169
        if (wdest == NULL) {
170
                g_free(wsrc);
171
                errno = EINVAL;
172
                return -1;
173
        }
174
175
        errno = 0;
176
        if (CreateHardLinkW(wdest, wsrc, NULL)) {
177
                retval = 0;
178
                /* debug_print("hard link created: %s -> %s\n", src, dest); */
179
        } else {
180
                retval = -1;
181
                switch (GetLastError()) {
182
                case ERROR_FILE_NOT_FOUND:
183
                case ERROR_PATH_NOT_FOUND:
184
                        errno = ENOENT; break;
185
                case ERROR_ACCESS_DENIED:
186
                case ERROR_LOCK_VIOLATION:
187
                case ERROR_SHARING_VIOLATION:
188
                        errno = EACCES; break;
189
                case ERROR_NOT_SAME_DEVICE:
190
                        errno = EXDEV; break;
191
                case ERROR_FILE_EXISTS:
192
                case ERROR_ALREADY_EXISTS:
193
                        errno = EEXIST; break;
194
                case ERROR_TOO_MANY_LINKS:
195
                        errno = EMLINK; break;
196
                default:
197
                        errno = EIO; break;
198
                }
199
        }
200
        save_errno = errno;
201
202
        g_free(wdest);
203
        g_free(wsrc);
204
205
        errno = save_errno;
206
        return retval;
207
#else
208
        return link(src, dest);
209
#endif
210
}
211
#endif /* !G_OS_UNIX */
212
213
void list_free_strings(GList *list)
214
{
215
        list = g_list_first(list);
216
217
        while (list != NULL) {
218
                g_free(list->data);
219
                list = list->next;
220
        }
221
}
222
223
void slist_free_strings(GSList *list)
224
{
225
        while (list != NULL) {
226
                g_free(list->data);
227
                list = list->next;
228
        }
229
}
230
231
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
232
{
233
        g_free(key);
234
}
235
236
void hash_free_strings(GHashTable *table)
237
{
238
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
239
}
240
241
static void hash_free_value_mem_func(gpointer key, gpointer value,
242
                                     gpointer data)
243
{
244
        g_free(value);
245
}
246
247
void hash_free_value_mem(GHashTable *table)
248
{
249
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
250
}
251
252
gint str_case_equal(gconstpointer v, gconstpointer v2)
253
{
254
        return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
255
}
256
257
guint str_case_hash(gconstpointer key)
258
{
259
        const gchar *p = key;
260
        guint h = *p;
261
262
        if (h) {
263
                h = g_ascii_tolower(h);
264
                for (p += 1; *p != '\0'; p++)
265
                        h = (h << 5) - h + g_ascii_tolower(*p);
266
        }
267
268
        return h;
269
}
270
271
void ptr_array_free_strings(GPtrArray *array)
272
{
273
        gint i;
274
        gchar *str;
275
276
        g_return_if_fail(array != NULL);
277
278
        for (i = 0; i < array->len; i++) {
279
                str = g_ptr_array_index(array, i);
280
                g_free(str);
281
        }
282
}
283
284
gboolean str_find(const gchar *haystack, const gchar *needle)
285
{
286
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
287
}
288
289
gboolean str_case_find(const gchar *haystack, const gchar *needle)
290
{
291
        return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
292
}
293
294
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
295
{
296
        return strcmp(haystack, needle) == 0;
297
}
298
299
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
300
{
301
        return g_ascii_strcasecmp(haystack, needle) == 0;
302
}
303
304
gint to_number(const gchar *nstr)
305
{
306
        register const gchar *p;
307
308
        if (*nstr == '\0') return -1;
309
310
        for (p = nstr; *p != '\0'; p++)
311
                if (!g_ascii_isdigit(*p)) return -1;
312
313
        return atoi(nstr);
314
}
315
316
guint to_unumber(const gchar *nstr)
317
{
318
        register const gchar *p;
319
        gulong val;
320
321
        if (*nstr == '\0') return 0;
322
323
        for (p = nstr; *p != '\0'; p++)
324
                if (!g_ascii_isdigit(*p)) return 0;
325
326
        errno = 0;
327
        val = strtoul(nstr, NULL, 10);
328
        if (val == ULONG_MAX && errno != 0)
329
                val = 0;
330
331
        return (guint)val;
332
}
333
334
/* convert integer into string,
335
   nstr must be not lower than 11 characters length */
336
gchar *itos_buf(gchar *nstr, gint n)
337
{
338
        g_snprintf(nstr, 11, "%d", n);
339
        return nstr;
340
}
341
342
/* convert integer into string */
343
gchar *itos(gint n)
344
{
345
        static gchar nstr[11];
346
347
        return itos_buf(nstr, n);
348
}
349
350
gchar *utos_buf(gchar *nstr, guint n)
351
{
352
        g_snprintf(nstr, 11, "%u", n);
353
        return nstr;
354
}
355
356
gchar *to_human_readable_buf(gchar *buf, size_t bufsize, gint64 size)
357
{
358
        if (size < 1024)
359
                g_snprintf(buf, bufsize, "%dB", (gint)size);
360
        else if ((size >> 10) < 1024)
361
                g_snprintf(buf, bufsize, "%.1fKB", (gfloat)size / (1 << 10));
362
        else if ((size >> 20) < 1024)
363
                g_snprintf(buf, bufsize, "%.2fMB", (gfloat)size / (1 << 20));
364
        else
365
                g_snprintf(buf, bufsize, "%.2fGB", (gfloat)size / (1 << 30));
366
367
        return buf;
368
}
369
370
gchar *to_human_readable(gint64 size)
371
{
372
        static gchar str[16];
373
374
        return to_human_readable_buf(str, sizeof(str), size);
375
}
376
377
/* strcmp with NULL-checking */
378
gint strcmp2(const gchar *s1, const gchar *s2)
379
{
380
        if (s1 == NULL || s2 == NULL)
381
                return -1;
382
        else
383
                return strcmp(s1, s2);
384
}
385
386
/* compare paths */
387
gint path_cmp(const gchar *s1, const gchar *s2)
388
{
389
        gint len1, len2;
390
#ifdef G_OS_WIN32
391
        gchar *s1_, *s2_;
392
#endif
393
394
        if (s1 == NULL || s2 == NULL) return -1;
395
        if (*s1 == '\0' || *s2 == '\0') return -1;
396
397
        len1 = strlen(s1);
398
        len2 = strlen(s2);
399
400
#ifdef G_OS_WIN32
401
        Xstrdup_a(s1_, s1, return -1);
402
        Xstrdup_a(s2_, s2, return -1);
403
        subst_char(s1_, '/', G_DIR_SEPARATOR);
404
        subst_char(s2_, '/', G_DIR_SEPARATOR);
405
        if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--;
406
        if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--;
407
408
        return strncmp(s1_, s2_, MAX(len1, len2));
409
#else
410
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
411
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
412
413
        return strncmp(s1, s2, MAX(len1, len2));
414
#endif
415
}
416
417
/* return TRUE if parent is equal to or ancestor of child */
418
gboolean is_path_parent(const gchar *parent, const gchar *child)
419
{
420
        gint plen;
421
        const gchar *base;
422
423
        g_return_val_if_fail(parent != NULL, FALSE);
424
        g_return_val_if_fail(child != NULL, FALSE);
425
426
        plen = strlen(parent);
427
        while (plen > 0 && G_IS_DIR_SEPARATOR(parent[plen - 1]))
428
                plen--;
429
430
#ifdef G_OS_WIN32
431
        if (!g_ascii_strncasecmp(parent, child, plen)) {
432
#else
433
        if (!strncmp(parent, child, plen)) {
434
#endif
435
                base = child + plen;
436
                if (!G_IS_DIR_SEPARATOR(*base) && *base != '\0')
437
                        return FALSE;
438
                return TRUE;
439
        }
440
441
        return FALSE;
442
}
443
444
/* remove trailing return code */
445
gchar *strretchomp(gchar *str)
446
{
447
        register gchar *s;
448
449
        if (!*str) return str;
450
451
        for (s = str + strlen(str) - 1;
452
             s >= str && (*s == '\n' || *s == '\r');
453
             s--)
454
                *s = '\0';
455
456
        return str;
457
}
458
459
/* remove trailing character */
460
gchar *strtailchomp(gchar *str, gchar tail_char)
461
{
462
        register gchar *s;
463
464
        if (!*str) return str;
465
        if (tail_char == '\0') return str;
466
467
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
468
                *s = '\0';
469
470
        return str;
471
}
472
473
/* remove CR (carriage return) */
474
gchar *strcrchomp(gchar *str)
475
{
476
        register gchar *s;
477
478
        if (!*str) return str;
479
480
        s = str + strlen(str) - 1;
481
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
482
                *(s - 1) = '\n';
483
                *s = '\0';
484
        }
485
486
        return str;
487
}
488
489
/* Similar to `strstr' but this function ignores the case of both strings.  */
490
gchar *strcasestr(const gchar *haystack, const gchar *needle)
491
{
492
        register size_t haystack_len, needle_len;
493
494
        haystack_len = strlen(haystack);
495
        needle_len   = strlen(needle);
496
497
        if (haystack_len < needle_len || needle_len == 0)
498
                return NULL;
499
500
        while (haystack_len >= needle_len) {
501
                if (!g_ascii_strncasecmp(haystack, needle, needle_len))
502
                        return (gchar *)haystack;
503
                else {
504
                        haystack++;
505
                        haystack_len--;
506
                }
507
        }
508
509
        return NULL;
510
}
511
512
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
513
                   gconstpointer needle, size_t needlelen)
514
{
515
        const gchar *haystack_ = (const gchar *)haystack;
516
        const gchar *needle_ = (const gchar *)needle;
517
        const gchar *haystack_cur = (const gchar *)haystack;
518
        size_t haystack_left = haystacklen;
519
520
        if (needlelen == 1)
521
                return memchr(haystack_, *needle_, haystacklen);
522
523
        while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
524
               != NULL) {
525
                if (haystacklen - (haystack_cur - haystack_) < needlelen)
526
                        break;
527
                if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
528
                        return (gpointer)haystack_cur;
529
                else {
530
                        haystack_cur++;
531
                        haystack_left = haystacklen - (haystack_cur - haystack_);
532
                }
533
        }
534
535
        return NULL;
536
}
537
538
/* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
539
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
540
{
541
        register const gchar *s = src;
542
        register gchar *d = dest;
543
544
        while (--n && *s)
545
                *d++ = *s++;
546
        *d = '\0';
547
548
        return dest;
549
}
550
551
/* Similar to g_str_has_suffix() but case-insensitive */
552
gboolean str_has_suffix_case(const gchar *str, const gchar *suffix)
553
{
554
        size_t len, s_len;
555
556
        if (!str || !suffix)
557
                return FALSE;
558
559
        len = strlen(str);
560
        s_len = strlen(suffix);
561
562
        if (s_len > len)
563
                return FALSE;
564
565
        return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0);
566
}
567
568
gint str_find_format_times(const gchar *haystack, gchar ch)
569
{
570
        gint n = 0;
571
        const gchar *p = haystack;
572
573
        while ((p = strchr(p, '%')) != NULL) {
574
                ++p;
575
                if (*p == '%') {
576
                        ++p;
577
                } else if (*p == ch) {
578
                        ++p;
579
                        ++n;
580
                } else
581
                        return -1;
582
        }
583
584
        return n;
585
}
586
587
/* Examine if next block is non-ASCII string */
588
gboolean is_next_nonascii(const gchar *s)
589
{
590
        const gchar *p;
591
        gboolean in_quote = FALSE;
592
593
        /* skip head space */
594
        for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
595
                ;
596
        while (*p != '\0') {
597
                if (!in_quote && g_ascii_isspace(*p))
598
                        break;
599
                if (*p == '"')
600
                        in_quote ^= TRUE;
601
                else if (*(guchar *)p > 127 || *(guchar *)p < 32)
602
                        return TRUE;
603
                ++p;
604
        }
605
606
        return FALSE;
607
}
608
609
gint get_next_word_len(const gchar *s)
610
{
611
        const gchar *p = s;
612
        gboolean in_quote = FALSE;
613
614
        while (*p != '\0') {
615
                if (!in_quote && g_ascii_isspace(*p))
616
                        break;
617
                if (*p == '"')
618
                        in_quote ^= TRUE;
619
                ++p;
620
        }
621
622
        return p - s;
623
}
624
625
/* compare subjects */
626
gint subject_compare(const gchar *s1, const gchar *s2)
627
{
628
        gchar *str1, *str2;
629
630
        if (!s1 || !s2) return -1;
631
        if (!*s1 || !*s2) return -1;
632
633
        Xstrdup_a(str1, s1, return -1);
634
        Xstrdup_a(str2, s2, return -1);
635
636
        trim_subject_for_compare(str1);
637
        trim_subject_for_compare(str2);
638
639
        if (!*str1 || !*str2) return -1;
640
641
        return strcmp(str1, str2);
642
}
643
644
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
645
{
646
        gchar *str1, *str2;
647
648
        if (!s1 || !s2) return -1;
649
650
        Xstrdup_a(str1, s1, return -1);
651
        Xstrdup_a(str2, s2, return -1);
652
653
        trim_subject_for_sort(str1);
654
        trim_subject_for_sort(str2);
655
656
        return g_ascii_strcasecmp(str1, str2);
657
}
658
659
void trim_subject_for_compare(gchar *str)
660
{
661
        gchar *srcp;
662
663
        eliminate_parenthesis(str, '[', ']');
664
        eliminate_parenthesis(str, '(', ')');
665
        g_strstrip(str);
666
667
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
668
                srcp = str + 3;
669
                while (g_ascii_isspace(*srcp)) srcp++;
670
                memmove(str, srcp, strlen(srcp) + 1);
671
        }
672
}
673
674
void trim_subject_for_sort(gchar *str)
675
{
676
        gchar *srcp;
677
678
        g_strstrip(str);
679
680
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
681
                srcp = str + 3;
682
                while (g_ascii_isspace(*srcp)) srcp++;
683
                memmove(str, srcp, strlen(srcp) + 1);
684
        }
685
}
686
687
void trim_subject(gchar *str)
688
{
689
        register gchar *srcp, *destp;
690
        gchar op, cl;
691
        gint in_brace;
692
693
        destp = str;
694
        while (!g_ascii_strncasecmp(destp, "Re:", 3)) {
695
                destp += 3;
696
                while (g_ascii_isspace(*destp)) destp++;
697
        }
698
699
        if (*destp == '[') {
700
                op = '[';
701
                cl = ']';
702
        } else if (*destp == '(') {
703
                op = '(';
704
                cl = ')';
705
        } else
706
                return;
707
708
        srcp = destp + 1;
709
        in_brace = 1;
710
        while (*srcp) {
711
                if (*srcp == op)
712
                        in_brace++;
713
                else if (*srcp == cl)
714
                        in_brace--;
715
                srcp++;
716
                if (in_brace == 0)
717
                        break;
718
        }
719
        while (g_ascii_isspace(*srcp)) srcp++;
720
        memmove(destp, srcp, strlen(srcp) + 1);
721
}
722
723
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
724
{
725
        register gchar *srcp, *destp;
726
        gint in_brace;
727
728
        srcp = destp = str;
729
730
        while ((destp = strchr(destp, op))) {
731
                in_brace = 1;
732
                srcp = destp + 1;
733
                while (*srcp) {
734
                        if (*srcp == op)
735
                                in_brace++;
736
                        else if (*srcp == cl)
737
                                in_brace--;
738
                        srcp++;
739
                        if (in_brace == 0)
740
                                break;
741
                }
742
                while (g_ascii_isspace(*srcp)) srcp++;
743
                memmove(destp, srcp, strlen(srcp) + 1);
744
        }
745
}
746
747
void extract_parenthesis(gchar *str, gchar op, gchar cl)
748
{
749
        register gchar *srcp, *destp;
750
        gint in_brace;
751
752
        srcp = destp = str;
753
754
        while ((srcp = strchr(destp, op))) {
755
                if (destp > str)
756
                        *destp++ = ' ';
757
                memmove(destp, srcp + 1, strlen(srcp));
758
                in_brace = 1;
759
                while(*destp) {
760
                        if (*destp == op)
761
                                in_brace++;
762
                        else if (*destp == cl)
763
                                in_brace--;
764
765
                        if (in_brace == 0)
766
                                break;
767
768
                        destp++;
769
                }
770
        }
771
        *destp = '\0';
772
}
773
774
void extract_parenthesis_with_escape(gchar *str, gchar op, gchar cl)
775
{
776
        register gchar *srcp, *destp;
777
        gint in_brace;
778
779
        srcp = destp = str;
780
781
        while ((srcp = strchr(srcp, op))) {
782
                if (destp > str)
783
                        *destp++ = ' ';
784
                ++srcp;
785
                in_brace = 1;
786
                while (*srcp) {
787
                        if (*srcp == op)
788
                                in_brace++;
789
                        else if (*srcp == cl)
790
                                in_brace--;
791
792
                        if (in_brace == 0)
793
                                break;
794
795
                        if (*srcp == '\\' && *(srcp + 1) != '\0')
796
                                ++srcp;
797
798
                        *destp++ = *srcp++;
799
                }
800
        }
801
        *destp = '\0';
802
}
803
804
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
805
                                         gchar op, gchar cl)
806
{
807
        register gchar *srcp, *destp;
808
        gint in_brace;
809
        gboolean in_quote = FALSE;
810
811
        srcp = destp = str;
812
813
        while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
814
                if (destp > str)
815
                        *destp++ = ' ';
816
                memmove(destp, srcp + 1, strlen(srcp));
817
                in_brace = 1;
818
                while(*destp) {
819
                        if (*destp == op && !in_quote)
820
                                in_brace++;
821
                        else if (*destp == cl && !in_quote)
822
                                in_brace--;
823
                        else if (*destp == quote_chr)
824
                                in_quote ^= TRUE;
825
826
                        if (in_brace == 0)
827
                                break;
828
829
                        destp++;
830
                }
831
        }
832
        *destp = '\0';
833
}
834
835
void eliminate_quote(gchar *str, gchar quote_chr)
836
{
837
        register gchar *srcp, *destp;
838
839
        srcp = destp = str;
840
841
        while ((destp = strchr(destp, quote_chr))) {
842
                if ((srcp = strchr(destp + 1, quote_chr))) {
843
                        srcp++;
844
                        while (g_ascii_isspace(*srcp)) srcp++;
845
                        memmove(destp, srcp, strlen(srcp) + 1);
846
                } else {
847
                        *destp = '\0';
848
                        break;
849
                }
850
        }
851
}
852
853
void extract_quote(gchar *str, gchar quote_chr)
854
{
855
        register gchar *p;
856
857
        if ((str = strchr(str, quote_chr))) {
858
                if ((p = strchr(str + 1, quote_chr))) {
859
                        *p = '\0';
860
                        memmove(str, str + 1, p - str);
861
                }
862
        }
863
}
864
865
void extract_quote_with_escape(gchar *str, gchar quote_chr)
866
{
867
        register gchar *sp, *dp;
868
869
        if ((sp = strchr(str, quote_chr))) {
870
                dp = sp;
871
                ++sp;
872
                while (*sp) {
873
                        if (*sp == quote_chr)
874
                                break;
875
                        else if (*sp == '\\' && *(sp + 1) != '\0')
876
                                ++sp;
877
878
                        *dp++ = *sp++;
879
                }
880
                *dp = '\0';
881
        }
882
}
883
884
void eliminate_address_comment(gchar *str)
885
{
886
        register gchar *srcp, *destp;
887
        gint in_brace;
888
889
        srcp = destp = str;
890
891
        while ((destp = strchr(destp, '"'))) {
892
                if ((srcp = strchr(destp + 1, '"'))) {
893
                        srcp++;
894
                        if (*srcp == '@') {
895
                                destp = srcp + 1;
896
                        } else {
897
                                while (g_ascii_isspace(*srcp)) srcp++;
898
                                memmove(destp, srcp, strlen(srcp) + 1);
899
                        }
900
                } else {
901
                        *destp = '\0';
902
                        break;
903
                }
904
        }
905
906
        srcp = destp = str;
907
908
        while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
909
                in_brace = 1;
910
                srcp = destp + 1;
911
                while (*srcp) {
912
                        if (*srcp == '(')
913
                                in_brace++;
914
                        else if (*srcp == ')')
915
                                in_brace--;
916
                        srcp++;
917
                        if (in_brace == 0)
918
                                break;
919
                }
920
                while (g_ascii_isspace(*srcp)) srcp++;
921
                memmove(destp, srcp, strlen(srcp) + 1);
922
        }
923
}
924
925
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
926
{
927
        gboolean in_quote = FALSE;
928
929
        while (*str) {
930
                if (*str == c && !in_quote)
931
                        return (gchar *)str;
932
                if (*str == quote_chr)
933
                        in_quote ^= TRUE;
934
                str++;
935
        }
936
937
        return NULL;
938
}
939
940
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
941
{
942
        gboolean in_quote = FALSE;
943
        const gchar *p;
944
945
        p = str + strlen(str) - 1;
946
        while (p >= str) {
947
                if (*p == c && !in_quote)
948
                        return (gchar *)p;
949
                if (*p == quote_chr)
950
                        in_quote ^= TRUE;
951
                p--;
952
        }
953
954
        return NULL;
955
}
956
957
void extract_address(gchar *str)
958
{
959
        eliminate_address_comment(str);
960
        if (strchr_with_skip_quote(str, '"', '<'))
961
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
962
        g_strstrip(str);
963
}
964
965
void extract_list_id_str(gchar *str)
966
{
967
        if (strchr_with_skip_quote(str, '"', '<'))
968
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
969
        g_strstrip(str);
970
}
971
972
gchar *extract_addresses(const gchar *str)
973
{
974
        GString *new_str;
975
        GSList *addr_list, *cur;
976
977
        if (!str)
978
                return NULL;
979
980
        addr_list = address_list_append(NULL, str);
981
982
        new_str = g_string_new(NULL);
983
984
        for (cur = addr_list; cur != NULL; cur = cur->next) {
985
                g_string_append(new_str, (gchar *)cur->data);
986
                if (cur->next)
987
                        g_string_append(new_str, ", ");
988
        }
989
990
        slist_free_strings(addr_list);
991
        g_slist_free(addr_list);
992
993
        return g_string_free(new_str, FALSE);
994
}
995
996
gchar *normalize_address_field(const gchar *str)
997
{
998
        GString *new_str;
999
        GSList *addr_list, *cur;
1000
        gchar *addr, *p, *q, *r;
1001
        gchar *ret_str;
1002
1003
        addr_list = address_list_append_orig(NULL, str);
1004
1005
        new_str = g_string_new(NULL);
1006
1007
        for (cur = addr_list; cur != NULL; cur = cur->next) {
1008
                p = addr = (gchar *)cur->data;
1009
                q = strchr_with_skip_quote(p, '"', '<');
1010
                if (q && q > p) {
1011
                        r = q - 1;
1012
                        while (r > p && g_ascii_isspace(*r))
1013
                                --r;
1014
                        g_string_append_len(new_str, p, r - p + 1);
1015
                        g_string_append_c(new_str, ' ');
1016
                        p = q;
1017
                }
1018
                if (*p == '<') {
1019
                        q = strchr(p, '>');
1020
                        if (q) {
1021
                                r = q + 1;
1022
                                if (*r) {
1023
                                        while (g_ascii_isspace(*r))
1024
                                                ++r;
1025
                                        g_string_append(new_str, r);
1026
                                        if (new_str->len > 0 &&
1027
                                            !g_ascii_isspace
1028
                                                (new_str->str[new_str->len - 1]))
1029
                                                g_string_append_c(new_str, ' ');
1030
                                }
1031
                                g_string_append_len(new_str, p, q - p + 1);
1032
                        } else {
1033
                                g_string_append(new_str, p);
1034
                                g_string_append_c(new_str, '>');
1035
                        }
1036
                } else
1037
                        g_string_append(new_str, p);
1038
1039
                if (cur->next)
1040
                        g_string_append(new_str, ", ");
1041
        }
1042
1043
        slist_free_strings(addr_list);
1044
        ret_str = new_str->str;
1045
        g_string_free(new_str, FALSE);
1046
1047
        return ret_str;
1048
}
1049
1050
gboolean address_equal(const gchar *addr1, const gchar *addr2)
1051
{
1052
        gchar *addr1_, *addr2_;
1053
1054
        if (!addr1 || !addr2)
1055
                return FALSE;
1056
1057
        Xstrdup_a(addr1_, addr1, return FALSE);
1058
        Xstrdup_a(addr2_, addr2, return FALSE);
1059
1060
        extract_address(addr1_);
1061
        extract_address(addr2_);
1062
1063
        return strcmp(addr1_, addr2_) == 0;
1064
}
1065
1066
GSList *address_list_append_orig(GSList *addr_list, const gchar *str)
1067
{
1068
        const gchar *p = str, *q;
1069
        gchar *addr;
1070
1071
        if (!str) return addr_list;
1072
1073
        while (*p) {
1074
                if (*p == ',' || g_ascii_isspace(*p)) {
1075
                        ++p;
1076
                } else if ((q = strchr_with_skip_quote(p, '"', ','))) {
1077
                        addr = g_strndup(p, q - p);
1078
                        g_strstrip(addr);
1079
                        addr_list = g_slist_append(addr_list, addr);
1080
                        p = q + 1;
1081
                } else {
1082
                        addr = g_strdup(p);
1083
                        g_strstrip(addr);
1084
                        addr_list = g_slist_append(addr_list, addr);
1085
                        break;
1086
                }
1087
        }
1088
1089
        return addr_list;
1090
}
1091
1092
GSList *address_list_append(GSList *addr_list, const gchar *str)
1093
{
1094
        gchar *work;
1095
        gchar *workp;
1096
1097
        if (!str) return addr_list;
1098
1099
        Xstrdup_a(work, str, return addr_list);
1100
1101
        eliminate_address_comment(work);
1102
        workp = work;
1103
1104
        while (workp && *workp) {
1105
                gchar *p, *next;
1106
1107
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1108
                        *p = '\0';
1109
                        next = p + 1;
1110
                } else
1111
                        next = NULL;
1112
1113
                if (strchr_with_skip_quote(workp, '"', '<'))
1114
                        extract_parenthesis_with_skip_quote
1115
                                (workp, '"', '<', '>');
1116
1117
                g_strstrip(workp);
1118
                if (*workp)
1119
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
1120
1121
                workp = next;
1122
        }
1123
1124
        return addr_list;
1125
}
1126
1127
GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1128
{
1129
        const gchar *strp;
1130
1131
        if (!str) return msgid_list;
1132
        strp = str;
1133
1134
        while (strp && *strp) {
1135
                const gchar *start, *end;
1136
                gchar *msgid;
1137
1138
                if ((start = strchr(strp, '<')) != NULL) {
1139
                        end = strchr(start + 1, '>');
1140
                        if (!end) break;
1141
                } else
1142
                        break;
1143
1144
                msgid = g_strndup(start + 1, end - start - 1);
1145
                g_strstrip(msgid);
1146
                if (*msgid)
1147
                        msgid_list = g_slist_prepend(msgid_list, msgid);
1148
                else
1149
                        g_free(msgid);
1150
1151
                strp = end + 1;
1152
        }
1153
1154
        return msgid_list;
1155
}
1156
1157
GSList *references_list_append(GSList *msgid_list, const gchar *str)
1158
{
1159
        GSList *list;
1160
1161
        list = references_list_prepend(NULL, str);
1162
        list = g_slist_reverse(list);
1163
        msgid_list = g_slist_concat(msgid_list, list);
1164
1165
        return msgid_list;
1166
}
1167
1168
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1169
{
1170
        gchar *work;
1171
        gchar *workp;
1172
1173
        if (!str) return group_list;
1174
1175
        Xstrdup_a(work, str, return group_list);
1176
1177
        workp = work;
1178
1179
        while (workp && *workp) {
1180
                gchar *p, *next;
1181
1182
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1183
                        *p = '\0';
1184
                        next = p + 1;
1185
                } else
1186
                        next = NULL;
1187
1188
                g_strstrip(workp);
1189
                if (*workp)
1190
                        group_list = g_slist_append(group_list,
1191
                                                    g_strdup(workp));
1192
1193
                workp = next;
1194
        }
1195
1196
        return group_list;
1197
}
1198
1199
GList *add_history(GList *list, const gchar *str)
1200
{
1201
        GList *old;
1202
1203
        g_return_val_if_fail(str != NULL, list);
1204
1205
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1206
        if (old) {
1207
                g_free(old->data);
1208
                list = g_list_remove(list, old->data);
1209
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1210
                GList *last;
1211
1212
                last = g_list_last(list);
1213
                if (last) {
1214
                        g_free(last->data);
1215
                        list = g_list_remove(list, last->data);
1216
                }
1217
        }
1218
1219
        list = g_list_prepend(list, g_strdup(str));
1220
1221
        return list;
1222
}
1223
1224
void remove_return(gchar *str)
1225
{
1226
        register gchar *p = str;
1227
1228
        while (*p) {
1229
                if (*p == '\n' || *p == '\r')
1230
                        memmove(p, p + 1, strlen(p));
1231
                else
1232
                        p++;
1233
        }
1234
}
1235
1236
void remove_space(gchar *str)
1237
{
1238
        register gchar *p = str;
1239
        register gint spc;
1240
1241
        while (*p) {
1242
                spc = 0;
1243
                while (g_ascii_isspace(*(p + spc)))
1244
                        spc++;
1245
                if (spc)
1246
                        memmove(p, p + spc, strlen(p + spc) + 1);
1247
                else
1248
                        p++;
1249
        }
1250
}
1251
1252
void unfold_line(gchar *str)
1253
{
1254
        register gchar *p = str;
1255
        register gint spc;
1256
1257
        while (*p) {
1258
                if (*p == '\n' || *p == '\r') {
1259
                        *p++ = ' ';
1260
                        spc = 0;
1261
                        while (g_ascii_isspace(*(p + spc)))
1262
                                spc++;
1263
                        if (spc)
1264
                                memmove(p, p + spc, strlen(p + spc) + 1);
1265
                } else
1266
                        p++;
1267
        }
1268
}
1269
1270
void subst_char(gchar *str, gchar orig, gchar subst)
1271
{
1272
        register gchar *p = str;
1273
1274
        while (*p) {
1275
                if (*p == orig)
1276
                        *p = subst;
1277
                p++;
1278
        }
1279
}
1280
1281
void subst_chars(gchar *str, gchar *orig, gchar subst)
1282
{
1283
        register gchar *p = str;
1284
1285
        while (*p) {
1286
                if (strchr(orig, *p) != NULL)
1287
                        *p = subst;
1288
                ++p;
1289
        }
1290
}
1291
1292
void subst_null(gchar *str, gint len, gchar subst)
1293
{
1294
        register gchar *p = str;
1295
1296
        while (len--) {
1297
                if (*p == '\0')
1298
                        *p = subst;
1299
                ++p;
1300
        }
1301
}
1302
1303
void subst_control(gchar *str, gchar subst)
1304
{
1305
        register gchar *p = str;
1306
1307
        while (*p) {
1308
                if (g_ascii_iscntrl(*p))
1309
                        *p = subst;
1310
                ++p;
1311
        }
1312
}
1313
1314
void subst_for_filename(gchar *str)
1315
{
1316
        subst_chars(str, " \t\r\n\"'\\/:;*?<>|", '_');
1317
}
1318
1319
gchar *get_alt_filename(const gchar *filename, gint count)
1320
{
1321
        const gchar *ext;
1322
        gchar *alt_filename;
1323
1324
        ext = strrchr(filename, '.');
1325
1326
        if (ext) {
1327
                gchar *base;
1328
1329
                base = g_strndup(filename, ext - filename);
1330
                alt_filename = g_strdup_printf("%s-%d%s", base, count, ext);
1331
                g_free(base);
1332
        } else
1333
                alt_filename = g_strdup_printf("%s-%d", filename, count);
1334
1335
        return alt_filename;
1336
}
1337
1338
gboolean is_header_line(const gchar *str)
1339
{
1340
        if (str[0] == ':') return FALSE;
1341
1342
        while (*str != '\0' && *str != ' ') {
1343
                if (*str == ':')
1344
                        return TRUE;
1345
                str++;
1346
        }
1347
1348
        return FALSE;
1349
}
1350
1351
gboolean is_ascii_str(const gchar *str)
1352
{
1353
        const guchar *p = (const guchar *)str;
1354
1355
        while (*p != '\0') {
1356
                if (*p != '\t' && *p != ' ' &&
1357
                    *p != '\r' && *p != '\n' &&
1358
                    (*p < 32 || *p >= 127))
1359
                        return FALSE;
1360
                p++;
1361
        }
1362
1363
        return TRUE;
1364
}
1365
1366
gint get_quote_level(const gchar *str)
1367
{
1368
        const gchar *first_pos;
1369
        const gchar *last_pos;
1370
        const gchar *p = str;
1371
        gint quote_level = -1;
1372
1373
        /* speed up line processing by only searching to the last '>' */
1374
        if ((first_pos = strchr(str, '>')) != NULL) {
1375
                /* skip a line if it contains a '<' before the initial '>' */
1376
                if (memchr(str, '<', first_pos - str) != NULL)
1377
                        return -1;
1378
                last_pos = strrchr(first_pos, '>');
1379
        } else
1380
                return -1;
1381
1382
        while (p <= last_pos) {
1383
                while (p < last_pos) {
1384
                        if (g_ascii_isspace(*p))
1385
                                p++;
1386
                        else
1387
                                break;
1388
                }
1389
1390
                if (*p == '>')
1391
                        quote_level++;
1392
                else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1393
                        /* any characters are allowed except '-' and space */
1394
                        while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) &&
1395
                               p < last_pos)
1396
                                p++;
1397
                        if (*p == '>')
1398
                                quote_level++;
1399
                        else
1400
                                break;
1401
                }
1402
1403
                p++;
1404
        }
1405
1406
        return quote_level;
1407
}
1408
1409
gint check_line_length(const gchar *str, gint max_chars, gint *line)
1410
{
1411
        const gchar *p = str, *q;
1412
        gint cur_line = 0, len;
1413
1414
        while ((q = strchr(p, '\n')) != NULL) {
1415
                len = q - p + 1;
1416
                if (len > max_chars) {
1417
                        if (line)
1418
                                *line = cur_line;
1419
                        return -1;
1420
                }
1421
                p = q + 1;
1422
                ++cur_line;
1423
        }
1424
1425
        len = strlen(p);
1426
        if (len > max_chars) {
1427
                if (line)
1428
                        *line = cur_line;
1429
                return -1;
1430
        }
1431
1432
        return 0;
1433
}
1434
1435
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1436
{
1437
        register guint haystack_len, needle_len;
1438
        gboolean in_squote = FALSE, in_dquote = FALSE;
1439
1440
        haystack_len = strlen(haystack);
1441
        needle_len   = strlen(needle);
1442
1443
        if (haystack_len < needle_len || needle_len == 0)
1444
                return NULL;
1445
1446
        while (haystack_len >= needle_len) {
1447
                if (!in_squote && !in_dquote &&
1448
                    !strncmp(haystack, needle, needle_len))
1449
                        return (gchar *)haystack;
1450
1451
                /* 'foo"bar"' -> foo"bar"
1452
                   "foo'bar'" -> foo'bar' */
1453
                if (*haystack == '\'') {
1454
                        if (in_squote)
1455
                                in_squote = FALSE;
1456
                        else if (!in_dquote)
1457
                                in_squote = TRUE;
1458
                } else if (*haystack == '\"') {
1459
                        if (in_dquote)
1460
                                in_dquote = FALSE;
1461
                        else if (!in_squote)
1462
                                in_dquote = TRUE;
1463
                }
1464
1465
                haystack++;
1466
                haystack_len--;
1467
        }
1468
1469
        return NULL;
1470
}
1471
1472
gchar *strcasestr_with_skip_quote(const gchar *haystack, const gchar *needle)
1473
{
1474
        register guint haystack_len, needle_len;
1475
        gboolean in_squote = FALSE, in_dquote = FALSE;
1476
1477
        haystack_len = strlen(haystack);
1478
        needle_len   = strlen(needle);
1479
1480
        if (haystack_len < needle_len || needle_len == 0)
1481
                return NULL;
1482
1483
        while (haystack_len >= needle_len) {
1484
                if (!in_squote && !in_dquote &&
1485
                    !g_ascii_strncasecmp(haystack, needle, needle_len))
1486
                        return (gchar *)haystack;
1487
1488
                /* 'foo"bar"' -> foo"bar"
1489
                   "foo'bar'" -> foo'bar' */
1490
                if (*haystack == '\'') {
1491
                        if (in_squote)
1492
                                in_squote = FALSE;
1493
                        else if (!in_dquote)
1494
                                in_squote = TRUE;
1495
                } else if (*haystack == '\"') {
1496
                        if (in_dquote)
1497
                                in_dquote = FALSE;
1498
                        else if (!in_squote)
1499
                                in_dquote = TRUE;
1500
                }
1501
1502
                haystack++;
1503
                haystack_len--;
1504
        }
1505
1506
        return NULL;
1507
}
1508
1509
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1510
{
1511
        const gchar *p;
1512
        gchar quote_chr = '"';
1513
        gint in_brace;
1514
        gboolean in_quote = FALSE;
1515
1516
        p = str;
1517
1518
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1519
                p++;
1520
                in_brace = 1;
1521
                while (*p) {
1522
                        if (*p == op && !in_quote)
1523
                                in_brace++;
1524
                        else if (*p == cl && !in_quote)
1525
                                in_brace--;
1526
                        else if (*p == quote_chr)
1527
                                in_quote ^= TRUE;
1528
1529
                        if (in_brace == 0)
1530
                                return (gchar *)p;
1531
1532
                        p++;
1533
                }
1534
        }
1535
1536
        return NULL;
1537
}
1538
1539
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1540
                             gint max_tokens)
1541
{
1542
        GSList *string_list = NULL, *slist;
1543
        gchar **str_array;
1544
        const gchar *s_op, *s_cl;
1545
        guint i, n = 1;
1546
1547
        g_return_val_if_fail(str != NULL, NULL);
1548
1549
        if (max_tokens < 1)
1550
                max_tokens = G_MAXINT;
1551
1552
        s_op = strchr_with_skip_quote(str, '"', op);
1553
        if (!s_op) return NULL;
1554
        str = s_op;
1555
        s_cl = strchr_parenthesis_close(str, op, cl);
1556
        if (s_cl) {
1557
                do {
1558
                        guint len;
1559
                        gchar *new_string;
1560
1561
                        str++;
1562
                        len = s_cl - str;
1563
                        new_string = g_new(gchar, len + 1);
1564
                        strncpy(new_string, str, len);
1565
                        new_string[len] = 0;
1566
                        string_list = g_slist_prepend(string_list, new_string);
1567
                        n++;
1568
                        str = s_cl + 1;
1569
1570
                        while (*str && g_ascii_isspace(*str)) str++;
1571
                        if (*str != op) {
1572
                                string_list = g_slist_prepend(string_list,
1573
                                                              g_strdup(""));
1574
                                n++;
1575
                                s_op = strchr_with_skip_quote(str, '"', op);
1576
                                if (!--max_tokens || !s_op) break;
1577
                                str = s_op;
1578
                        } else
1579
                                s_op = str;
1580
                        s_cl = strchr_parenthesis_close(str, op, cl);
1581
                } while (--max_tokens && s_cl);
1582
        }
1583
1584
        str_array = g_new(gchar*, n);
1585
1586
        i = n - 1;
1587
        str_array[i--] = NULL;
1588
        for (slist = string_list; slist; slist = slist->next)
1589
                str_array[i--] = slist->data;
1590
1591
        g_slist_free(string_list);
1592
1593
        return str_array;
1594
}
1595
1596
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1597
                            gint max_tokens)
1598
{
1599
        GSList *string_list = NULL, *slist;
1600
        gchar **str_array, *s, *new_str;
1601
        guint i, n = 1, len;
1602
1603
        g_return_val_if_fail(str != NULL, NULL);
1604
        g_return_val_if_fail(delim != NULL, NULL);
1605
1606
        if (max_tokens < 1)
1607
                max_tokens = G_MAXINT;
1608
1609
        s = strstr_with_skip_quote(str, delim);
1610
        if (s) {
1611
                guint delimiter_len = strlen(delim);
1612
1613
                do {
1614
                        len = s - str;
1615
                        new_str = g_strndup(str, len);
1616
1617
                        if (new_str[0] == '\'' || new_str[0] == '\"') {
1618
                                if (new_str[len - 1] == new_str[0]) {
1619
                                        new_str[len - 1] = '\0';
1620
                                        memmove(new_str, new_str + 1, len - 1);
1621
                                }
1622
                        }
1623
                        string_list = g_slist_prepend(string_list, new_str);
1624
                        n++;
1625
                        str = s + delimiter_len;
1626
                        s = strstr_with_skip_quote(str, delim);
1627
                } while (--max_tokens && s);
1628
        }
1629
1630
        if (*str) {
1631
                new_str = g_strdup(str);
1632
                if (new_str[0] == '\'' || new_str[0] == '\"') {
1633
                        len = strlen(str);
1634
                        if (new_str[len - 1] == new_str[0]) {
1635
                                new_str[len - 1] = '\0';
1636
                                memmove(new_str, new_str + 1, len - 1);
1637
                        }
1638
                }
1639
                string_list = g_slist_prepend(string_list, new_str);
1640
                n++;
1641
        }
1642
1643
        str_array = g_new(gchar*, n);
1644
1645
        i = n - 1;
1646
        str_array[i--] = NULL;
1647
        for (slist = string_list; slist; slist = slist->next)
1648
                str_array[i--] = slist->data;
1649
1650
        g_slist_free(string_list);
1651
1652
        return str_array;
1653
}
1654
1655
gchar **strsplit_csv(const gchar *str, gchar delim, gint max_tokens)
1656
{
1657
        GSList *string_list = NULL, *slist;
1658
        gchar **str_array, *s, *new_str;
1659
        gchar *tmp, *tmpp, *p;
1660
        guint i, n = 1, len;
1661
1662
        g_return_val_if_fail(str != NULL, NULL);
1663
1664
        if (max_tokens < 1)
1665
                max_tokens = G_MAXINT;
1666
1667
        s = strchr_with_skip_quote(str, '"', delim);
1668
        if (s) {
1669
                do {
1670
                        len = s - str;
1671
                        tmpp = tmp = g_strndup(str, len);
1672
1673
                        if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1674
                                tmp[len - 1] = '\0';
1675
                                ++tmpp;
1676
                                p = new_str = g_malloc(len - 1);
1677
                                while (*tmpp) {
1678
                                        if (*tmpp == '"' && *(tmpp + 1) == '"')
1679
                                                ++tmpp;
1680
                                        *p++ = *tmpp++;
1681
                                }
1682
                                *p = '\0';
1683
                                g_free(tmp);
1684
                        } else
1685
                                new_str = tmp;
1686
1687
                        string_list = g_slist_prepend(string_list, new_str);
1688
                        n++;
1689
                        str = s + 1;
1690
                        s = strchr_with_skip_quote(str, '"', delim);
1691
                } while (--max_tokens && s);
1692
        }
1693
1694
        if (*str) {
1695
                len = strlen(str);
1696
                tmpp = tmp = g_strdup(str);
1697
1698
                if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1699
                        tmp[len - 1] = '\0';
1700
                        ++tmpp;
1701
                        p = new_str = g_malloc(len - 1);
1702
                        while (*tmpp) {
1703
                                if (*tmpp == '"' && *(tmpp + 1) == '"')
1704
                                        ++tmpp;
1705
                                *p++ = *tmpp++;
1706
                        }
1707
                        *p = '\0';
1708
                        g_free(tmp);
1709
                } else
1710
                        new_str = tmp;
1711
1712
                string_list = g_slist_prepend(string_list, new_str);
1713
                n++;
1714
        }
1715
1716
        str_array = g_new(gchar*, n);
1717
1718
        i = n - 1;
1719
        str_array[i--] = NULL;
1720
        for (slist = string_list; slist; slist = slist->next)
1721
                str_array[i--] = slist->data;
1722
1723
        g_slist_free(string_list);
1724
1725
        return str_array;
1726
}
1727
1728
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1729
{
1730
        gchar *abbrev_group;
1731
        gchar *ap;
1732
        const gchar *p = group;
1733
        const gchar *last;
1734
1735
        last = group + strlen(group);
1736
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1737
1738
        while (*p) {
1739
                while (*p == '.')
1740
                        *ap++ = *p++;
1741
                if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1742
                        *ap++ = *p++;
1743
                        while (*p != '.') p++;
1744
                } else {
1745
                        strcpy(ap, p);
1746
                        return abbrev_group;
1747
                }
1748
        }
1749
1750
        *ap = '\0';
1751
        return abbrev_group;
1752
}
1753
1754
gchar *trim_string(const gchar *str, gint len)
1755
{
1756
        const gchar *p = str;
1757
        gint mb_len;
1758
        gchar *new_str;
1759
        gint new_len = 0;
1760
1761
        if (!str) return NULL;
1762
        if (strlen(str) <= len)
1763
                return g_strdup(str);
1764
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1765
                return g_strdup(str);
1766
1767
        while (*p != '\0') {
1768
                mb_len = g_utf8_skip[*(guchar *)p];
1769
                if (mb_len == 0)
1770
                        break;
1771
                else if (new_len + mb_len > len)
1772
                        break;
1773
1774
                new_len += mb_len;
1775
                p += mb_len;
1776
        }
1777
1778
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1779
        return g_strconcat(new_str, "...", NULL);
1780
}
1781
1782
gchar *trim_string_before(const gchar *str, gint len)
1783
{
1784
        const gchar *p = str;
1785
        gint mb_len;
1786
        gint new_len;
1787
1788
        if (!str) return NULL;
1789
        if ((new_len = strlen(str)) <= len)
1790
                return g_strdup(str);
1791
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1792
                return g_strdup(str);
1793
1794
        while (*p != '\0') {
1795
                mb_len = g_utf8_skip[*(guchar *)p];
1796
                if (mb_len == 0)
1797
                        break;
1798
1799
                new_len -= mb_len;
1800
                p += mb_len;
1801
1802
                if (new_len <= len)
1803
                        break;
1804
        }
1805
1806
        return g_strconcat("...", p, NULL);
1807
}
1808
1809
GList *uri_list_extract_filenames(const gchar *uri_list)
1810
{
1811
        GList *result = NULL;
1812
        gchar *file;
1813
1814
#if GLIB_CHECK_VERSION(2, 6, 0)
1815
        gchar **uris;
1816
        gint i;
1817
1818
        uris = g_uri_list_extract_uris(uri_list);
1819
        g_return_val_if_fail(uris != NULL, NULL);
1820
1821
        for (i = 0; uris[i] != NULL; i++) {
1822
                file = g_filename_from_uri(uris[i], NULL, NULL);
1823
                if (file)
1824
                        result = g_list_append(result, file);
1825
        }
1826
1827
        g_strfreev(uris);
1828
1829
        return result;
1830
#else
1831
        const gchar *p, *q;
1832
1833
        p = uri_list;
1834
1835
        while (p) {
1836
                if (*p != '#') {
1837
                        while (g_ascii_isspace(*p)) p++;
1838
                        if (!strncmp(p, "file:", 5)) {
1839
                                p += 5;
1840
                                while (*p == '/' && *(p + 1) == '/') p++;
1841
                                q = p;
1842
                                while (*q && *q != '\n' && *q != '\r') q++;
1843
1844
                                if (q > p) {
1845
                                        q--;
1846
                                        while (q > p && g_ascii_isspace(*q))
1847
                                                q--;
1848
                                        file = g_malloc(q - p + 2);
1849
                                        strncpy(file, p, q - p + 1);
1850
                                        file[q - p + 1] = '\0';
1851
                                        decode_uri(file, file);
1852
                                        result = g_list_append(result, file);
1853
                                }
1854
                        }
1855
                }
1856
                p = strchr(p, '\n');
1857
                if (p) p++;
1858
        }
1859
1860
        return result;
1861
#endif
1862
}
1863
1864
#define HEX_TO_INT(val, hex) \
1865
{ \
1866
        gchar c = hex; \
1867
 \
1868
        if ('0' <= c && c <= '9') { \
1869
                val = c - '0'; \
1870
        } else if ('a' <= c && c <= 'f') { \
1871
                val = c - 'a' + 10; \
1872
        } else if ('A' <= c && c <= 'F') { \
1873
                val = c - 'A' + 10; \
1874
        } else { \
1875
                val = 0; \
1876
        } \
1877
}
1878
1879
#define INT_TO_HEX(hex, val)                \
1880
{                                        \
1881
        if ((val) < 10)                        \
1882
                hex = '0' + (val);        \
1883
        else                                \
1884
                hex = 'a' + (val) - 10;        \
1885
}
1886
1887
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1888
 * characters.
1889
 */
1890
static gint axtoi(const gchar *hex_str)
1891
{
1892
        gint hi, lo;
1893
1894
        HEX_TO_INT(hi, hex_str[0]);
1895
        HEX_TO_INT(lo, hex_str[1]);
1896
1897
        return (hi << 4) + lo;
1898
}
1899
1900
static void get_hex_str(gchar *out, guchar ch)
1901
{
1902
        gchar hex;
1903
1904
        INT_TO_HEX(hex, ch >> 4);
1905
        *out++ = hex;
1906
        INT_TO_HEX(hex, ch & 0x0f);
1907
        *out++ = hex;
1908
}
1909
1910
gboolean is_uri_string(const gchar *str)
1911
{
1912
        return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1913
                g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1914
                g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1915
                g_ascii_strncasecmp(str, "www.", 4) == 0);
1916
}
1917
1918
gchar *get_uri_path(const gchar *uri)
1919
{
1920
        if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1921
                return (gchar *)(uri + 7);
1922
        else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1923
                return (gchar *)(uri + 8);
1924
        else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1925
                return (gchar *)(uri + 6);
1926
        else
1927
                return (gchar *)uri;
1928
}
1929
1930
gint get_uri_len(const gchar *str)
1931
{
1932
        const gchar *p;
1933
1934
        if (is_uri_string(str)) {
1935
                for (p = str; *p != '\0'; p++) {
1936
                        if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1937
                                break;
1938
                }
1939
                return p - str;
1940
        }
1941
1942
        return 0;
1943
}
1944
1945
/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1946
 * plusses, and escape characters are used)
1947
 * Note: decoded_uri and encoded_uri can point the same location
1948
 */
1949
void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1950
{
1951
        gchar *dec = decoded_uri;
1952
        const gchar *enc = encoded_uri;
1953
1954
        while (*enc) {
1955
                if (*enc == '%') {
1956
                        enc++;
1957
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1958
                            g_ascii_isxdigit((guchar)enc[1])) {
1959
                                *dec = axtoi(enc);
1960
                                dec++;
1961
                                enc += 2;
1962
                        }
1963
                } else {
1964
                        if (*enc == '+')
1965
                                *dec = ' ';
1966
                        else
1967
                                *dec = *enc;
1968
                        dec++;
1969
                        enc++;
1970
                }
1971
        }
1972
1973
        *dec = '\0';
1974
}
1975
1976
void decode_xdigit_encoded_str(gchar *decoded, const gchar *encoded)
1977
{
1978
        gchar *dec = decoded;
1979
        const gchar *enc = encoded;
1980
1981
        while (*enc) {
1982
                if (*enc == '%') {
1983
                        enc++;
1984
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1985
                            g_ascii_isxdigit((guchar)enc[1])) {
1986
                                *dec++ = axtoi(enc);
1987
                                enc += 2;
1988
                        }
1989
                } else
1990
                        *dec++ = *enc++;
1991
        }
1992
1993
        *dec = '\0';
1994
}
1995
1996
gchar *encode_uri(const gchar *filename)
1997
{
1998
        gchar *uri;
1999
2000
        uri = g_filename_to_uri(filename, NULL, NULL);
2001
        if (!uri)
2002
                uri = g_strconcat("file://", filename, NULL);
2003
2004
        return uri;
2005
}
2006
2007
gchar *uriencode_for_filename(const gchar *filename)
2008
{
2009
        const gchar *p = filename;
2010
        gchar *enc, *outp;
2011
2012
        outp = enc = g_malloc(strlen(filename) * 3 + 1);
2013
2014
        for (p = filename; *p != '\0'; p++) {
2015
                if (strchr("\t\r\n\"'\\/:;*?<>|", *p)) {
2016
                        *outp++ = '%';
2017
                        get_hex_str(outp, *p);
2018
                        outp += 2;
2019
                } else
2020
                        *outp++ = *p;
2021
        }
2022
2023
        *outp = '\0';
2024
        return enc;
2025
}
2026
2027
gchar *uriencode_for_mailto(const gchar *mailto)
2028
{
2029
        const gchar *p = mailto;
2030
        gchar *enc, *outp;
2031
2032
        outp = enc = g_malloc(strlen(mailto) * 3 + 1);
2033
2034
        for (p = mailto; *p != '\0'; p++) {
2035
                if (*p == '+') {
2036
                        *outp++ = '%';
2037
                        get_hex_str(outp, *p);
2038
                        outp += 2;
2039
                } else
2040
                        *outp++ = *p;
2041
        }
2042
2043
        *outp = '\0';
2044
        return enc;
2045
}
2046
2047
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
2048
                     gchar **subject, gchar **inreplyto, gchar **body)
2049
{
2050
        gchar *tmp_mailto;
2051
        gchar *p;
2052
2053
        Xstrdup_a(tmp_mailto, mailto, return -1);
2054
2055
        if (!strncmp(tmp_mailto, "mailto:", 7))
2056
                tmp_mailto += 7;
2057
2058
        p = strchr(tmp_mailto, '?');
2059
        if (p) {
2060
                *p = '\0';
2061
                p++;
2062
        }
2063
2064
        if (to && !*to) {
2065
                *to = g_malloc(strlen(tmp_mailto) + 1);
2066
                decode_uri(*to, tmp_mailto);
2067
        }
2068
2069
        while (p) {
2070
                gchar *field, *value;
2071
2072
                field = p;
2073
2074
                p = strchr(p, '=');
2075
                if (!p) break;
2076
                *p = '\0';
2077
                p++;
2078
2079
                value = p;
2080
2081
                p = strchr(p, '&');
2082
                if (p) {
2083
                        *p = '\0';
2084
                        p++;
2085
                }
2086
2087
                if (*value == '\0') continue;
2088
2089
                if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
2090
                        *cc = g_malloc(strlen(value) + 1);
2091
                        decode_uri(*cc, value);
2092
                } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
2093
                        *bcc = g_malloc(strlen(value) + 1);
2094
                        decode_uri(*bcc, value);
2095
                } else if (subject && !*subject &&
2096
                           !g_ascii_strcasecmp(field, "subject")) {
2097
                        *subject = g_malloc(strlen(value) + 1);
2098
                        decode_uri(*subject, value);
2099
                } else if (inreplyto && !*inreplyto &&
2100
                           !g_ascii_strcasecmp(field, "in-reply-to")) {
2101
                        *inreplyto = g_malloc(strlen(value) + 1);
2102
                        decode_uri(*inreplyto, value);
2103
                } else if (body && !*body &&
2104
                           !g_ascii_strcasecmp(field, "body")) {
2105
                        *body = g_malloc(strlen(value) + 1);
2106
                        decode_uri(*body, value);
2107
                }
2108
        }
2109
2110
        return 0;
2111
}
2112
2113
static gchar *startup_dir = NULL;
2114
static gchar *rc_dir = NULL;
2115
2116
void set_startup_dir(void)
2117
{
2118
#ifdef G_OS_WIN32
2119
        if (!startup_dir) {
2120
                startup_dir = g_win32_get_package_installation_directory
2121
                        (NULL, NULL);
2122
                if (startup_dir) {
2123
                        if (g_chdir(startup_dir) < 0) {
2124
                                FILE_OP_ERROR(startup_dir, "chdir");
2125
                                g_free(startup_dir);
2126
                                startup_dir = g_get_current_dir();
2127
                        }
2128
                } else
2129
                        startup_dir = g_get_current_dir();
2130
        }
2131
#else
2132
        if (!startup_dir)
2133
                startup_dir = g_get_current_dir();
2134
#endif
2135
}
2136
2137
void set_rc_dir(const gchar *dir)
2138
{
2139
        if (rc_dir)
2140
                g_free(rc_dir);
2141
2142
        if (dir) {
2143
                if (g_path_is_absolute(dir))
2144
                        rc_dir = g_strdup(dir);
2145
                else
2146
                        rc_dir = g_strconcat(get_startup_dir(),
2147
                                             G_DIR_SEPARATOR_S, dir, NULL);
2148
        } else
2149
                rc_dir = NULL;
2150
}
2151
2152
const gchar *get_startup_dir(void)
2153
{
2154
        if (!startup_dir)
2155
                set_startup_dir();
2156
2157
        return startup_dir;
2158
}
2159
2160
#ifdef G_OS_WIN32
2161
static gchar *get_win32_special_folder_path(gint nfolder)
2162
{
2163
        gchar *folder = NULL;
2164
        HRESULT hr;
2165
2166
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2167
                wchar_t path[MAX_PATH + 1];
2168
                hr = SHGetFolderPathW(NULL, nfolder, NULL, 0, path);
2169
                if (hr == S_OK)
2170
                        folder = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
2171
        } else {
2172
                gchar path[MAX_PATH + 1];
2173
                hr = SHGetFolderPathA(NULL, nfolder, NULL, 0, path);
2174
                if (hr == S_OK)
2175
                        folder = g_locale_to_utf8(path, -1, NULL, NULL, NULL);
2176
        }
2177
2178
        return folder;
2179
}
2180
#endif
2181
2182
const gchar *get_home_dir(void)
2183
{
2184
#ifdef G_OS_WIN32
2185
        static const gchar *home_dir = NULL;
2186
2187
        if (!home_dir) {
2188
                home_dir = g_get_home_dir();
2189
                if (!home_dir)
2190
                        home_dir = "C:\\Sylpheed";
2191
        }
2192
2193
        return home_dir;
2194
#else
2195
        return g_get_home_dir();
2196
#endif
2197
}
2198
2199
const gchar *get_document_dir(void)
2200
{
2201
#ifdef G_OS_WIN32
2202
        static const gchar *document_dir = NULL;
2203
        HRESULT hr;
2204
2205
        if (!document_dir) {
2206
                document_dir = get_win32_special_folder_path(CSIDL_PERSONAL);
2207
                if (!document_dir)
2208
                        document_dir = get_home_dir();
2209
        }
2210
2211
        return document_dir;
2212
#elif defined(__APPLE__)
2213
        static const gchar *document_dir = NULL;
2214
2215
        if (!document_dir) {
2216
                document_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2217
                                           "Documents", NULL);
2218
        }
2219
2220
        return document_dir;
2221
#else
2222
        return get_home_dir();
2223
#endif
2224
}
2225
2226
const gchar *get_rc_dir(void)
2227
{
2228
        if (!rc_dir) {
2229
#ifdef G_OS_WIN32
2230
                gchar *appdata;
2231
2232
                appdata = get_win32_special_folder_path(CSIDL_APPDATA);
2233
                if (appdata)
2234
                        rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S,
2235
                                             RC_DIR, NULL);
2236
                else
2237
                        rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2238
                                             RC_DIR, NULL);
2239
                g_free(appdata);
2240
#elif defined(__APPLE__)
2241
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2242
                                     "Library", G_DIR_SEPARATOR_S,
2243
                                     "Application Support", G_DIR_SEPARATOR_S,
2244
                                     RC_DIR, NULL);
2245
#else
2246
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2247
                                     RC_DIR, NULL);
2248
#endif
2249
        }
2250
2251
        return rc_dir;
2252
}
2253
2254
const gchar *get_old_rc_dir(void)
2255
{
2256
        static gchar *old_rc_dir = NULL;
2257
2258
        if (!old_rc_dir)
2259
                old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2260
                                         OLD_RC_DIR, NULL);
2261
2262
        return old_rc_dir;
2263
}
2264
2265
const gchar *get_mail_base_dir(void)
2266
{
2267
#if defined(G_OS_WIN32) || defined(__APPLE__)
2268
        static gchar *mail_base_dir = NULL;
2269
2270
        if (!mail_base_dir)
2271
                mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2272
                                            "Mailboxes", NULL);
2273
2274
        return mail_base_dir;
2275
#else
2276
        return get_home_dir();
2277
#endif
2278
}
2279
2280
const gchar *get_news_cache_dir(void)
2281
{
2282
        static gchar *news_cache_dir = NULL;
2283
2284
        if (!news_cache_dir)
2285
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2286
                                             NEWS_CACHE_DIR, NULL);
2287
2288
        return news_cache_dir;
2289
}
2290
2291
const gchar *get_imap_cache_dir(void)
2292
{
2293
        static gchar *imap_cache_dir = NULL;
2294
2295
        if (!imap_cache_dir)
2296
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2297
                                             IMAP_CACHE_DIR, NULL);
2298
2299
        return imap_cache_dir;
2300
}
2301
2302
const gchar *get_mime_tmp_dir(void)
2303
{
2304
        static gchar *mime_tmp_dir = NULL;
2305
2306
        if (!mime_tmp_dir)
2307
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2308
                                           MIME_TMP_DIR, NULL);
2309
2310
        return mime_tmp_dir;
2311
}
2312
2313
const gchar *get_template_dir(void)
2314
{
2315
        static gchar *template_dir = NULL;
2316
2317
        if (!template_dir)
2318
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2319
                                           TEMPLATE_DIR, NULL);
2320
2321
        return template_dir;
2322
}
2323
2324
const gchar *get_tmp_dir(void)
2325
{
2326
        static gchar *tmp_dir = NULL;
2327
2328
        if (!tmp_dir)
2329
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2330
                                      TMP_DIR, NULL);
2331
2332
        return tmp_dir;
2333
}
2334
2335
gchar *get_tmp_file(void)
2336
{
2337
        gchar *tmp_file;
2338
        static guint32 id = 0;
2339
2340
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2341
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
2342
2343
        return tmp_file;
2344
}
2345
2346
off_t get_file_size(const gchar *file)
2347
{
2348
        struct stat s;
2349
2350
        if (g_stat(file, &s) < 0) {
2351
                FILE_OP_ERROR(file, "stat");
2352
                return -1;
2353
        }
2354
2355
        return s.st_size;
2356
}
2357
2358
off_t get_file_size_as_crlf(const gchar *file)
2359
{
2360
        FILE *fp;
2361
        off_t size = 0;
2362
        gchar buf[BUFFSIZE];
2363
2364
        if ((fp = g_fopen(file, "rb")) == NULL) {
2365
                FILE_OP_ERROR(file, "fopen");
2366
                return -1;
2367
        }
2368
2369
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2370
                strretchomp(buf);
2371
                size += strlen(buf) + 2;
2372
        }
2373
2374
        if (ferror(fp)) {
2375
                FILE_OP_ERROR(file, "fgets");
2376
                size = -1;
2377
        }
2378
2379
        fclose(fp);
2380
2381
        return size;
2382
}
2383
2384
off_t get_left_file_size(FILE *fp)
2385
{
2386
        glong pos;
2387
        glong end;
2388
        off_t size;
2389
2390
        if ((pos = ftell(fp)) < 0) {
2391
                perror("ftell");
2392
                return -1;
2393
        }
2394
        if (fseek(fp, 0L, SEEK_END) < 0) {
2395
                perror("fseek");
2396
                return -1;
2397
        }
2398
        if ((end = ftell(fp)) < 0) {
2399
                perror("fseek");
2400
                return -1;
2401
        }
2402
        size = end - pos;
2403
        if (fseek(fp, pos, SEEK_SET) < 0) {
2404
                perror("fseek");
2405
                return -1;
2406
        }
2407
2408
        return size;
2409
}
2410
2411
gint get_last_empty_line_size(FILE *fp, off_t size)
2412
{
2413
        glong pos;
2414
        gint lsize = 0;
2415
        gchar buf[4];
2416
        size_t nread;
2417
2418
        if (size < 4)
2419
                return -1;
2420
2421
        if ((pos = ftell(fp)) < 0) {
2422
                perror("ftell");
2423
                return -1;
2424
        }
2425
        if (fseek(fp, size - 4, SEEK_CUR) < 0) {
2426
                perror("fseek");
2427
                return -1;
2428
        }
2429
2430
        /* read last 4 bytes */
2431
        nread = fread(buf, sizeof(buf), 1, fp);
2432
        if (nread != 1) {
2433
                perror("fread");
2434
                return -1;
2435
        }
2436
        /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */
2437
        if (buf[3] == '\n') {
2438
                if (buf[2] == '\n')
2439
                        lsize = 1;
2440
                else if (buf[2] == '\r') {
2441
                        if (buf[1] == '\n')
2442
                                lsize = 2;
2443
                }
2444
        }
2445
2446
        if (fseek(fp, pos, SEEK_SET) < 0) {
2447
                perror("fseek");
2448
                return -1;
2449
        }
2450
2451
        return lsize;
2452
}
2453
2454
gboolean file_exist(const gchar *file, gboolean allow_fifo)
2455
{
2456
        if (file == NULL)
2457
                return FALSE;
2458
2459
        if (allow_fifo) {
2460
                struct stat s;
2461
2462
                if (g_stat(file, &s) < 0) {
2463
                        if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2464
                        return FALSE;
2465
                }
2466
                if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode))
2467
                        return TRUE;
2468
        } else {
2469
                return g_file_test(file, G_FILE_TEST_IS_REGULAR);
2470
        }
2471
2472
        return FALSE;
2473
}
2474
2475
gboolean is_dir_exist(const gchar *dir)
2476
{
2477
        if (dir == NULL)
2478
                return FALSE;
2479
2480
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
2481
}
2482
2483
gboolean is_file_entry_exist(const gchar *file)
2484
{
2485
        if (file == NULL)
2486
                return FALSE;
2487
2488
        return g_file_test(file, G_FILE_TEST_EXISTS);
2489
}
2490
2491
gboolean dirent_is_regular_file(struct dirent *d)
2492
{
2493
#ifdef HAVE_DIRENT_D_TYPE
2494
        if (d->d_type == DT_REG)
2495
                return TRUE;
2496
        else if (d->d_type != DT_UNKNOWN)
2497
                return FALSE;
2498
#endif
2499
2500
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2501
}
2502
2503
gboolean dirent_is_directory(struct dirent *d)
2504
{
2505
#ifdef HAVE_DIRENT_D_TYPE
2506
        if (d->d_type == DT_DIR)
2507
                return TRUE;
2508
        else if (d->d_type != DT_UNKNOWN)
2509
                return FALSE;
2510
#endif
2511
2512
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2513
}
2514
2515
gint change_dir(const gchar *dir)
2516
{
2517
        gchar *prevdir = NULL;
2518
2519
        if (debug_mode)
2520
                prevdir = g_get_current_dir();
2521
2522
        if (g_chdir(dir) < 0) {
2523
                FILE_OP_ERROR(dir, "chdir");
2524
                if (debug_mode) g_free(prevdir);
2525
                return -1;
2526
        } else if (debug_mode) {
2527
                gchar *cwd;
2528
2529
                cwd = g_get_current_dir();
2530
                if (strcmp(prevdir, cwd) != 0)
2531
                        g_print("current dir: %s\n", cwd);
2532
                g_free(cwd);
2533
                g_free(prevdir);
2534
        }
2535
2536
        return 0;
2537
}
2538
2539
gint make_dir(const gchar *dir)
2540
{
2541
        if (g_mkdir(dir, S_IRWXU) < 0) {
2542
                FILE_OP_ERROR(dir, "mkdir");
2543
                return -1;
2544
        }
2545
        if (g_chmod(dir, S_IRWXU) < 0)
2546
                FILE_OP_ERROR(dir, "chmod");
2547
2548
        return 0;
2549
}
2550
2551
gint make_dir_hier(const gchar *dir)
2552
{
2553
        gchar *parent_dir;
2554
        const gchar *p;
2555
2556
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2557
                parent_dir = g_strndup(dir, p - dir);
2558
                if (*parent_dir != '\0') {
2559
                        if (!is_dir_exist(parent_dir)) {
2560
                                if (make_dir(parent_dir) < 0) {
2561
                                        g_free(parent_dir);
2562
                                        return -1;
2563
                                }
2564
                        }
2565
                }
2566
                g_free(parent_dir);
2567
        }
2568
2569
        if (!is_dir_exist(dir)) {
2570
                if (make_dir(dir) < 0)
2571
                        return -1;
2572
        }
2573
2574
        return 0;
2575
}
2576
2577
gint remove_all_files(const gchar *dir)
2578
{
2579
        GDir *dp;
2580
        const gchar *dir_name;
2581
        gchar *prev_dir;
2582
2583
        prev_dir = g_get_current_dir();
2584
2585
        if (g_chdir(dir) < 0) {
2586
                FILE_OP_ERROR(dir, "chdir");
2587
                g_free(prev_dir);
2588
                return -1;
2589
        }
2590
2591
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2592
                g_warning("failed to open directory: %s\n", dir);
2593
                g_free(prev_dir);
2594
                return -1;
2595
        }
2596
2597
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2598
                if (g_unlink(dir_name) < 0)
2599
                        FILE_OP_ERROR(dir_name, "unlink");
2600
        }
2601
2602
        g_dir_close(dp);
2603
2604
        if (g_chdir(prev_dir) < 0) {
2605
                FILE_OP_ERROR(prev_dir, "chdir");
2606
                g_free(prev_dir);
2607
                return -1;
2608
        }
2609
2610
        g_free(prev_dir);
2611
2612
        return 0;
2613
}
2614
2615
gint remove_numbered_files(const gchar *dir, guint first, guint last)
2616
{
2617
        GDir *dp;
2618
        const gchar *dir_name;
2619
        gchar *prev_dir;
2620
        guint file_no;
2621
2622
        prev_dir = g_get_current_dir();
2623
2624
        if (g_chdir(dir) < 0) {
2625
                FILE_OP_ERROR(dir, "chdir");
2626
                g_free(prev_dir);
2627
                return -1;
2628
        }
2629
2630
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2631
                g_warning("failed to open directory: %s\n", dir);
2632
                g_free(prev_dir);
2633
                return -1;
2634
        }
2635
2636
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2637
                file_no = to_unumber(dir_name);
2638
                if (file_no > 0 && first <= file_no && file_no <= last) {
2639
                        if (is_dir_exist(dir_name))
2640
                                continue;
2641
                        if (g_unlink(dir_name) < 0)
2642
                                FILE_OP_ERROR(dir_name, "unlink");
2643
                }
2644
        }
2645
2646
        g_dir_close(dp);
2647
2648
        if (g_chdir(prev_dir) < 0) {
2649
                FILE_OP_ERROR(prev_dir, "chdir");
2650
                g_free(prev_dir);
2651
                return -1;
2652
        }
2653
2654
        g_free(prev_dir);
2655
2656
        return 0;
2657
}
2658
2659
gint remove_all_numbered_files(const gchar *dir)
2660
{
2661
        return remove_numbered_files(dir, 0, UINT_MAX);
2662
}
2663
2664
gint remove_expired_files(const gchar *dir, guint hours)
2665
{
2666
        GDir *dp;
2667
        const gchar *dir_name;
2668
        struct stat s;
2669
        gchar *prev_dir;
2670
        guint file_no;
2671
        time_t mtime, now, expire_time;
2672
2673
        prev_dir = g_get_current_dir();
2674
2675
        if (g_chdir(dir) < 0) {
2676
                FILE_OP_ERROR(dir, "chdir");
2677
                g_free(prev_dir);
2678
                return -1;
2679
        }
2680
2681
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2682
                g_warning("failed to open directory: %s\n", dir);
2683
                g_free(prev_dir);
2684
                return -1;
2685
        }
2686
2687
        now = time(NULL);
2688
        expire_time = hours * 60 * 60;
2689
2690
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2691
                file_no = to_unumber(dir_name);
2692
                if (file_no > 0) {
2693
                        if (g_stat(dir_name, &s) < 0) {
2694
                                FILE_OP_ERROR(dir_name, "stat");
2695
                                continue;
2696
                        }
2697
                        if (S_ISDIR(s.st_mode))
2698
                                continue;
2699
                        mtime = MAX(s.st_mtime, s.st_atime);
2700
                        if (now - mtime > expire_time) {
2701
                                if (g_unlink(dir_name) < 0)
2702
                                        FILE_OP_ERROR(dir_name, "unlink");
2703
                        }
2704
                }
2705
        }
2706
2707
        g_dir_close(dp);
2708
2709
        if (g_chdir(prev_dir) < 0) {
2710
                FILE_OP_ERROR(prev_dir, "chdir");
2711
                g_free(prev_dir);
2712
                return -1;
2713
        }
2714
2715
        g_free(prev_dir);
2716
2717
        return 0;
2718
}
2719
2720
static gint remove_dir_recursive_real(const gchar *dir)
2721
{
2722
        struct stat s;
2723
        GDir *dp;
2724
        const gchar *dir_name;
2725
        gchar *prev_dir;
2726
2727
        if (g_stat(dir, &s) < 0) {
2728
                FILE_OP_ERROR(dir, "stat");
2729
                if (ENOENT == errno) return 0;
2730
                return -1;
2731
        }
2732
2733
        if (!S_ISDIR(s.st_mode)) {
2734
                if (g_unlink(dir) < 0) {
2735
                        FILE_OP_ERROR(dir, "unlink");
2736
                        return -1;
2737
                }
2738
2739
                return 0;
2740
        }
2741
2742
        prev_dir = g_get_current_dir();
2743
        /* g_print("prev_dir = %s\n", prev_dir); */
2744
2745
        if (g_chdir(dir) < 0) {
2746
                FILE_OP_ERROR(dir, "chdir");
2747
                g_free(prev_dir);
2748
                return -1;
2749
        }
2750
2751
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2752
                g_warning("failed to open directory: %s\n", dir);
2753
                g_chdir(prev_dir);
2754
                g_free(prev_dir);
2755
                return -1;
2756
        }
2757
2758
        /* remove all files in the directory */
2759
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2760
                /* g_print("removing %s\n", dir_name); */
2761
2762
                if (is_dir_exist(dir_name)) {
2763
                        if (remove_dir_recursive_real(dir_name) < 0) {
2764
                                g_warning("can't remove directory\n");
2765
                                return -1;
2766
                        }
2767
                } else {
2768
                        if (g_unlink(dir_name) < 0)
2769
                                FILE_OP_ERROR(dir_name, "unlink");
2770
                }
2771
        }
2772
2773
        g_dir_close(dp);
2774
2775
        if (g_chdir(prev_dir) < 0) {
2776
                FILE_OP_ERROR(prev_dir, "chdir");
2777
                g_free(prev_dir);
2778
                return -1;
2779
        }
2780
2781
        g_free(prev_dir);
2782
2783
        if (g_rmdir(dir) < 0) {
2784
                if (ENOTDIR == errno) {
2785
                        if (g_unlink(dir) < 0) {
2786
                                FILE_OP_ERROR(dir, "unlink");
2787
                                return -1;
2788
                        }
2789
                } else {
2790
                        FILE_OP_ERROR(dir, "rmdir");
2791
                        return -1;
2792
                }
2793
        }
2794
2795
        return 0;
2796
}
2797
2798
gint remove_dir_recursive(const gchar *dir)
2799
{
2800
        gchar *cur_dir;
2801
        gint ret;
2802
2803
        debug_print("remove_dir_recursive: %s\n", dir);
2804
2805
        cur_dir = g_get_current_dir();
2806
2807
        if (g_chdir(dir) < 0) {
2808
                FILE_OP_ERROR(dir, "chdir");
2809
                ret = -1;
2810
                goto leave;
2811
        }
2812
        if (g_chdir("..") < 0) {
2813
                FILE_OP_ERROR(dir, "chdir");
2814
                ret = -1;
2815
                goto leave;
2816
        }
2817
2818
        ret = remove_dir_recursive_real(dir);
2819
2820
leave:
2821
        if (is_dir_exist(cur_dir)) {
2822
                if (g_chdir(cur_dir) < 0) {
2823
                        FILE_OP_ERROR(cur_dir, "chdir");
2824
                }
2825
        }
2826
2827
        g_free(cur_dir);
2828
2829
        return ret;
2830
}
2831
2832
gint rename_force(const gchar *oldpath, const gchar *newpath)
2833
{
2834
#if !defined(G_OS_UNIX) && !GLIB_CHECK_VERSION(2, 9, 1)
2835
        if (!is_file_entry_exist(oldpath)) {
2836
                errno = ENOENT;
2837
                return -1;
2838
        }
2839
        if (is_file_exist(newpath)) {
2840
                if (g_unlink(newpath) < 0)
2841
                        FILE_OP_ERROR(newpath, "unlink");
2842
        }
2843
#endif
2844
        return g_rename(oldpath, newpath);
2845
}
2846
2847
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2848
{
2849
#ifdef G_OS_WIN32
2850
        wchar_t *wsrc;
2851
        wchar_t *wdest;
2852
        gchar *dest_bak = NULL;
2853
        gboolean err = FALSE;
2854
2855
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
2856
        if (wsrc == NULL) {
2857
                return -1;
2858
        }
2859
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
2860
        if (wdest == NULL) {
2861
                g_free(wsrc);
2862
                return -1;
2863
        }
2864
2865
        if (keep_backup == FALSE) {
2866
                if (CopyFileW(wsrc, wdest, FALSE) == 0)
2867
                        err = TRUE;
2868
                g_free(wdest);
2869
                g_free(wsrc);
2870
                return err ? -1 : 0;
2871
        }
2872
2873
        if (is_file_exist(dest)) {
2874
                dest_bak = g_strconcat(dest, ".bak", NULL);
2875
                if (rename_force(dest, dest_bak) < 0) {
2876
                        FILE_OP_ERROR(dest, "rename");
2877
                        g_free(dest_bak);
2878
                        g_free(wdest);
2879
                        g_free(wsrc);
2880
                        return -1;
2881
                }
2882
        }
2883
2884
        if (CopyFileW(wsrc, wdest, FALSE) == 0)
2885
                err = TRUE;
2886
2887
        g_free(wdest);
2888
        g_free(wsrc);
2889
#else
2890
        gint srcfd, destfd;
2891
        gint n_read;
2892
        gchar buf[BUFFSIZE];
2893
        gchar *dest_bak = NULL;
2894
        gboolean err = FALSE;
2895
2896
        if ((srcfd = g_open(src, O_RDONLY, 0600)) < 0) {
2897
                FILE_OP_ERROR(src, "open");
2898
                return -1;
2899
        }
2900
        if (is_file_exist(dest)) {
2901
                dest_bak = g_strconcat(dest, ".bak", NULL);
2902
                if (rename_force(dest, dest_bak) < 0) {
2903
                        FILE_OP_ERROR(dest, "rename");
2904
                        close(srcfd);
2905
                        g_free(dest_bak);
2906
                        return -1;
2907
                }
2908
        }
2909
2910
        if ((destfd = g_open(dest, O_WRONLY | O_CREAT, 0600)) < 0) {
2911
                FILE_OP_ERROR(dest, "open");
2912
                close(srcfd);
2913
                if (dest_bak) {
2914
                        if (rename_force(dest_bak, dest) < 0)
2915
                                FILE_OP_ERROR(dest_bak, "rename");
2916
                        g_free(dest_bak);
2917
                }
2918
                return -1;
2919
        }
2920
2921
        while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) {
2922
                gchar *p = buf;
2923
                const gchar *endp = buf + n_read;
2924
                gint n_write;
2925
2926
                while (p < endp) {
2927
                        if ((n_write = write(destfd, p, endp - p)) < 0) {
2928
                                g_warning(_("writing to %s failed.\n"), dest);
2929
                                close(destfd);
2930
                                close(srcfd);
2931
                                g_unlink(dest);
2932
                                if (dest_bak) {
2933
                                        if (rename_force(dest_bak, dest) < 0)
2934
                                                FILE_OP_ERROR(dest_bak, "rename");
2935
                                        g_free(dest_bak);
2936
                                }
2937
                                return -1;
2938
                        }
2939
                        p += n_write;
2940
                }
2941
        }
2942
2943
        if (close(destfd) < 0) {
2944
                FILE_OP_ERROR(dest, "close");
2945
                err = TRUE;
2946
        }
2947
        close(srcfd);
2948
#endif
2949
2950
        if (err) {
2951
                g_unlink(dest);
2952
                if (dest_bak) {
2953
                        if (rename_force(dest_bak, dest) < 0)
2954
                                FILE_OP_ERROR(dest_bak, "rename");
2955
                        g_free(dest_bak);
2956
                }
2957
                return -1;
2958
        }
2959
2960
        if (keep_backup == FALSE && dest_bak)
2961
                g_unlink(dest_bak);
2962
2963
        g_free(dest_bak);
2964
2965
        return 0;
2966
}
2967
2968
gint copy_dir(const gchar *src, const gchar *dest)
2969
{
2970
        GDir *dir;
2971
        const gchar *dir_name;
2972
        gchar *src_file;
2973
        gchar *dest_file;
2974
2975
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
2976
                g_warning("failed to open directory: %s\n", src);
2977
                return -1;
2978
        }
2979
2980
        if (make_dir_hier(dest) < 0) {
2981
                g_dir_close(dir);
2982
                return -1;
2983
        }
2984
2985
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
2986
                src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
2987
                dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
2988
                                        NULL);
2989
                if (is_file_exist(src_file))
2990
                        copy_file(src_file, dest_file, FALSE);
2991
                g_free(dest_file);
2992
                g_free(src_file);
2993
        }
2994
2995
        g_dir_close(dir);
2996
2997
        return 0;
2998
}
2999
3000
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
3001
{
3002
        if (overwrite == FALSE && is_file_entry_exist(dest)) {
3003
                g_warning("move_file(): file %s already exists.", dest);
3004
                return -1;
3005
        }
3006
3007
        if (rename_force(src, dest) == 0) return 0;
3008
3009
        if (EXDEV != errno) {
3010
                FILE_OP_ERROR(src, "rename");
3011
                return -1;
3012
        }
3013
3014
        if (copy_file(src, dest, FALSE) < 0) return -1;
3015
3016
        g_unlink(src);
3017
3018
        return 0;
3019
}
3020
3021
gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
3022
{
3023
        gint n_read;
3024
        gint bytes_left, to_read;
3025
        gchar buf[BUFSIZ];
3026
3027
        g_return_val_if_fail(fp != NULL, -1);
3028
        g_return_val_if_fail(dest_fp != NULL, -1);
3029
3030
        if (fseek(fp, offset, SEEK_SET) < 0) {
3031
                perror("fseek");
3032
                return -1;
3033
        }
3034
3035
        bytes_left = length;
3036
        to_read = MIN(bytes_left, sizeof(buf));
3037
3038
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
3039
                if (n_read < to_read && ferror(fp))
3040
                        break;
3041
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3042
                        g_warning("append_file_part: writing to file failed.\n");
3043
                        return -1;
3044
                }
3045
                bytes_left -= n_read;
3046
                if (bytes_left == 0)
3047
                        break;
3048
                to_read = MIN(bytes_left, sizeof(buf));
3049
        }
3050
3051
        if (ferror(fp)) {
3052
                perror("fread");
3053
                return -1;
3054
        }
3055
        if (fflush(dest_fp) == EOF) {
3056
                FILE_OP_ERROR("append_file_part", "fflush");
3057
                return -1;
3058
        }
3059
3060
        return 0;
3061
}
3062
3063
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
3064
{
3065
        FILE *dest_fp;
3066
3067
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3068
                FILE_OP_ERROR(dest, "fopen");
3069
                return -1;
3070
        }
3071
3072
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3073
                FILE_OP_ERROR(dest, "chmod");
3074
                g_warning("can't change file mode\n");
3075
        }
3076
3077
        if (append_file_part(fp, offset, length, dest_fp) < 0) {
3078
                g_warning("writing to %s failed.\n", dest);
3079
                fclose(dest_fp);
3080
                g_unlink(dest);
3081
                return -1;
3082
        }
3083
3084
        if (fclose(dest_fp) == EOF) {
3085
                FILE_OP_ERROR(dest, "fclose");
3086
                g_unlink(dest);
3087
                return -1;
3088
        }
3089
3090
        return 0;
3091
}
3092
3093
/* convert line endings into CRLF. If the last line doesn't end with
3094
 * linebreak, add it.
3095
 */
3096
gchar *canonicalize_str(const gchar *str)
3097
{
3098
        const gchar *p;
3099
        guint new_len = 0;
3100
        gchar *out, *outp;
3101
3102
        for (p = str; *p != '\0'; ++p) {
3103
                if (*p != '\r') {
3104
                        ++new_len;
3105
                        if (*p == '\n')
3106
                                ++new_len;
3107
                }
3108
        }
3109
        if (p == str || *(p - 1) != '\n')
3110
                new_len += 2;
3111
3112
        out = outp = g_malloc(new_len + 1);
3113
        for (p = str; *p != '\0'; ++p) {
3114
                if (*p != '\r') {
3115
                        if (*p == '\n')
3116
                                *outp++ = '\r';
3117
                        *outp++ = *p;
3118
                }
3119
        }
3120
        if (p == str || *(p - 1) != '\n') {
3121
                *outp++ = '\r';
3122
                *outp++ = '\n';
3123
        }
3124
        *outp = '\0';
3125
3126
        return out;
3127
}
3128
3129
gint canonicalize_file(const gchar *src, const gchar *dest)
3130
{
3131
        FILE *src_fp, *dest_fp;
3132
        gchar buf[BUFFSIZE];
3133
        gint len;
3134
        gboolean err = FALSE;
3135
        gboolean last_linebreak = FALSE;
3136
3137
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3138
                FILE_OP_ERROR(src, "fopen");
3139
                return -1;
3140
        }
3141
3142
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3143
                FILE_OP_ERROR(dest, "fopen");
3144
                fclose(src_fp);
3145
                return -1;
3146
        }
3147
3148
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3149
                FILE_OP_ERROR(dest, "chmod");
3150
                g_warning("can't change file mode\n");
3151
        }
3152
3153
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3154
                gint r = 0;
3155
3156
                len = strlen(buf);
3157
                if (len == 0) break;
3158
                last_linebreak = FALSE;
3159
3160
                if (buf[len - 1] != '\n') {
3161
                        last_linebreak = TRUE;
3162
                        r = fputs(buf, dest_fp);
3163
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3164
                        r = fputs(buf, dest_fp);
3165
                } else {
3166
                        if (len > 1) {
3167
                                r = fwrite(buf, len - 1, 1, dest_fp);
3168
                                if (r != 1)
3169
                                        r = EOF;
3170
                        }
3171
                        if (r != EOF)
3172
                                r = fputs("\r\n", dest_fp);
3173
                }
3174
3175
                if (r == EOF) {
3176
                        g_warning("writing to %s failed.\n", dest);
3177
                        fclose(dest_fp);
3178
                        fclose(src_fp);
3179
                        g_unlink(dest);
3180
                        return -1;
3181
                }
3182
        }
3183
3184
        if (last_linebreak == TRUE) {
3185
                if (fputs("\r\n", dest_fp) == EOF)
3186
                        err = TRUE;
3187
        }
3188
3189
        if (ferror(src_fp)) {
3190
                FILE_OP_ERROR(src, "fgets");
3191
                err = TRUE;
3192
        }
3193
        fclose(src_fp);
3194
        if (fclose(dest_fp) == EOF) {
3195
                FILE_OP_ERROR(dest, "fclose");
3196
                err = TRUE;
3197
        }
3198
3199
        if (err) {
3200
                g_unlink(dest);
3201
                return -1;
3202
        }
3203
3204
        return 0;
3205
}
3206
3207
gint canonicalize_file_replace(const gchar *file)
3208
{
3209
        gchar *tmp_file;
3210
3211
        tmp_file = get_tmp_file();
3212
3213
        if (canonicalize_file(file, tmp_file) < 0) {
3214
                g_free(tmp_file);
3215
                return -1;
3216
        }
3217
3218
        if (move_file(tmp_file, file, TRUE) < 0) {
3219
                g_warning("can't replace %s .\n", file);
3220
                g_unlink(tmp_file);
3221
                g_free(tmp_file);
3222
                return -1;
3223
        }
3224
3225
        g_free(tmp_file);
3226
        return 0;
3227
}
3228
3229
FILE *canonicalize_file_stream(FILE *src_fp, gint *length)
3230
{
3231
        FILE *dest_fp;
3232
        gchar buf[BUFFSIZE];
3233
        gint len;
3234
        gint length_ = 0;
3235
        gboolean err = FALSE;
3236
        gboolean last_linebreak = FALSE;
3237
3238
        if ((dest_fp = my_tmpfile()) == NULL)
3239
                return NULL;
3240
3241
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3242
                gint r = 0;
3243
3244
                len = strlen(buf);
3245
                if (len == 0) break;
3246
                last_linebreak = FALSE;
3247
3248
                if (buf[len - 1] != '\n') {
3249
                        last_linebreak = TRUE;
3250
                        r = fputs(buf, dest_fp);
3251
                        length_ += len;
3252
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3253
                        r = fputs(buf, dest_fp);
3254
                        length_ += len;
3255
                } else {
3256
                        if (len > 1) {
3257
                                r = fwrite(buf, len - 1, 1, dest_fp);
3258
                                if (r != 1)
3259
                                        r = EOF;
3260
                                else
3261
                                        length_ += len - 1;
3262
                        }
3263
                        if (r != EOF) {
3264
                                r = fputs("\r\n", dest_fp);
3265
                                length_ += 2;
3266
                        }
3267
                }
3268
3269
                if (r == EOF) {
3270
                        g_warning("writing to temporary file failed.\n");
3271
                        fclose(dest_fp);
3272
                        return NULL;
3273
                }
3274
        }
3275
3276
        if (last_linebreak == TRUE) {
3277
                if (fputs("\r\n", dest_fp) == EOF)
3278
                        err = TRUE;
3279
                else
3280
                        length_ += 2;
3281
        }
3282
3283
        if (ferror(src_fp)) {
3284
                FILE_OP_ERROR("canonicalize_file_stream", "fgets");
3285
                err = TRUE;
3286
        }
3287
        if (fflush(dest_fp) == EOF) {
3288
                FILE_OP_ERROR("canonicalize_file_stream", "fflush");
3289
                err = TRUE;
3290
        }
3291
3292
        if (err) {
3293
                fclose(dest_fp);
3294
                return NULL;
3295
        }
3296
3297
        if (length)
3298
                *length = length_;
3299
3300
        rewind(dest_fp);
3301
        return dest_fp;
3302
}
3303
3304
gint uncanonicalize_file(const gchar *src, const gchar *dest)
3305
{
3306
        FILE *src_fp, *dest_fp;
3307
        gchar buf[BUFFSIZE];
3308
        gboolean err = FALSE;
3309
3310
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3311
                FILE_OP_ERROR(src, "fopen");
3312
                return -1;
3313
        }
3314
3315
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3316
                FILE_OP_ERROR(dest, "fopen");
3317
                fclose(src_fp);
3318
                return -1;
3319
        }
3320
3321
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3322
                FILE_OP_ERROR(dest, "chmod");
3323
                g_warning("can't change file mode\n");
3324
        }
3325
3326
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3327
                strcrchomp(buf);
3328
                if (fputs(buf, dest_fp) == EOF) {
3329
                        g_warning("writing to %s failed.\n", dest);
3330
                        fclose(dest_fp);
3331
                        fclose(src_fp);
3332
                        g_unlink(dest);
3333
                        return -1;
3334
                }
3335
        }
3336
3337
        if (ferror(src_fp)) {
3338
                FILE_OP_ERROR(src, "fgets");
3339
                err = TRUE;
3340
        }
3341
        fclose(src_fp);
3342
        if (fclose(dest_fp) == EOF) {
3343
                FILE_OP_ERROR(dest, "fclose");
3344
                err = TRUE;
3345
        }
3346
3347
        if (err) {
3348
                g_unlink(dest);
3349
                return -1;
3350
        }
3351
3352
        return 0;
3353
}
3354
3355
gint uncanonicalize_file_replace(const gchar *file)
3356
{
3357
        gchar *tmp_file;
3358
3359
        tmp_file = get_tmp_file();
3360
3361
        if (uncanonicalize_file(file, tmp_file) < 0) {
3362
                g_free(tmp_file);
3363
                return -1;
3364
        }
3365
3366
        if (move_file(tmp_file, file, TRUE) < 0) {
3367
                g_warning("can't replace %s .\n", file);
3368
                g_unlink(tmp_file);
3369
                g_free(tmp_file);
3370
                return -1;
3371
        }
3372
3373
        g_free(tmp_file);
3374
        return 0;
3375
}
3376
3377
gchar *normalize_newlines(const gchar *str)
3378
{
3379
        const gchar *p = str;
3380
        gchar *out, *outp;
3381
3382
        out = outp = g_malloc(strlen(str) + 1);
3383
        for (p = str; *p != '\0'; ++p) {
3384
                if (*p == '\r') {
3385
                        if (*(p + 1) != '\n')
3386
                                *outp++ = '\n';
3387
                } else
3388
                        *outp++ = *p;
3389
        }
3390
3391
        *outp = '\0';
3392
3393
        return out;
3394
}
3395
3396
gchar *strchomp_all(const gchar *str)
3397
{
3398
        const gchar *p = str;
3399
        const gchar *newline, *last;
3400
        gchar *out, *outp;
3401
3402
        out = outp = g_malloc(strlen(str) + 1);
3403
        while (*p != '\0') {
3404
                newline = strchr(p, '\n');
3405
                if (newline) {
3406
                        for (last = newline;
3407
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3408
                                ;
3409
                        strncpy(outp, p, last - p);
3410
                        outp += last - p;
3411
3412
                        if (p < newline && *(newline - 1) == '\r') {
3413
                                strncpy(outp, newline - 1, 2);
3414
                                outp += 2;
3415
                        } else {
3416
                                *outp++ = *newline;
3417
                        }
3418
3419
                        p = newline + 1;
3420
                } else {
3421
                        for (last = p + strlen(p);
3422
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3423
                                ;
3424
                        strncpy(outp, p, last - p);
3425
                        outp += last - p;
3426
                        break;
3427
                }
3428
        }
3429
3430
        *outp = '\0';
3431
3432
        return out;
3433
}
3434
3435
FILE *get_outgoing_rfc2822_file(FILE *fp)
3436
{
3437
        gchar buf[BUFFSIZE];
3438
        FILE *outfp;
3439
3440
        outfp = my_tmpfile();
3441
        if (!outfp) {
3442
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile");
3443
                return NULL;
3444
        }
3445
3446
        /* output header part */
3447
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3448
                strretchomp(buf);
3449
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3450
                        gint next;
3451
3452
                        for (;;) {
3453
                                next = fgetc(fp);
3454
                                if (next == EOF)
3455
                                        break;
3456
                                else if (next != ' ' && next != '\t') {
3457
                                        ungetc(next, fp);
3458
                                        break;
3459
                                }
3460
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3461
                                        break;
3462
                        }
3463
                } else {
3464
                        if (fputs(buf, outfp) == EOF)
3465
                                goto file_error;
3466
                        if (fputs("\r\n", outfp) == EOF)
3467
                                goto file_error;
3468
                        if (buf[0] == '\0')
3469
                                break;
3470
                }
3471
        }
3472
3473
        /* output body part */
3474
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3475
                strretchomp(buf);
3476
                if (buf[0] == '.') {
3477
                        if (fputc('.', outfp) == EOF)
3478
                                goto file_error;
3479
                }
3480
                if (fputs(buf, outfp) == EOF)
3481
                        goto file_error;
3482
                if (fputs("\r\n", outfp) == EOF)
3483
                        goto file_error;
3484
        }
3485
3486
        if (fflush(outfp) == EOF) {
3487
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush");
3488
                goto file_error;
3489
        }
3490
3491
        rewind(outfp);
3492
        return outfp;
3493
3494
file_error:
3495
        g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n");
3496
        fclose(outfp);
3497
        return NULL;
3498
}
3499
3500
gchar *get_outgoing_rfc2822_str(FILE *fp)
3501
{
3502
        gchar buf[BUFFSIZE];
3503
        GString *str;
3504
        gchar *ret;
3505
3506
        str = g_string_new(NULL);
3507
3508
        /* output header part */
3509
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3510
                strretchomp(buf);
3511
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3512
                        gint next;
3513
3514
                        for (;;) {
3515
                                next = fgetc(fp);
3516
                                if (next == EOF)
3517
                                        break;
3518
                                else if (next != ' ' && next != '\t') {
3519
                                        ungetc(next, fp);
3520
                                        break;
3521
                                }
3522
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3523
                                        break;
3524
                        }
3525
#if 0
3526
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
3527
                        get_rfc822_date(buf, sizeof(buf));
3528
                        g_string_append_printf(str, "Date: %s\r\n", buf);
3529
#endif
3530
                } else {
3531
                        g_string_append(str, buf);
3532
                        g_string_append(str, "\r\n");
3533
                        if (buf[0] == '\0')
3534
                                break;
3535
                }
3536
        }
3537
3538
        /* output body part */
3539
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3540
                strretchomp(buf);
3541
                if (buf[0] == '.')
3542
                        g_string_append_c(str, '.');
3543
                g_string_append(str, buf);
3544
                g_string_append(str, "\r\n");
3545
        }
3546
3547
        ret = str->str;
3548
        g_string_free(str, FALSE);
3549
3550
        return ret;
3551
}
3552
3553
/*
3554
 * Create a new boundary in a way that it is very unlikely that this
3555
 * will occur in the following text.  It would be easy to ensure
3556
 * uniqueness if everything is either quoted-printable or base64
3557
 * encoded (note that conversion is allowed), but because MIME bodies
3558
 * may be nested, it may happen that the same boundary has already
3559
 * been used. We avoid scanning the message for conflicts and hope the
3560
 * best.
3561
 *
3562
 *   boundary := 0*69<bchars> bcharsnospace
3563
 *   bchars := bcharsnospace / " "
3564
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3565
 *                    "+" / "_" / "," / "-" / "." /
3566
 *                    "/" / ":" / "=" / "?"
3567
 *
3568
 * some special characters removed because of buggy MTAs
3569
 */
3570
3571
gchar *generate_mime_boundary(const gchar *prefix)
3572
{
3573
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3574
                             "abcdefghijklmnopqrstuvwxyz"
3575
                             "1234567890+_./=";
3576
        gchar buf_uniq[17];
3577
        gchar buf_date[64];
3578
        gint i;
3579
3580
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3581
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3582
        buf_uniq[i] = '\0';
3583
3584
        get_rfc822_date(buf_date, sizeof(buf_date));
3585
        subst_chars(buf_date, " ,:", '_');
3586
3587
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
3588
                               buf_date, buf_uniq);
3589
}
3590
3591
gint change_file_mode_rw(FILE *fp, const gchar *file)
3592
{
3593
#ifdef G_OS_WIN32
3594
        DWORD attr;
3595
        BOOL retval;
3596
3597
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3598
                wchar_t *wpath;
3599
3600
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3601
                if (wpath == NULL)
3602
                        return -1;
3603
3604
                attr = GetFileAttributesW(wpath);
3605
                retval = SetFileAttributesW
3606
                        (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3607
3608
                g_free(wpath);
3609
        } else {
3610
                gchar *cp_path;
3611
3612
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3613
                if (cp_path == NULL)
3614
                        return -1;
3615
3616
                attr = GetFileAttributesA(cp_path);
3617
                retval = SetFileAttributesA
3618
                        (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3619
3620
                g_free(cp_path);
3621
        }
3622
3623
        if (retval)
3624
                return 0;
3625
        else
3626
                return -1;
3627
#else
3628
#if HAVE_FCHMOD
3629
        if (fp)
3630
                return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3631
        else
3632
#endif
3633
                return g_chmod(file, S_IRUSR|S_IWUSR);
3634
#endif
3635
}
3636
3637
#ifdef G_OS_WIN32
3638
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
3639
{
3640
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3641
                wchar_t *wpath;
3642
                wchar_t *wprefix;
3643
                wchar_t *wname;
3644
                gint save_errno;
3645
                gchar *name;
3646
3647
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
3648
                if (wpath == NULL) {
3649
                        errno = EINVAL;
3650
                        return NULL;
3651
                }
3652
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
3653
                if (wprefix == NULL) {
3654
                        errno = EINVAL;
3655
                        g_free(wpath);
3656
                        return NULL;
3657
                }
3658
3659
                wname = _wtempnam(wpath, wprefix);
3660
                save_errno = errno;
3661
3662
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
3663
                if (name == NULL) {
3664
                        save_errno = EINVAL;
3665
                }
3666
3667
                g_free(wname);
3668
                g_free(wprefix);
3669
                g_free(wpath);
3670
3671
                errno = save_errno;
3672
                return name;
3673
        } else {
3674
                gchar *cp_path;
3675
                gchar *cp_prefix;
3676
                gchar *cp_name;
3677
                gint save_errno;
3678
                gchar *name;
3679
3680
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
3681
                if (cp_path == NULL) {
3682
                        errno = EINVAL;
3683
                        return NULL;
3684
                }
3685
3686
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
3687
                if (cp_prefix == NULL) {
3688
                        errno = EINVAL;
3689
                        g_free(cp_path);
3690
                        return NULL;
3691
                }
3692
3693
                cp_name = _tempnam(cp_path, cp_prefix);
3694
                save_errno = errno;
3695
3696
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
3697
                if (name == NULL) {
3698
                        save_errno = EINVAL;
3699
                }
3700
3701
                g_free(cp_name);
3702
                g_free(cp_prefix);
3703
                g_free(cp_path);
3704
3705
                errno = save_errno;
3706
                return name;
3707
        }
3708
}
3709
#endif
3710
3711
FILE *my_tmpfile(void)
3712
{
3713
#ifdef G_OS_WIN32
3714
        const gchar *tmpdir;
3715
        gchar *fname;
3716
        gint fd;
3717
        FILE *fp;
3718
3719
        tmpdir = get_tmp_dir();
3720
        fname = _s_tempnam(tmpdir, "sylph");
3721
        if (!fname)
3722
                return NULL;
3723
3724
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
3725
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
3726
        if (fd < 0) {
3727
                g_free(fname);
3728
                return NULL;
3729
        }
3730
3731
        fp = fdopen(fd, "w+b");
3732
        if (!fp) {
3733
                perror("fdopen");
3734
                close(fd);
3735
        }
3736
3737
        g_free(fname);
3738
3739
        return fp;
3740
#else
3741
        const gchar suffix[] = ".XXXXXX";
3742
        const gchar *tmpdir;
3743
        guint tmplen;
3744
        const gchar *progname;
3745
        guint proglen;
3746
        gchar *fname;
3747
        gint fd;
3748
        FILE *fp;
3749
3750
        tmpdir = get_tmp_dir();
3751
        tmplen = strlen(tmpdir);
3752
        progname = g_get_prgname();
3753
        if (!progname)
3754
                progname = "sylph";
3755
        proglen = strlen(progname);
3756
        fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix));
3757
3758
        memcpy(fname, tmpdir, tmplen);
3759
        fname[tmplen] = G_DIR_SEPARATOR;
3760
        memcpy(fname + tmplen + 1, progname, proglen);
3761
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3762
3763
        fd = g_mkstemp(fname);
3764
        if (fd < 0) {
3765
                g_free(fname);
3766
                return tmpfile();
3767
        }
3768
3769
        g_unlink(fname);
3770
3771
        fp = fdopen(fd, "w+b");
3772
        if (!fp) {
3773
                perror("fdopen");
3774
                close(fd);
3775
        }
3776
3777
        g_free(fname);
3778
3779
        return fp;
3780
#endif
3781
}
3782
3783
FILE *str_open_as_stream(const gchar *str)
3784
{
3785
        FILE *fp;
3786
        size_t len;
3787
3788
        g_return_val_if_fail(str != NULL, NULL);
3789
3790
        fp = my_tmpfile();
3791
        if (!fp) {
3792
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3793
                return NULL;
3794
        }
3795
3796
        len = strlen(str);
3797
        if (len == 0) return fp;
3798
3799
        if (fwrite(str, len, 1, fp) != 1) {
3800
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3801
                fclose(fp);
3802
                return NULL;
3803
        }
3804
        if (fflush(fp) == EOF) {
3805
                FILE_OP_ERROR("str_open_as_stream", "fflush");
3806
                fclose(fp);
3807
                return NULL;
3808
        }
3809
3810
        rewind(fp);
3811
        return fp;
3812
}
3813
3814
gint str_write_to_file(const gchar *str, const gchar *file)
3815
{
3816
        FILE *fp;
3817
        size_t len;
3818
3819
        g_return_val_if_fail(str != NULL, -1);
3820
        g_return_val_if_fail(file != NULL, -1);
3821
3822
        if ((fp = g_fopen(file, "wb")) == NULL) {
3823
                FILE_OP_ERROR(file, "fopen");
3824
                return -1;
3825
        }
3826
3827
        len = strlen(str);
3828
        if (len == 0) {
3829
                fclose(fp);
3830
                return 0;
3831
        }
3832
3833
        if (fwrite(str, len, 1, fp) != 1) {
3834
                FILE_OP_ERROR(file, "fwrite");
3835
                fclose(fp);
3836
                g_unlink(file);
3837
                return -1;
3838
        }
3839
3840
        if (fclose(fp) == EOF) {
3841
                FILE_OP_ERROR(file, "fclose");
3842
                g_unlink(file);
3843
                return -1;
3844
        }
3845
3846
        return 0;
3847
}
3848
3849
gchar *file_read_to_str(const gchar *file)
3850
{
3851
        FILE *fp;
3852
        gchar *str;
3853
3854
        g_return_val_if_fail(file != NULL, NULL);
3855
3856
        if ((fp = g_fopen(file, "rb")) == NULL) {
3857
                FILE_OP_ERROR(file, "fopen");
3858
                return NULL;
3859
        }
3860
3861
        str = file_read_stream_to_str(fp);
3862
3863
        fclose(fp);
3864
3865
        return str;
3866
}
3867
3868
gchar *file_read_stream_to_str(FILE *fp)
3869
{
3870
        GByteArray *array;
3871
        guchar buf[BUFSIZ];
3872
        gint n_read;
3873
        gchar *str;
3874
3875
        g_return_val_if_fail(fp != NULL, NULL);
3876
3877
        array = g_byte_array_new();
3878
3879
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3880
                if (n_read < sizeof(buf) && ferror(fp))
3881
                        break;
3882
                g_byte_array_append(array, buf, n_read);
3883
        }
3884
3885
        if (ferror(fp)) {
3886
                FILE_OP_ERROR("file stream", "fread");
3887
                g_byte_array_free(array, TRUE);
3888
                return NULL;
3889
        }
3890
3891
        buf[0] = '\0';
3892
        g_byte_array_append(array, buf, 1);
3893
        str = (gchar *)array->data;
3894
        g_byte_array_free(array, FALSE);
3895
3896
        return str;
3897
}
3898
3899
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3900
static gchar **argv_utf8_to_locale(gchar **argv)
3901
{
3902
        gint argc = 0, i;
3903
        gchar **cp_argv;
3904
3905
        while (argv[argc] != NULL)
3906
                argc++;
3907
3908
        cp_argv = g_new(gchar *, argc + 1);
3909
3910
        for (i = 0; i < argc; i++) {
3911
                cp_argv[i] = g_locale_from_utf8(argv[i], -1, NULL, NULL, NULL);
3912
                if (cp_argv[i] == NULL) {
3913
                        g_warning("Failed to convert from UTF-8 to locale encoding: %s\n", argv[i]);
3914
                        g_strfreev(cp_argv);
3915
                        return NULL;
3916
                }
3917
        }
3918
        cp_argv[i] = NULL;
3919
3920
        return cp_argv;
3921
}
3922
#endif
3923
3924
gint execute_async(gchar *const argv[])
3925
{
3926
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3927
        gchar **cp_argv;
3928
3929
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3930
3931
        cp_argv = argv_utf8_to_locale((gchar **)argv);
3932
        if (!cp_argv)
3933
                return -1;
3934
        if (g_spawn_async(NULL, cp_argv, NULL, G_SPAWN_SEARCH_PATH,
3935
                          NULL, NULL, NULL, NULL) == FALSE) {
3936
                g_warning("Can't execute command: %s\n", argv[0]);
3937
                g_strfreev(cp_argv);
3938
                return -1;
3939
        }
3940
        g_strfreev(cp_argv);
3941
#else
3942
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3943
3944
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3945
                          NULL, NULL, NULL, NULL) == FALSE) {
3946
                g_warning("Can't execute command: %s\n", argv[0]);
3947
                return -1;
3948
        }
3949
#endif
3950
3951
        return 0;
3952
}
3953
3954
gint execute_sync(gchar *const argv[])
3955
{
3956
        gint status;
3957
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3958
        gchar **cp_argv;
3959
#endif
3960
3961
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3962
3963
#ifdef G_OS_WIN32
3964
#if !GLIB_CHECK_VERSION(2, 8, 2)
3965
        cp_argv = argv_utf8_to_locale((gchar **)argv);
3966
        if (!cp_argv)
3967
                return -1;
3968
        if (g_spawn_sync(NULL, cp_argv, NULL,
3969
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
3970
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3971
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3972
                g_warning("Can't execute command: %s\n", argv[0]);
3973
                g_strfreev(cp_argv);
3974
                return -1;
3975
        }
3976
        g_strfreev(cp_argv);
3977
#else /* !GLIB_CHECK_VERSION */
3978
        if (g_spawn_sync(NULL, (gchar **)argv, NULL,
3979
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
3980
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3981
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3982
                g_warning("Can't execute command: %s\n", argv[0]);
3983
                return -1;
3984
        }
3985
#endif /* !GLIB_CHECK_VERSION */
3986
3987
        return status;
3988
#else /* G_OS_WIN32 */
3989
        if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3990
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3991
                g_warning("Can't execute command: %s\n", argv[0]);
3992
                return -1;
3993
        }
3994
3995
        if (WIFEXITED(status))
3996
                return WEXITSTATUS(status);
3997
        else
3998
                return -1;
3999
#endif /* G_OS_WIN32 */
4000
}
4001
4002
gint execute_command_line(const gchar *cmdline, gboolean async)
4003
{
4004
        gchar **argv;
4005
        gint ret;
4006
4007
        if (debug_mode) {
4008
                gchar *utf8_cmdline;
4009
4010
                utf8_cmdline = g_filename_to_utf8
4011
                        (cmdline, -1, NULL, NULL, NULL);
4012
                debug_print("execute_command_line(): executing: %s\n",
4013
                            utf8_cmdline ? utf8_cmdline : cmdline);
4014
                g_free(utf8_cmdline);
4015
        }
4016
4017
        argv = strsplit_with_quote(cmdline, " ", 0);
4018
4019
        if (async)
4020
                ret = execute_async(argv);
4021
        else
4022
                ret = execute_sync(argv);
4023
4024
        g_strfreev(argv);
4025
4026
        return ret;
4027
}
4028
4029
#if USE_THREADS
4030
typedef struct _CmdData
4031
{
4032
        const gchar *cmdline;
4033
        volatile gint flag;
4034
        gint status;
4035
} CmdData;
4036
4037
static gpointer execute_command_line_async_func(gpointer data)
4038
{
4039
        CmdData *cmd_data = (CmdData *)data;
4040
        gchar **argv;
4041
4042
        argv = strsplit_with_quote(cmd_data->cmdline, " ", 0);
4043
        cmd_data->status = execute_sync(argv);
4044
        g_strfreev(argv);
4045
4046
        debug_print("execute_command_line_async_func: exec done: %s\n",
4047
                    cmd_data->cmdline);
4048
        g_atomic_int_set(&cmd_data->flag, 1);
4049
        g_main_context_wakeup(NULL);
4050
4051
        return GINT_TO_POINTER(0);
4052
}
4053
4054
gint execute_command_line_async_wait(const gchar *cmdline)
4055
{
4056
        CmdData data = {NULL, 0, 0};
4057
        GThread *thread;
4058
4059
        if (debug_mode) {
4060
                gchar *utf8_cmdline;
4061
4062
                utf8_cmdline = g_filename_to_utf8
4063
                        (cmdline, -1, NULL, NULL, NULL);
4064
                debug_print("execute_command_line(): executing: %s\n",
4065
                            utf8_cmdline ? utf8_cmdline : cmdline);
4066
                g_free(utf8_cmdline);
4067
        }
4068
4069
        data.cmdline = cmdline;
4070
        thread = g_thread_create(execute_command_line_async_func, &data, TRUE,
4071
                                 NULL);
4072
        if (!thread)
4073
                return -1;
4074
4075
        debug_print("execute_command_line_async_wait: waiting thread\n");
4076
        while (g_atomic_int_get(&data.flag) == 0)
4077
                event_loop_iterate();
4078
4079
        g_thread_join(thread);
4080
        debug_print("execute_command_line_async_wait: thread exited\n");
4081
4082
        return data.status;
4083
}
4084
#else /* USE_THREADS */
4085
gint execute_command_line_async_wait(const gchar *cmdline)
4086
{
4087
        return execute_command_line(cmdline, FALSE);
4088
}
4089
#endif /* USE_THREADS */
4090
4091
gint execute_open_file(const gchar *file, const gchar *content_type)
4092
{
4093
#ifdef G_OS_WIN32
4094
        g_return_val_if_fail(file != NULL, -1);
4095
4096
        log_print("opening %s - %s\n", file, content_type ? content_type : "");
4097
4098
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4099
                wchar_t *wpath;
4100
4101
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4102
                if (wpath == NULL)
4103
                        return -1;
4104
4105
                ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL);
4106
4107
                g_free(wpath);
4108
4109
                return 0;
4110
        } else {
4111
                gchar *cp_path;
4112
4113
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4114
                if (cp_path == NULL)
4115
                        return -1;
4116
4117
                ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL);
4118
4119
                g_free(cp_path);
4120
4121
                return 0;
4122
        }
4123
#elif defined(__APPLE__)
4124
        const gchar *argv[3] = {"open", NULL, NULL};
4125
4126
        g_return_val_if_fail(file != NULL, -1);
4127
4128
        log_print("opening %s - %s\n", file, content_type ? content_type : "");
4129
4130
        argv[1] = file;
4131
        execute_async(argv);
4132
#endif
4133
4134
        return 0;
4135
}
4136
4137
gint execute_print_file(const gchar *file)
4138
{
4139
        g_return_val_if_fail(file != NULL, -1);
4140
4141
#ifdef G_OS_WIN32
4142
        log_print("printing %s\n", file);
4143
4144
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4145
                wchar_t *wpath;
4146
4147
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4148
                if (wpath == NULL)
4149
                        return -1;
4150
4151
                ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL);
4152
4153
                g_free(wpath);
4154
4155
                return 0;
4156
        } else {
4157
                gchar *cp_path;
4158
4159
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4160
                if (cp_path == NULL)
4161
                        return -1;
4162
4163
                ShellExecuteA(NULL, "print", cp_path, NULL, NULL,
4164
                              SW_SHOWNORMAL);
4165
4166
                g_free(cp_path);
4167
4168
                return 0;
4169
        }
4170
#endif
4171
        return 0;
4172
}
4173
4174
gchar *get_command_output(const gchar *cmdline)
4175
{
4176
        gchar *child_stdout;
4177
        gint status;
4178
4179
        g_return_val_if_fail(cmdline != NULL, NULL);
4180
4181
        debug_print("get_command_output(): executing: %s\n", cmdline);
4182
4183
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
4184
                                      NULL) == FALSE) {
4185
                g_warning("Can't execute command: %s\n", cmdline);
4186
                return NULL;
4187
        }
4188
4189
        return child_stdout;
4190
}
4191
4192
gint open_uri(const gchar *uri, const gchar *cmdline)
4193
{
4194
        gchar buf[BUFFSIZE];
4195
4196
        g_return_val_if_fail(uri != NULL, -1);
4197
4198
#if defined(G_OS_WIN32) || defined(__APPLE__)
4199
        if (!cmdline || cmdline[0] == '\0')
4200
                return execute_open_file(uri, NULL);
4201
#endif
4202
4203
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
4204
                g_snprintf(buf, sizeof(buf), cmdline, uri);
4205
        else {
4206
                if (cmdline)
4207
                        g_warning("Open URI command line is invalid "
4208
                                  "(there must be only one '%%s'): %s",
4209
                                  cmdline);
4210
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
4211
        }
4212
4213
        execute_command_line(buf, TRUE);
4214
4215
        return 0;
4216
}
4217
4218
time_t remote_tzoffset_sec(const gchar *zone)
4219
{
4220
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
4221
        gchar zone3[4];
4222
        gchar *p;
4223
        gchar c;
4224
        gint iustz;
4225
        gint offset;
4226
        time_t remoteoffset;
4227
4228
        strncpy(zone3, zone, 3);
4229
        zone3[3] = '\0';
4230
        remoteoffset = 0;
4231
4232
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
4233
            (c == '+' || c == '-')) {
4234
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
4235
                if (c == '-')
4236
                        remoteoffset = -remoteoffset;
4237
        } else if (!strncmp(zone, "UT" , 2) ||
4238
                   !strncmp(zone, "GMT", 2)) {
4239
                remoteoffset = 0;
4240
        } else if (strlen(zone3) == 3) {
4241
                for (p = ustzstr; *p != '\0'; p += 3) {
4242
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
4243
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
4244
                                remoteoffset = iustz * 3600;
4245
                                break;
4246
                        }
4247
                }
4248
                if (*p == '\0')
4249
                        return -1;
4250
        } else if (strlen(zone3) == 1) {
4251
                switch (zone[0]) {
4252
                case 'Z': remoteoffset =   0; break;
4253
                case 'A': remoteoffset =  -1; break;
4254
                case 'B': remoteoffset =  -2; break;
4255
                case 'C': remoteoffset =  -3; break;
4256
                case 'D': remoteoffset =  -4; break;
4257
                case 'E': remoteoffset =  -5; break;
4258
                case 'F': remoteoffset =  -6; break;
4259
                case 'G': remoteoffset =  -7; break;
4260
                case 'H': remoteoffset =  -8; break;
4261
                case 'I': remoteoffset =  -9; break;
4262
                case 'K': remoteoffset = -10; break; /* J is not used */
4263
                case 'L': remoteoffset = -11; break;
4264
                case 'M': remoteoffset = -12; break;
4265
                case 'N': remoteoffset =   1; break;
4266
                case 'O': remoteoffset =   2; break;
4267
                case 'P': remoteoffset =   3; break;
4268
                case 'Q': remoteoffset =   4; break;
4269
                case 'R': remoteoffset =   5; break;
4270
                case 'S': remoteoffset =   6; break;
4271
                case 'T': remoteoffset =   7; break;
4272
                case 'U': remoteoffset =   8; break;
4273
                case 'V': remoteoffset =   9; break;
4274
                case 'W': remoteoffset =  10; break;
4275
                case 'X': remoteoffset =  11; break;
4276
                case 'Y': remoteoffset =  12; break;
4277
                default:  remoteoffset =   0; break;
4278
                }
4279
                remoteoffset = remoteoffset * 3600;
4280
        } else
4281
                return -1;
4282
4283
        return remoteoffset;
4284
}
4285
4286
time_t tzoffset_sec(time_t *now)
4287
{
4288
        struct tm gmt, *tmp, *lt;
4289
        gint off;
4290
4291
        tmp = gmtime(now);
4292
        g_return_val_if_fail(tmp != NULL, -1);
4293
        gmt = *tmp;
4294
        lt = localtime(now);
4295
        g_return_val_if_fail(lt != NULL, -1);
4296
4297
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4298
4299
        if (lt->tm_year < gmt.tm_year)
4300
                off -= 24 * 60;
4301
        else if (lt->tm_year > gmt.tm_year)
4302
                off += 24 * 60;
4303
        else if (lt->tm_yday < gmt.tm_yday)
4304
                off -= 24 * 60;
4305
        else if (lt->tm_yday > gmt.tm_yday)
4306
                off += 24 * 60;
4307
4308
        if (off >= 24 * 60)                /* should be impossible */
4309
                off = 23 * 60 + 59;        /* if not, insert silly value */
4310
        if (off <= -24 * 60)
4311
                off = -(23 * 60 + 59);
4312
4313
        return off * 60;
4314
}
4315
4316
/* calculate timezone offset (buf must not be less than 6 bytes) */
4317
gchar *tzoffset_buf(gchar *buf, time_t *now)
4318
{
4319
        struct tm gmt, *tmp, *lt;
4320
        gint off;
4321
        gchar sign = '+';
4322
4323
        tmp = gmtime(now);
4324
        g_return_val_if_fail(tmp != NULL, NULL);
4325
        gmt = *tmp;
4326
        lt = localtime(now);
4327
        g_return_val_if_fail(lt != NULL, NULL);
4328
4329
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4330
4331
        if (lt->tm_year < gmt.tm_year)
4332
                off -= 24 * 60;
4333
        else if (lt->tm_year > gmt.tm_year)
4334
                off += 24 * 60;
4335
        else if (lt->tm_yday < gmt.tm_yday)
4336
                off -= 24 * 60;
4337
        else if (lt->tm_yday > gmt.tm_yday)
4338
                off += 24 * 60;
4339
4340
        if (off < 0) {
4341
                sign = '-';
4342
                off = -off;
4343
        }
4344
4345
        if (off >= 24 * 60)                /* should be impossible */
4346
                off = 23 * 60 + 59;        /* if not, insert silly value */
4347
4348
        g_snprintf(buf, 6, "%c%02d%02d", sign, off / 60, off % 60);
4349
4350
        return buf;
4351
}
4352
4353
gchar *tzoffset(time_t *now)
4354
{
4355
        static gchar offset_string[6];
4356
4357
        return tzoffset_buf(offset_string, now);
4358
}
4359
4360
void get_rfc822_date(gchar *buf, gint len)
4361
{
4362
        struct tm *lt;
4363
        time_t t;
4364
        gchar day[4], mon[4];
4365
        gint dd, hh, mm, ss, yyyy;
4366
        gchar off[6];
4367
4368
        t = time(NULL);
4369
        lt = localtime(&t);
4370
4371
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
4372
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
4373
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
4374
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t));
4375
}
4376
4377
/* just a wrapper to suppress the warning of gcc about %c */
4378
size_t my_strftime(gchar *s, size_t max, const gchar *format,
4379
                   const struct tm *tm)
4380
{
4381
        return strftime(s, max, format, tm);
4382
}
4383
4384
/* UI hints */
4385
4386
static UIUpdateFunc ui_update_func = NULL;
4387
4388
void set_ui_update_func(UIUpdateFunc func)
4389
{
4390
        ui_update_func = func;
4391
}
4392
4393
void ui_update(void)
4394
{
4395
        if (ui_update_func)
4396
                ui_update_func();
4397
}
4398
4399
static EventLoopFunc event_loop_func = NULL;
4400
4401
void set_event_loop_func(EventLoopFunc func)
4402
{
4403
        event_loop_func = func;
4404
}
4405
4406
void event_loop_iterate(void)
4407
{
4408
        if (event_loop_func)
4409
                event_loop_func();
4410
        else
4411
                g_main_context_iteration(NULL, TRUE);
4412
}
4413
4414
static ProgressFunc progress_func = NULL;
4415
4416
void set_progress_func(ProgressFunc func)
4417
{
4418
        progress_func = func;
4419
}
4420
4421
void progress_show(gint cur, gint total)
4422
{
4423
        if (progress_func)
4424
                progress_func(cur, total);
4425
}
4426
4427
/* user input */
4428
4429
static QueryPasswordFunc query_password_func = NULL;
4430
4431
void set_input_query_password_func(QueryPasswordFunc func)
4432
{
4433
        query_password_func = func;
4434
}
4435
4436
gchar *input_query_password(const gchar *server, const gchar *user)
4437
{
4438
        if (query_password_func)
4439
                return query_password_func(server, user);
4440
        else
4441
                return NULL;
4442
}
4443
4444
/* logging */
4445
4446
static FILE *log_fp = NULL;
4447
#if USE_THREADS
4448
G_LOCK_DEFINE_STATIC(log_fp);
4449
#define S_LOCK(name)        G_LOCK(name)
4450
#define S_UNLOCK(name)        G_UNLOCK(name)
4451
#else
4452
#define S_LOCK(name)
4453
#define S_UNLOCK(name)
4454
#endif
4455
4456
void set_log_file(const gchar *filename)
4457
{
4458
        S_LOCK(log_fp);
4459
        if (!log_fp) {
4460
                log_fp = g_fopen(filename, "w");
4461
                if (!log_fp)
4462
                        FILE_OP_ERROR(filename, "fopen");
4463
        }
4464
        S_UNLOCK(log_fp);
4465
}
4466
4467
void close_log_file(void)
4468
{
4469
        S_LOCK(log_fp);
4470
        if (log_fp) {
4471
                fclose(log_fp);
4472
                log_fp = NULL;
4473
        }
4474
        S_UNLOCK(log_fp);
4475
}
4476
4477
static guint log_verbosity_count = 0;
4478
4479
void set_log_verbosity(gboolean verbose)
4480
{
4481
        if (verbose)
4482
                log_verbosity_count++;
4483
        else if (log_verbosity_count > 0)
4484
                log_verbosity_count--;
4485
}
4486
4487
gboolean get_debug_mode(void)
4488
{
4489
        return debug_mode;
4490
}
4491
4492
void set_debug_mode(gboolean enable)
4493
{
4494
        debug_mode = enable;
4495
}
4496
4497
static void log_dummy_func(const gchar *str)
4498
{
4499
}
4500
4501
static void log_dummy_flush_func(void)
4502
{
4503
}
4504
4505
static LogFunc log_print_ui_func = log_dummy_func;
4506
static LogFunc log_message_ui_func = log_dummy_func;
4507
static LogFunc log_warning_ui_func = log_dummy_func;
4508
static LogFunc log_error_ui_func = log_dummy_func;
4509
static LogFlushFunc log_flush_ui_func = log_dummy_flush_func;
4510
4511
static LogFunc log_show_status_func = log_dummy_func;
4512
4513
void set_log_ui_func(LogFunc print_func, LogFunc message_func,
4514
                     LogFunc warning_func, LogFunc error_func)
4515
{
4516
        log_print_ui_func = print_func;
4517
        log_message_ui_func = message_func;
4518
        log_warning_ui_func = warning_func;
4519
        log_error_ui_func = error_func;
4520
}
4521
4522
void set_log_ui_func_full(LogFunc print_func, LogFunc message_func,
4523
                          LogFunc warning_func, LogFunc error_func,
4524
                          LogFlushFunc flush_func)
4525
{
4526
        set_log_ui_func(print_func, message_func, warning_func, error_func);
4527
        log_flush_ui_func = flush_func;
4528
}
4529
4530
void set_log_show_status_func(LogFunc status_func)
4531
{
4532
        log_show_status_func = status_func;
4533
}
4534
4535
void debug_print(const gchar *format, ...)
4536
{
4537
        va_list args;
4538
        gchar buf[BUFFSIZE];
4539
4540
        if (!debug_mode) return;
4541
4542
        va_start(args, format);
4543
        g_vsnprintf(buf, sizeof(buf), format, args);
4544
        va_end(args);
4545
4546
        g_print("%s", buf);
4547
}
4548
4549
void status_print(const gchar *format, ...)
4550
{
4551
        va_list args;
4552
        gchar buf[BUFFSIZE];
4553
4554
        va_start(args, format);
4555
        g_vsnprintf(buf, sizeof(buf), format, args);
4556
        va_end(args);
4557
4558
        log_show_status_func(buf);
4559
}
4560
4561
#define TIME_LEN        11
4562
4563
void log_write(const gchar *str, const gchar *prefix)
4564
{
4565
        S_LOCK(log_fp);
4566
4567
        if (log_fp) {
4568
                gchar buf[TIME_LEN + 1];
4569
                time_t t;
4570
4571
                time(&t);
4572
                strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4573
4574
                fputs(buf, log_fp);
4575
                if (prefix)
4576
                        fputs(prefix, log_fp);
4577
                fputs(str, log_fp);
4578
                fflush(log_fp);
4579
        }
4580
4581
        S_UNLOCK(log_fp);
4582
}
4583
4584
void log_print(const gchar *format, ...)
4585
{
4586
        va_list args;
4587
        gchar buf[BUFFSIZE + TIME_LEN];
4588
        time_t t;
4589
4590
        time(&t);
4591
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4592
4593
        va_start(args, format);
4594
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4595
        va_end(args);
4596
4597
        if (debug_mode) g_print("%s", buf);
4598
        log_print_ui_func(buf);
4599
        S_LOCK(log_fp);
4600
        if (log_fp) {
4601
                fputs(buf, log_fp);
4602
                fflush(log_fp);
4603
        }
4604
        S_UNLOCK(log_fp);
4605
        if (log_verbosity_count)
4606
                log_show_status_func(buf + TIME_LEN);
4607
}
4608
4609
void log_message(const gchar *format, ...)
4610
{
4611
        va_list args;
4612
        gchar buf[BUFFSIZE + TIME_LEN];
4613
        time_t t;
4614
4615
        time(&t);
4616
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4617
4618
        va_start(args, format);
4619
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4620
        va_end(args);
4621
4622
        if (debug_mode) g_message("%s", buf + TIME_LEN);
4623
        log_message_ui_func(buf + TIME_LEN);
4624
        S_LOCK(log_fp);
4625
        if (log_fp) {
4626
                fwrite(buf, TIME_LEN, 1, log_fp);
4627
                fputs("* message: ", log_fp);
4628
                fputs(buf + TIME_LEN, log_fp);
4629
                fflush(log_fp);
4630
        }
4631
        S_UNLOCK(log_fp);
4632
        log_show_status_func(buf + TIME_LEN);
4633
}
4634
4635
void log_warning(const gchar *format, ...)
4636
{
4637
        va_list args;
4638
        gchar buf[BUFFSIZE + TIME_LEN];
4639
        time_t t;
4640
4641
        time(&t);
4642
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4643
4644
        va_start(args, format);
4645
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4646
        va_end(args);
4647
4648
        g_warning("%s", buf);
4649
        log_warning_ui_func(buf + TIME_LEN);
4650
        S_LOCK(log_fp);
4651
        if (log_fp) {
4652
                fwrite(buf, TIME_LEN, 1, log_fp);
4653
                fputs("** warning: ", log_fp);
4654
                fputs(buf + TIME_LEN, log_fp);
4655
                fflush(log_fp);
4656
        }
4657
        S_UNLOCK(log_fp);
4658
}
4659
4660
void log_error(const gchar *format, ...)
4661
{
4662
        va_list args;
4663
        gchar buf[BUFFSIZE + TIME_LEN];
4664
        time_t t;
4665
4666
        time(&t);
4667
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4668
4669
        va_start(args, format);
4670
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4671
        va_end(args);
4672
4673
        g_warning("%s", buf);
4674
        log_error_ui_func(buf + TIME_LEN);
4675
        S_LOCK(log_fp);
4676
        if (log_fp) {
4677
                fwrite(buf, TIME_LEN, 1, log_fp);
4678
                fputs("*** error: ", log_fp);
4679
                fputs(buf + TIME_LEN, log_fp);
4680
                fflush(log_fp);
4681
        }
4682
        S_UNLOCK(log_fp);
4683
}
4684
4685
void log_flush(void)
4686
{
4687
        S_LOCK(log_fp);
4688
        if (log_fp)
4689
                fflush(log_fp);
4690
        S_UNLOCK(log_fp);
4691
        log_flush_ui_func();
4692
}