Statistics
| Branch: | Tag: | Revision:

root / libsylph / utils.c @ aebfd4cc

History | View | Annotate | Download (64.6 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2011 Hiroyuki Yamamoto
4
 */
5

    
6
#ifdef HAVE_CONFIG_H
7
#  include "config.h"
8
#endif
9

    
10
#include "defs.h"
11

    
12
#include <glib.h>
13
#include <glib/gi18n.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <ctype.h>
17
#include <errno.h>
18
#include <stdlib.h>
19
#include <sys/stat.h>
20
#include <fcntl.h>
21
#include <unistd.h>
22
#include <stdarg.h>
23
#include <sys/types.h>
24
#if HAVE_SYS_WAIT_H
25
#  include <sys/wait.h>
26
#endif
27
#include <dirent.h>
28
#include <time.h>
29

    
30
#ifdef G_OS_WIN32
31
#ifndef WINVER
32
#  define WINVER 0x0500
33
#endif
34
#  include <windows.h>
35
#  include <wchar.h>
36
#  include <direct.h>
37
#  include <io.h>
38
#  include <shlobj.h>
39
#endif
40

    
41
#include "utils.h"
42

    
43
#define BUFFSIZE        8192
44

    
45
static gboolean debug_mode = FALSE;
46

    
47

    
48
#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
49
gint g_chdir(const gchar *path)
50
{
51
#ifdef G_OS_WIN32
52
        if (G_WIN32_HAVE_WIDECHAR_API()) {
53
                wchar_t *wpath;
54
                gint retval;
55
                gint save_errno;
56

    
57
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
58
                if (wpath == NULL) {
59
                        errno = EINVAL;
60
                        return -1;
61
                }
62

    
63
                retval = _wchdir(wpath);
64
                save_errno = errno;
65

    
66
                g_free(wpath);
67

    
68
                errno = save_errno;
69
                return retval;
70
        } else {
71
                gchar *cp_path;
72
                gint retval;
73
                gint save_errno;
74

    
75
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
76
                if (cp_path == NULL) {
77
                        errno = EINVAL;
78
                        return -1;
79
                }
80

    
81
                retval = chdir(cp_path);
82
                save_errno = errno;
83

    
84
                g_free(cp_path);
85

    
86
                errno = save_errno;
87
                return retval;
88
        }
89
#else
90
        return chdir(path);
91
#endif
92
}
93

    
94
gint g_chmod(const gchar *path, gint mode)
95
{
96
#ifdef G_OS_WIN32
97
        if (G_WIN32_HAVE_WIDECHAR_API()) {
98
                wchar_t *wpath;
99
                gint retval;
100
                gint save_errno;
101

    
102
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
103
                if (wpath == NULL) {
104
                        errno = EINVAL;
105
                        return -1;
106
                }
107

    
108
                retval = _wchmod(wpath, mode);
109
                save_errno = errno;
110

    
111
                g_free(wpath);
112

    
113
                errno = save_errno;
114
                return retval;
115
        } else {
116
                gchar *cp_path;
117
                gint retval;
118
                gint save_errno;
119

    
120
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
121
                if (cp_path == NULL) {
122
                        errno = EINVAL;
123
                        return -1;
124
                }
125

    
126
                retval = chmod(cp_path, mode);
127
                save_errno = errno;
128

    
129
                g_free(cp_path);
130

    
131
                errno = save_errno;
132
                return retval;
133
        }
134
#else
135
        return chmod(path, mode);
136
#endif
137
}
138
#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
139

    
140
#ifndef G_OS_UNIX
141
gint syl_link(const gchar *src, const gchar *dest)
142
{
143
#ifdef G_OS_WIN32
144
        wchar_t *wsrc;
145
        wchar_t *wdest;
146
        gint retval;
147
        gint save_errno;
148

    
149
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
150
        if (wsrc == NULL) {
151
                errno = EINVAL;
152
                return -1;
153
        }
154
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
155
        if (wdest == NULL) {
156
                g_free(wsrc);
157
                errno = EINVAL;
158
                return -1;
159
        }
160

    
161
        errno = 0;
162
        if (CreateHardLinkW(wdest, wsrc, NULL)) {
163
                retval = 0;
164
                /* debug_print("hard link created: %s -> %s\n", src, dest); */
165
        } else {
166
                retval = -1;
167
                switch (GetLastError()) {
168
                case ERROR_FILE_NOT_FOUND:
169
                case ERROR_PATH_NOT_FOUND:
170
                        errno = ENOENT; break;
171
                case ERROR_ACCESS_DENIED:
172
                case ERROR_LOCK_VIOLATION:
173
                case ERROR_SHARING_VIOLATION:
174
                        errno = EACCES; break;
175
                case ERROR_NOT_SAME_DEVICE:
176
                        errno = EXDEV; break;
177
                case ERROR_FILE_EXISTS:
178
                case ERROR_ALREADY_EXISTS:
179
                        errno = EEXIST; break;
180
                case ERROR_TOO_MANY_LINKS:
181
                        errno = EMLINK; break;
182
                default:
183
                        errno = EIO; break;
184
                }
185
        }
186
        save_errno = errno;
187

    
188
        g_free(wdest);
189
        g_free(wsrc);
190

    
191
        errno = save_errno;
192
        return retval;
193
#else
194
        return link(src, dest);
195
#endif
196
}
197
#endif /* !G_OS_UNIX */
198

    
199
void list_free_strings(GList *list)
200
{
201
        list = g_list_first(list);
202

    
203
        while (list != NULL) {
204
                g_free(list->data);
205
                list = list->next;
206
        }
207
}
208

    
209
void slist_free_strings(GSList *list)
210
{
211
        while (list != NULL) {
212
                g_free(list->data);
213
                list = list->next;
214
        }
215
}
216

    
217
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
218
{
219
        g_free(key);
220
}
221

    
222
void hash_free_strings(GHashTable *table)
223
{
224
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
225
}
226

    
227
static void hash_free_value_mem_func(gpointer key, gpointer value,
228
                                     gpointer data)
229
{
230
        g_free(value);
231
}
232

    
233
void hash_free_value_mem(GHashTable *table)
234
{
235
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
236
}
237

    
238
gint str_case_equal(gconstpointer v, gconstpointer v2)
239
{
240
        return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
241
}
242

    
243
guint str_case_hash(gconstpointer key)
244
{
245
        const gchar *p = key;
246
        guint h = *p;
247

    
248
        if (h) {
249
                h = g_ascii_tolower(h);
250
                for (p += 1; *p != '\0'; p++)
251
                        h = (h << 5) - h + g_ascii_tolower(*p);
252
        }
253

    
254
        return h;
255
}
256

    
257
void ptr_array_free_strings(GPtrArray *array)
258
{
259
        gint i;
260
        gchar *str;
261

    
262
        g_return_if_fail(array != NULL);
263

    
264
        for (i = 0; i < array->len; i++) {
265
                str = g_ptr_array_index(array, i);
266
                g_free(str);
267
        }
268
}
269

    
270
gboolean str_find(const gchar *haystack, const gchar *needle)
271
{
272
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
273
}
274

    
275
gboolean str_case_find(const gchar *haystack, const gchar *needle)
276
{
277
        return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
278
}
279

    
280
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
281
{
282
        return strcmp(haystack, needle) == 0;
283
}
284

    
285
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
286
{
287
        return g_ascii_strcasecmp(haystack, needle) == 0;
288
}
289

    
290
gint to_number(const gchar *nstr)
291
{
292
        register const gchar *p;
293

    
294
        if (*nstr == '\0') return -1;
295

    
296
        for (p = nstr; *p != '\0'; p++)
297
                if (!g_ascii_isdigit(*p)) return -1;
298

    
299
        return atoi(nstr);
300
}
301

    
302
guint to_unumber(const gchar *nstr)
303
{
304
        register const gchar *p;
305
        gulong val;
306

    
307
        if (*nstr == '\0') return 0;
308

    
309
        for (p = nstr; *p != '\0'; p++)
310
                if (!g_ascii_isdigit(*p)) return 0;
311

    
312
        errno = 0;
313
        val = strtoul(nstr, NULL, 10);
314
        if (val == ULONG_MAX && errno != 0)
315
                val = 0;
316

    
317
        return (guint)val;
318
}
319

    
320
/* convert integer into string,
321
   nstr must be not lower than 11 characters length */
322
gchar *itos_buf(gchar *nstr, gint n)
323
{
324
        g_snprintf(nstr, 11, "%d", n);
325
        return nstr;
326
}
327

    
328
/* convert integer into string */
329
gchar *itos(gint n)
330
{
331
        static gchar nstr[11];
332

    
333
        return itos_buf(nstr, n);
334
}
335

    
336
gchar *utos_buf(gchar *nstr, guint n)
337
{
338
        g_snprintf(nstr, 11, "%u", n);
339
        return nstr;
340
}
341

    
342
gchar *to_human_readable_buf(gchar *buf, size_t bufsize, gint64 size)
343
{
344
        if (size < 1024)
345
                g_snprintf(buf, bufsize, "%dB", (gint)size);
346
        else if ((size >> 10) < 1024)
347
                g_snprintf(buf, bufsize, "%.1fKB", (gfloat)size / (1 << 10));
348
        else if ((size >> 20) < 1024)
349
                g_snprintf(buf, bufsize, "%.2fMB", (gfloat)size / (1 << 20));
350
        else
351
                g_snprintf(buf, bufsize, "%.2fGB", (gfloat)size / (1 << 30));
352

    
353
        return buf;
354
}
355

    
356
gchar *to_human_readable(gint64 size)
357
{
358
        static gchar str[16];
359

    
360
        return to_human_readable_buf(str, sizeof(str), size);
361
}
362

    
363
/* strcmp with NULL-checking */
364
gint strcmp2(const gchar *s1, const gchar *s2)
365
{
366
        if (s1 == NULL || s2 == NULL)
367
                return -1;
368
        else
369
                return strcmp(s1, s2);
370
}
371

    
372
/* compare paths */
373
gint path_cmp(const gchar *s1, const gchar *s2)
374
{
375
        gint len1, len2;
376
#ifdef G_OS_WIN32
377
        gchar *s1_, *s2_;
378
#endif
379

    
380
        if (s1 == NULL || s2 == NULL) return -1;
381
        if (*s1 == '\0' || *s2 == '\0') return -1;
382

    
383
        len1 = strlen(s1);
384
        len2 = strlen(s2);
385

    
386
#ifdef G_OS_WIN32
387
        Xstrdup_a(s1_, s1, return -1);
388
        Xstrdup_a(s2_, s2, return -1);
389
        subst_char(s1_, '/', G_DIR_SEPARATOR);
390
        subst_char(s2_, '/', G_DIR_SEPARATOR);
391
        if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--;
392
        if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--;
393

    
394
        return strncmp(s1_, s2_, MAX(len1, len2));
395
#else
396
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
397
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
398

    
399
        return strncmp(s1, s2, MAX(len1, len2));
400
#endif
401
}
402

    
403
/* return TRUE if parent is equal to or ancestor of child */
404
gboolean is_path_parent(const gchar *parent, const gchar *child)
405
{
406
        gint plen;
407
        const gchar *base;
408

    
409
        g_return_val_if_fail(parent != NULL, FALSE);
410
        g_return_val_if_fail(child != NULL, FALSE);
411

    
412
        plen = strlen(parent);
413
        while (plen > 0 && G_IS_DIR_SEPARATOR(parent[plen - 1]))
414
                plen--;
415

    
416
#ifdef G_OS_WIN32
417
        if (!g_ascii_strncasecmp(parent, child, plen)) {
418
#else
419
        if (!strncmp(parent, child, plen)) {
420
#endif
421
                base = child + plen;
422
                if (!G_IS_DIR_SEPARATOR(*base) && *base != '\0')
423
                        return FALSE;
424
                return TRUE;
425
        }
426

    
427
        return FALSE;
428
}
429

    
430
/* remove trailing return code */
431
gchar *strretchomp(gchar *str)
432
{
433
        register gchar *s;
434

    
435
        if (!*str) return str;
436

    
437
        for (s = str + strlen(str) - 1;
438
             s >= str && (*s == '\n' || *s == '\r');
439
             s--)
440
                *s = '\0';
441

    
442
        return str;
443
}
444

    
445
/* remove trailing character */
446
gchar *strtailchomp(gchar *str, gchar tail_char)
447
{
448
        register gchar *s;
449

    
450
        if (!*str) return str;
451
        if (tail_char == '\0') return str;
452

    
453
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
454
                *s = '\0';
455

    
456
        return str;
457
}
458

    
459
/* remove CR (carriage return) */
460
gchar *strcrchomp(gchar *str)
461
{
462
        register gchar *s;
463

    
464
        if (!*str) return str;
465

    
466
        s = str + strlen(str) - 1;
467
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
468
                *(s - 1) = '\n';
469
                *s = '\0';
470
        }
471

    
472
        return str;
473
}
474

    
475
/* Similar to `strstr' but this function ignores the case of both strings.  */
476
gchar *strcasestr(const gchar *haystack, const gchar *needle)
477
{
478
        register size_t haystack_len, needle_len;
479

    
480
        haystack_len = strlen(haystack);
481
        needle_len   = strlen(needle);
482

    
483
        if (haystack_len < needle_len || needle_len == 0)
484
                return NULL;
485

    
486
        while (haystack_len >= needle_len) {
487
                if (!g_ascii_strncasecmp(haystack, needle, needle_len))
488
                        return (gchar *)haystack;
489
                else {
490
                        haystack++;
491
                        haystack_len--;
492
                }
493
        }
494

    
495
        return NULL;
496
}
497

    
498
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
499
                   gconstpointer needle, size_t needlelen)
500
{
501
        const gchar *haystack_ = (const gchar *)haystack;
502
        const gchar *needle_ = (const gchar *)needle;
503
        const gchar *haystack_cur = (const gchar *)haystack;
504
        size_t haystack_left = haystacklen;
505

    
506
        if (needlelen == 1)
507
                return memchr(haystack_, *needle_, haystacklen);
508

    
509
        while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
510
               != NULL) {
511
                if (haystacklen - (haystack_cur - haystack_) < needlelen)
512
                        break;
513
                if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
514
                        return (gpointer)haystack_cur;
515
                else {
516
                        haystack_cur++;
517
                        haystack_left = haystacklen - (haystack_cur - haystack_);
518
                }
519
        }
520

    
521
        return NULL;
522
}
523

    
524
/* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
525
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
526
{
527
        register const gchar *s = src;
528
        register gchar *d = dest;
529

    
530
        while (--n && *s)
531
                *d++ = *s++;
532
        *d = '\0';
533

    
534
        return dest;
535
}
536

    
537
/* Similar to g_str_has_suffix() but case-insensitive */
538
gboolean str_has_suffix_case(const gchar *str, const gchar *suffix)
539
{
540
        size_t len, s_len;
541

    
542
        if (!str || !suffix)
543
                return FALSE;
544

    
545
        len = strlen(str);
546
        s_len = strlen(suffix);
547

    
548
        if (s_len > len)
549
                return FALSE;
550

    
551
        return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0);
552
}
553

    
554
gint str_find_format_times(const gchar *haystack, gchar ch)
555
{
556
        gint n = 0;
557
        const gchar *p = haystack;
558

    
559
        while ((p = strchr(p, '%')) != NULL) {
560
                ++p;
561
                if (*p == '%') {
562
                        ++p;
563
                } else if (*p == ch) {
564
                        ++p;
565
                        ++n;
566
                } else
567
                        return -1;
568
        }
569

    
570
        return n;
571
}
572

    
573
/* Examine if next block is non-ASCII string */
574
gboolean is_next_nonascii(const gchar *s)
575
{
576
        const gchar *p;
577
        gboolean in_quote = FALSE;
578

    
579
        /* skip head space */
580
        for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
581
                ;
582
        while (*p != '\0') {
583
                if (!in_quote && g_ascii_isspace(*p))
584
                        break;
585
                if (*p == '"')
586
                        in_quote ^= TRUE;
587
                else if (*(guchar *)p > 127 || *(guchar *)p < 32)
588
                        return TRUE;
589
                ++p;
590
        }
591

    
592
        return FALSE;
593
}
594

    
595
gint get_next_word_len(const gchar *s)
596
{
597
        const gchar *p = s;
598
        gboolean in_quote = FALSE;
599

    
600
        while (*p != '\0') {
601
                if (!in_quote && g_ascii_isspace(*p))
602
                        break;
603
                if (*p == '"')
604
                        in_quote ^= TRUE;
605
                ++p;
606
        }
607

    
608
        return p - s;
609
}
610

    
611
/* compare subjects */
612
gint subject_compare(const gchar *s1, const gchar *s2)
613
{
614
        gchar *str1, *str2;
615

    
616
        if (!s1 || !s2) return -1;
617
        if (!*s1 || !*s2) return -1;
618

    
619
        Xstrdup_a(str1, s1, return -1);
620
        Xstrdup_a(str2, s2, return -1);
621

    
622
        trim_subject_for_compare(str1);
623
        trim_subject_for_compare(str2);
624

    
625
        if (!*str1 || !*str2) return -1;
626

    
627
        return strcmp(str1, str2);
628
}
629

    
630
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
631
{
632
        gchar *str1, *str2;
633

    
634
        if (!s1 || !s2) return -1;
635

    
636
        Xstrdup_a(str1, s1, return -1);
637
        Xstrdup_a(str2, s2, return -1);
638

    
639
        trim_subject_for_sort(str1);
640
        trim_subject_for_sort(str2);
641

    
642
        return g_ascii_strcasecmp(str1, str2);
643
}
644

    
645
void trim_subject_for_compare(gchar *str)
646
{
647
        gchar *srcp;
648

    
649
        eliminate_parenthesis(str, '[', ']');
650
        eliminate_parenthesis(str, '(', ')');
651
        g_strstrip(str);
652

    
653
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
654
                srcp = str + 3;
655
                while (g_ascii_isspace(*srcp)) srcp++;
656
                memmove(str, srcp, strlen(srcp) + 1);
657
        }
658
}
659

    
660
void trim_subject_for_sort(gchar *str)
661
{
662
        gchar *srcp;
663

    
664
        g_strstrip(str);
665

    
666
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
667
                srcp = str + 3;
668
                while (g_ascii_isspace(*srcp)) srcp++;
669
                memmove(str, srcp, strlen(srcp) + 1);
670
        }
671
}
672

    
673
void trim_subject(gchar *str)
674
{
675
        register gchar *srcp, *destp;
676
        gchar op, cl;
677
        gint in_brace;
678

    
679
        destp = str;
680
        while (!g_ascii_strncasecmp(destp, "Re:", 3)) {
681
                destp += 3;
682
                while (g_ascii_isspace(*destp)) destp++;
683
        }
684

    
685
        if (*destp == '[') {
686
                op = '[';
687
                cl = ']';
688
        } else if (*destp == '(') {
689
                op = '(';
690
                cl = ')';
691
        } else
692
                return;
693

    
694
        srcp = destp + 1;
695
        in_brace = 1;
696
        while (*srcp) {
697
                if (*srcp == op)
698
                        in_brace++;
699
                else if (*srcp == cl)
700
                        in_brace--;
701
                srcp++;
702
                if (in_brace == 0)
703
                        break;
704
        }
705
        while (g_ascii_isspace(*srcp)) srcp++;
706
        memmove(destp, srcp, strlen(srcp) + 1);
707
}
708

    
709
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
710
{
711
        register gchar *srcp, *destp;
712
        gint in_brace;
713

    
714
        srcp = destp = str;
715

    
716
        while ((destp = strchr(destp, op))) {
717
                in_brace = 1;
718
                srcp = destp + 1;
719
                while (*srcp) {
720
                        if (*srcp == op)
721
                                in_brace++;
722
                        else if (*srcp == cl)
723
                                in_brace--;
724
                        srcp++;
725
                        if (in_brace == 0)
726
                                break;
727
                }
728
                while (g_ascii_isspace(*srcp)) srcp++;
729
                memmove(destp, srcp, strlen(srcp) + 1);
730
        }
731
}
732

    
733
void extract_parenthesis(gchar *str, gchar op, gchar cl)
734
{
735
        register gchar *srcp, *destp;
736
        gint in_brace;
737

    
738
        srcp = destp = str;
739

    
740
        while ((srcp = strchr(destp, op))) {
741
                if (destp > str)
742
                        *destp++ = ' ';
743
                memmove(destp, srcp + 1, strlen(srcp));
744
                in_brace = 1;
745
                while(*destp) {
746
                        if (*destp == op)
747
                                in_brace++;
748
                        else if (*destp == cl)
749
                                in_brace--;
750

    
751
                        if (in_brace == 0)
752
                                break;
753

    
754
                        destp++;
755
                }
756
        }
757
        *destp = '\0';
758
}
759

    
760
void extract_parenthesis_with_escape(gchar *str, gchar op, gchar cl)
761
{
762
        register gchar *srcp, *destp;
763
        gint in_brace;
764

    
765
        srcp = destp = str;
766

    
767
        while ((srcp = strchr(srcp, op))) {
768
                if (destp > str)
769
                        *destp++ = ' ';
770
                ++srcp;
771
                in_brace = 1;
772
                while (*srcp) {
773
                        if (*srcp == op)
774
                                in_brace++;
775
                        else if (*srcp == cl)
776
                                in_brace--;
777

    
778
                        if (in_brace == 0)
779
                                break;
780

    
781
                        if (*srcp == '\\' && *(srcp + 1) != '\0')
782
                                ++srcp;
783

    
784
                        *destp++ = *srcp++;
785
                }
786
        }
787
        *destp = '\0';
788
}
789

    
790
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
791
                                         gchar op, gchar cl)
792
{
793
        register gchar *srcp, *destp;
794
        gint in_brace;
795
        gboolean in_quote = FALSE;
796

    
797
        srcp = destp = str;
798

    
799
        while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
800
                if (destp > str)
801
                        *destp++ = ' ';
802
                memmove(destp, srcp + 1, strlen(srcp));
803
                in_brace = 1;
804
                while(*destp) {
805
                        if (*destp == op && !in_quote)
806
                                in_brace++;
807
                        else if (*destp == cl && !in_quote)
808
                                in_brace--;
809
                        else if (*destp == quote_chr)
810
                                in_quote ^= TRUE;
811

    
812
                        if (in_brace == 0)
813
                                break;
814

    
815
                        destp++;
816
                }
817
        }
818
        *destp = '\0';
819
}
820

    
821
void eliminate_quote(gchar *str, gchar quote_chr)
822
{
823
        register gchar *srcp, *destp;
824

    
825
        srcp = destp = str;
826

    
827
        while ((destp = strchr(destp, quote_chr))) {
828
                if ((srcp = strchr(destp + 1, quote_chr))) {
829
                        srcp++;
830
                        while (g_ascii_isspace(*srcp)) srcp++;
831
                        memmove(destp, srcp, strlen(srcp) + 1);
832
                } else {
833
                        *destp = '\0';
834
                        break;
835
                }
836
        }
837
}
838

    
839
void extract_quote(gchar *str, gchar quote_chr)
840
{
841
        register gchar *p;
842

    
843
        if ((str = strchr(str, quote_chr))) {
844
                if ((p = strchr(str + 1, quote_chr))) {
845
                        *p = '\0';
846
                        memmove(str, str + 1, p - str);
847
                }
848
        }
849
}
850

    
851
void extract_quote_with_escape(gchar *str, gchar quote_chr)
852
{
853
        register gchar *sp, *dp;
854

    
855
        if ((sp = strchr(str, quote_chr))) {
856
                dp = sp;
857
                ++sp;
858
                while (*sp) {
859
                        if (*sp == quote_chr)
860
                                break;
861
                        else if (*sp == '\\' && *(sp + 1) != '\0')
862
                                ++sp;
863

    
864
                        *dp++ = *sp++;
865
                }
866
                *dp = '\0';
867
        }
868
}
869

    
870
void eliminate_address_comment(gchar *str)
871
{
872
        register gchar *srcp, *destp;
873
        gint in_brace;
874

    
875
        srcp = destp = str;
876

    
877
        while ((destp = strchr(destp, '"'))) {
878
                if ((srcp = strchr(destp + 1, '"'))) {
879
                        srcp++;
880
                        if (*srcp == '@') {
881
                                destp = srcp + 1;
882
                        } else {
883
                                while (g_ascii_isspace(*srcp)) srcp++;
884
                                memmove(destp, srcp, strlen(srcp) + 1);
885
                        }
886
                } else {
887
                        *destp = '\0';
888
                        break;
889
                }
890
        }
891

    
892
        srcp = destp = str;
893

    
894
        while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
895
                in_brace = 1;
896
                srcp = destp + 1;
897
                while (*srcp) {
898
                        if (*srcp == '(')
899
                                in_brace++;
900
                        else if (*srcp == ')')
901
                                in_brace--;
902
                        srcp++;
903
                        if (in_brace == 0)
904
                                break;
905
                }
906
                while (g_ascii_isspace(*srcp)) srcp++;
907
                memmove(destp, srcp, strlen(srcp) + 1);
908
        }
909
}
910

    
911
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
912
{
913
        gboolean in_quote = FALSE;
914

    
915
        while (*str) {
916
                if (*str == c && !in_quote)
917
                        return (gchar *)str;
918
                if (*str == quote_chr)
919
                        in_quote ^= TRUE;
920
                str++;
921
        }
922

    
923
        return NULL;
924
}
925

    
926
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
927
{
928
        gboolean in_quote = FALSE;
929
        const gchar *p;
930

    
931
        p = str + strlen(str) - 1;
932
        while (p >= str) {
933
                if (*p == c && !in_quote)
934
                        return (gchar *)p;
935
                if (*p == quote_chr)
936
                        in_quote ^= TRUE;
937
                p--;
938
        }
939

    
940
        return NULL;
941
}
942

    
943
void extract_address(gchar *str)
944
{
945
        eliminate_address_comment(str);
946
        if (strchr_with_skip_quote(str, '"', '<'))
947
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
948
        g_strstrip(str);
949
}
950

    
951
void extract_list_id_str(gchar *str)
952
{
953
        if (strchr_with_skip_quote(str, '"', '<'))
954
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
955
        g_strstrip(str);
956
}
957

    
958
gchar *extract_addresses(const gchar *str)
959
{
960
        GString *new_str;
961
        GSList *addr_list, *cur;
962

    
963
        if (!str)
964
                return NULL;
965

    
966
        addr_list = address_list_append(NULL, str);
967

    
968
        new_str = g_string_new(NULL);
969

    
970
        for (cur = addr_list; cur != NULL; cur = cur->next) {
971
                g_string_append(new_str, (gchar *)cur->data);
972
                if (cur->next)
973
                        g_string_append(new_str, ", ");
974
        }
975

    
976
        slist_free_strings(addr_list);
977
        g_slist_free(addr_list);
978

    
979
        return g_string_free(new_str, FALSE);
980
}
981

    
982
gchar *normalize_address_field(const gchar *str)
983
{
984
        GString *new_str;
985
        GSList *addr_list, *cur;
986
        gchar *addr, *p, *q, *r;
987
        gchar *ret_str;
988

    
989
        addr_list = address_list_append_orig(NULL, str);
990

    
991
        new_str = g_string_new(NULL);
992

    
993
        for (cur = addr_list; cur != NULL; cur = cur->next) {
994
                p = addr = (gchar *)cur->data;
995
                q = strchr_with_skip_quote(p, '"', '<');
996
                if (q && q > p) {
997
                        r = q - 1;
998
                        while (r > p && g_ascii_isspace(*r))
999
                                --r;
1000
                        g_string_append_len(new_str, p, r - p + 1);
1001
                        g_string_append_c(new_str, ' ');
1002
                        p = q;
1003
                }
1004
                if (*p == '<') {
1005
                        q = strchr(p, '>');
1006
                        if (q) {
1007
                                r = q + 1;
1008
                                if (*r) {
1009
                                        while (g_ascii_isspace(*r))
1010
                                                ++r;
1011
                                        g_string_append(new_str, r);
1012
                                        if (new_str->len > 0 &&
1013
                                            !g_ascii_isspace
1014
                                                (new_str->str[new_str->len - 1]))
1015
                                                g_string_append_c(new_str, ' ');
1016
                                }
1017
                                g_string_append_len(new_str, p, q - p + 1);
1018
                        } else {
1019
                                g_string_append(new_str, p);
1020
                                g_string_append_c(new_str, '>');
1021
                        }
1022
                } else
1023
                        g_string_append(new_str, p);
1024

    
1025
                if (cur->next)
1026
                        g_string_append(new_str, ", ");
1027
        }
1028

    
1029
        slist_free_strings(addr_list);
1030
        ret_str = new_str->str;
1031
        g_string_free(new_str, FALSE);
1032

    
1033
        return ret_str;
1034
}
1035

    
1036
gboolean address_equal(const gchar *addr1, const gchar *addr2)
1037
{
1038
        gchar *addr1_, *addr2_;
1039

    
1040
        if (!addr1 || !addr2)
1041
                return FALSE;
1042

    
1043
        Xstrdup_a(addr1_, addr1, return FALSE);
1044
        Xstrdup_a(addr2_, addr2, return FALSE);
1045

    
1046
        extract_address(addr1_);
1047
        extract_address(addr2_);
1048

    
1049
        return strcmp(addr1_, addr2_) == 0;
1050
}
1051

    
1052
GSList *address_list_append_orig(GSList *addr_list, const gchar *str)
1053
{
1054
        const gchar *p = str, *q;
1055
        gchar *addr;
1056

    
1057
        if (!str) return addr_list;
1058

    
1059
        while (*p) {
1060
                if (*p == ',' || g_ascii_isspace(*p)) {
1061
                        ++p;
1062
                } else if ((q = strchr_with_skip_quote(p, '"', ','))) {
1063
                        addr = g_strndup(p, q - p);
1064
                        g_strstrip(addr);
1065
                        addr_list = g_slist_append(addr_list, addr);
1066
                        p = q + 1;
1067
                } else {
1068
                        addr = g_strdup(p);
1069
                        g_strstrip(addr);
1070
                        addr_list = g_slist_append(addr_list, addr);
1071
                        break;
1072
                }
1073
        }
1074

    
1075
        return addr_list;
1076
}
1077

    
1078
GSList *address_list_append(GSList *addr_list, const gchar *str)
1079
{
1080
        gchar *work;
1081
        gchar *workp;
1082

    
1083
        if (!str) return addr_list;
1084

    
1085
        Xstrdup_a(work, str, return addr_list);
1086

    
1087
        eliminate_address_comment(work);
1088
        workp = work;
1089

    
1090
        while (workp && *workp) {
1091
                gchar *p, *next;
1092

    
1093
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1094
                        *p = '\0';
1095
                        next = p + 1;
1096
                } else
1097
                        next = NULL;
1098

    
1099
                if (strchr_with_skip_quote(workp, '"', '<'))
1100
                        extract_parenthesis_with_skip_quote
1101
                                (workp, '"', '<', '>');
1102

    
1103
                g_strstrip(workp);
1104
                if (*workp)
1105
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
1106

    
1107
                workp = next;
1108
        }
1109

    
1110
        return addr_list;
1111
}
1112

    
1113
GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1114
{
1115
        const gchar *strp;
1116

    
1117
        if (!str) return msgid_list;
1118
        strp = str;
1119

    
1120
        while (strp && *strp) {
1121
                const gchar *start, *end;
1122
                gchar *msgid;
1123

    
1124
                if ((start = strchr(strp, '<')) != NULL) {
1125
                        end = strchr(start + 1, '>');
1126
                        if (!end) break;
1127
                } else
1128
                        break;
1129

    
1130
                msgid = g_strndup(start + 1, end - start - 1);
1131
                g_strstrip(msgid);
1132
                if (*msgid)
1133
                        msgid_list = g_slist_prepend(msgid_list, msgid);
1134
                else
1135
                        g_free(msgid);
1136

    
1137
                strp = end + 1;
1138
        }
1139

    
1140
        return msgid_list;
1141
}
1142

    
1143
GSList *references_list_append(GSList *msgid_list, const gchar *str)
1144
{
1145
        GSList *list;
1146

    
1147
        list = references_list_prepend(NULL, str);
1148
        list = g_slist_reverse(list);
1149
        msgid_list = g_slist_concat(msgid_list, list);
1150

    
1151
        return msgid_list;
1152
}
1153

    
1154
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1155
{
1156
        gchar *work;
1157
        gchar *workp;
1158

    
1159
        if (!str) return group_list;
1160

    
1161
        Xstrdup_a(work, str, return group_list);
1162

    
1163
        workp = work;
1164

    
1165
        while (workp && *workp) {
1166
                gchar *p, *next;
1167

    
1168
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1169
                        *p = '\0';
1170
                        next = p + 1;
1171
                } else
1172
                        next = NULL;
1173

    
1174
                g_strstrip(workp);
1175
                if (*workp)
1176
                        group_list = g_slist_append(group_list,
1177
                                                    g_strdup(workp));
1178

    
1179
                workp = next;
1180
        }
1181

    
1182
        return group_list;
1183
}
1184

    
1185
GList *add_history(GList *list, const gchar *str)
1186
{
1187
        GList *old;
1188

    
1189
        g_return_val_if_fail(str != NULL, list);
1190

    
1191
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1192
        if (old) {
1193
                g_free(old->data);
1194
                list = g_list_remove(list, old->data);
1195
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1196
                GList *last;
1197

    
1198
                last = g_list_last(list);
1199
                if (last) {
1200
                        g_free(last->data);
1201
                        list = g_list_remove(list, last->data);
1202
                }
1203
        }
1204

    
1205
        list = g_list_prepend(list, g_strdup(str));
1206

    
1207
        return list;
1208
}
1209

    
1210
void remove_return(gchar *str)
1211
{
1212
        register gchar *p = str;
1213

    
1214
        while (*p) {
1215
                if (*p == '\n' || *p == '\r')
1216
                        memmove(p, p + 1, strlen(p));
1217
                else
1218
                        p++;
1219
        }
1220
}
1221

    
1222
void remove_space(gchar *str)
1223
{
1224
        register gchar *p = str;
1225
        register gint spc;
1226

    
1227
        while (*p) {
1228
                spc = 0;
1229
                while (g_ascii_isspace(*(p + spc)))
1230
                        spc++;
1231
                if (spc)
1232
                        memmove(p, p + spc, strlen(p + spc) + 1);
1233
                else
1234
                        p++;
1235
        }
1236
}
1237

    
1238
void unfold_line(gchar *str)
1239
{
1240
        register gchar *p = str;
1241
        register gint spc;
1242

    
1243
        while (*p) {
1244
                if (*p == '\n' || *p == '\r') {
1245
                        *p++ = ' ';
1246
                        spc = 0;
1247
                        while (g_ascii_isspace(*(p + spc)))
1248
                                spc++;
1249
                        if (spc)
1250
                                memmove(p, p + spc, strlen(p + spc) + 1);
1251
                } else
1252
                        p++;
1253
        }
1254
}
1255

    
1256
void subst_char(gchar *str, gchar orig, gchar subst)
1257
{
1258
        register gchar *p = str;
1259

    
1260
        while (*p) {
1261
                if (*p == orig)
1262
                        *p = subst;
1263
                p++;
1264
        }
1265
}
1266

    
1267
void subst_chars(gchar *str, gchar *orig, gchar subst)
1268
{
1269
        register gchar *p = str;
1270

    
1271
        while (*p) {
1272
                if (strchr(orig, *p) != NULL)
1273
                        *p = subst;
1274
                ++p;
1275
        }
1276
}
1277

    
1278
void subst_null(gchar *str, gint len, gchar subst)
1279
{
1280
        register gchar *p = str;
1281

    
1282
        while (len--) {
1283
                if (*p == '\0')
1284
                        *p = subst;
1285
                ++p;
1286
        }
1287
}
1288

    
1289
void subst_control(gchar *str, gchar subst)
1290
{
1291
        register gchar *p = str;
1292

    
1293
        while (*p) {
1294
                if (g_ascii_iscntrl(*p))
1295
                        *p = subst;
1296
                ++p;
1297
        }
1298
}
1299

    
1300
void subst_for_filename(gchar *str)
1301
{
1302
        subst_chars(str, " \t\r\n\"'\\/:;*?<>|", '_');
1303
}
1304

    
1305
gchar *get_alt_filename(const gchar *filename, gint count)
1306
{
1307
        const gchar *ext;
1308
        gchar *alt_filename;
1309

    
1310
        ext = strrchr(filename, '.');
1311

    
1312
        if (ext) {
1313
                gchar *base;
1314

    
1315
                base = g_strndup(filename, ext - filename);
1316
                alt_filename = g_strdup_printf("%s-%d%s", base, count, ext);
1317
                g_free(base);
1318
        } else
1319
                alt_filename = g_strdup_printf("%s-%d", filename, count);
1320

    
1321
        return alt_filename;
1322
}
1323

    
1324
gboolean is_header_line(const gchar *str)
1325
{
1326
        if (str[0] == ':') return FALSE;
1327

    
1328
        while (*str != '\0' && *str != ' ') {
1329
                if (*str == ':')
1330
                        return TRUE;
1331
                str++;
1332
        }
1333

    
1334
        return FALSE;
1335
}
1336

    
1337
gboolean is_ascii_str(const gchar *str)
1338
{
1339
        const guchar *p = (const guchar *)str;
1340

    
1341
        while (*p != '\0') {
1342
                if (*p != '\t' && *p != ' ' &&
1343
                    *p != '\r' && *p != '\n' &&
1344
                    (*p < 32 || *p >= 127))
1345
                        return FALSE;
1346
                p++;
1347
        }
1348

    
1349
        return TRUE;
1350
}
1351

    
1352
gint get_quote_level(const gchar *str)
1353
{
1354
        const gchar *first_pos;
1355
        const gchar *last_pos;
1356
        const gchar *p = str;
1357
        gint quote_level = -1;
1358

    
1359
        /* speed up line processing by only searching to the last '>' */
1360
        if ((first_pos = strchr(str, '>')) != NULL) {
1361
                /* skip a line if it contains a '<' before the initial '>' */
1362
                if (memchr(str, '<', first_pos - str) != NULL)
1363
                        return -1;
1364
                last_pos = strrchr(first_pos, '>');
1365
        } else
1366
                return -1;
1367

    
1368
        while (p <= last_pos) {
1369
                while (p < last_pos) {
1370
                        if (g_ascii_isspace(*p))
1371
                                p++;
1372
                        else
1373
                                break;
1374
                }
1375

    
1376
                if (*p == '>')
1377
                        quote_level++;
1378
                else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1379
                        /* any characters are allowed except '-' and space */
1380
                        while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) &&
1381
                               p < last_pos)
1382
                                p++;
1383
                        if (*p == '>')
1384
                                quote_level++;
1385
                        else
1386
                                break;
1387
                }
1388

    
1389
                p++;
1390
        }
1391

    
1392
        return quote_level;
1393
}
1394

    
1395
gint check_line_length(const gchar *str, gint max_chars, gint *line)
1396
{
1397
        const gchar *p = str, *q;
1398
        gint cur_line = 0, len;
1399

    
1400
        while ((q = strchr(p, '\n')) != NULL) {
1401
                len = q - p + 1;
1402
                if (len > max_chars) {
1403
                        if (line)
1404
                                *line = cur_line;
1405
                        return -1;
1406
                }
1407
                p = q + 1;
1408
                ++cur_line;
1409
        }
1410

    
1411
        len = strlen(p);
1412
        if (len > max_chars) {
1413
                if (line)
1414
                        *line = cur_line;
1415
                return -1;
1416
        }
1417

    
1418
        return 0;
1419
}
1420

    
1421
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1422
{
1423
        register guint haystack_len, needle_len;
1424
        gboolean in_squote = FALSE, in_dquote = FALSE;
1425

    
1426
        haystack_len = strlen(haystack);
1427
        needle_len   = strlen(needle);
1428

    
1429
        if (haystack_len < needle_len || needle_len == 0)
1430
                return NULL;
1431

    
1432
        while (haystack_len >= needle_len) {
1433
                if (!in_squote && !in_dquote &&
1434
                    !strncmp(haystack, needle, needle_len))
1435
                        return (gchar *)haystack;
1436

    
1437
                /* 'foo"bar"' -> foo"bar"
1438
                   "foo'bar'" -> foo'bar' */
1439
                if (*haystack == '\'') {
1440
                        if (in_squote)
1441
                                in_squote = FALSE;
1442
                        else if (!in_dquote)
1443
                                in_squote = TRUE;
1444
                } else if (*haystack == '\"') {
1445
                        if (in_dquote)
1446
                                in_dquote = FALSE;
1447
                        else if (!in_squote)
1448
                                in_dquote = TRUE;
1449
                }
1450

    
1451
                haystack++;
1452
                haystack_len--;
1453
        }
1454

    
1455
        return NULL;
1456
}
1457

    
1458
gchar *strcasestr_with_skip_quote(const gchar *haystack, const gchar *needle)
1459
{
1460
        register guint haystack_len, needle_len;
1461
        gboolean in_squote = FALSE, in_dquote = FALSE;
1462

    
1463
        haystack_len = strlen(haystack);
1464
        needle_len   = strlen(needle);
1465

    
1466
        if (haystack_len < needle_len || needle_len == 0)
1467
                return NULL;
1468

    
1469
        while (haystack_len >= needle_len) {
1470
                if (!in_squote && !in_dquote &&
1471
                    !g_ascii_strncasecmp(haystack, needle, needle_len))
1472
                        return (gchar *)haystack;
1473

    
1474
                /* 'foo"bar"' -> foo"bar"
1475
                   "foo'bar'" -> foo'bar' */
1476
                if (*haystack == '\'') {
1477
                        if (in_squote)
1478
                                in_squote = FALSE;
1479
                        else if (!in_dquote)
1480
                                in_squote = TRUE;
1481
                } else if (*haystack == '\"') {
1482
                        if (in_dquote)
1483
                                in_dquote = FALSE;
1484
                        else if (!in_squote)
1485
                                in_dquote = TRUE;
1486
                }
1487

    
1488
                haystack++;
1489
                haystack_len--;
1490
        }
1491

    
1492
        return NULL;
1493
}
1494

    
1495
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1496
{
1497
        const gchar *p;
1498
        gchar quote_chr = '"';
1499
        gint in_brace;
1500
        gboolean in_quote = FALSE;
1501

    
1502
        p = str;
1503

    
1504
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1505
                p++;
1506
                in_brace = 1;
1507
                while (*p) {
1508
                        if (*p == op && !in_quote)
1509
                                in_brace++;
1510
                        else if (*p == cl && !in_quote)
1511
                                in_brace--;
1512
                        else if (*p == quote_chr)
1513
                                in_quote ^= TRUE;
1514

    
1515
                        if (in_brace == 0)
1516
                                return (gchar *)p;
1517

    
1518
                        p++;
1519
                }
1520
        }
1521

    
1522
        return NULL;
1523
}
1524

    
1525
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1526
                             gint max_tokens)
1527
{
1528
        GSList *string_list = NULL, *slist;
1529
        gchar **str_array;
1530
        const gchar *s_op, *s_cl;
1531
        guint i, n = 1;
1532

    
1533
        g_return_val_if_fail(str != NULL, NULL);
1534

    
1535
        if (max_tokens < 1)
1536
                max_tokens = G_MAXINT;
1537

    
1538
        s_op = strchr_with_skip_quote(str, '"', op);
1539
        if (!s_op) return NULL;
1540
        str = s_op;
1541
        s_cl = strchr_parenthesis_close(str, op, cl);
1542
        if (s_cl) {
1543
                do {
1544
                        guint len;
1545
                        gchar *new_string;
1546

    
1547
                        str++;
1548
                        len = s_cl - str;
1549
                        new_string = g_new(gchar, len + 1);
1550
                        strncpy(new_string, str, len);
1551
                        new_string[len] = 0;
1552
                        string_list = g_slist_prepend(string_list, new_string);
1553
                        n++;
1554
                        str = s_cl + 1;
1555

    
1556
                        while (*str && g_ascii_isspace(*str)) str++;
1557
                        if (*str != op) {
1558
                                string_list = g_slist_prepend(string_list,
1559
                                                              g_strdup(""));
1560
                                n++;
1561
                                s_op = strchr_with_skip_quote(str, '"', op);
1562
                                if (!--max_tokens || !s_op) break;
1563
                                str = s_op;
1564
                        } else
1565
                                s_op = str;
1566
                        s_cl = strchr_parenthesis_close(str, op, cl);
1567
                } while (--max_tokens && s_cl);
1568
        }
1569

    
1570
        str_array = g_new(gchar*, n);
1571

    
1572
        i = n - 1;
1573
        str_array[i--] = NULL;
1574
        for (slist = string_list; slist; slist = slist->next)
1575
                str_array[i--] = slist->data;
1576

    
1577
        g_slist_free(string_list);
1578

    
1579
        return str_array;
1580
}
1581

    
1582
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1583
                            gint max_tokens)
1584
{
1585
        GSList *string_list = NULL, *slist;
1586
        gchar **str_array, *s, *new_str;
1587
        guint i, n = 1, len;
1588

    
1589
        g_return_val_if_fail(str != NULL, NULL);
1590
        g_return_val_if_fail(delim != NULL, NULL);
1591

    
1592
        if (max_tokens < 1)
1593
                max_tokens = G_MAXINT;
1594

    
1595
        s = strstr_with_skip_quote(str, delim);
1596
        if (s) {
1597
                guint delimiter_len = strlen(delim);
1598

    
1599
                do {
1600
                        len = s - str;
1601
                        new_str = g_strndup(str, len);
1602

    
1603
                        if (new_str[0] == '\'' || new_str[0] == '\"') {
1604
                                if (new_str[len - 1] == new_str[0]) {
1605
                                        new_str[len - 1] = '\0';
1606
                                        memmove(new_str, new_str + 1, len - 1);
1607
                                }
1608
                        }
1609
                        string_list = g_slist_prepend(string_list, new_str);
1610
                        n++;
1611
                        str = s + delimiter_len;
1612
                        s = strstr_with_skip_quote(str, delim);
1613
                } while (--max_tokens && s);
1614
        }
1615

    
1616
        if (*str) {
1617
                new_str = g_strdup(str);
1618
                if (new_str[0] == '\'' || new_str[0] == '\"') {
1619
                        len = strlen(str);
1620
                        if (new_str[len - 1] == new_str[0]) {
1621
                                new_str[len - 1] = '\0';
1622
                                memmove(new_str, new_str + 1, len - 1);
1623
                        }
1624
                }
1625
                string_list = g_slist_prepend(string_list, new_str);
1626
                n++;
1627
        }
1628

    
1629
        str_array = g_new(gchar*, n);
1630

    
1631
        i = n - 1;
1632
        str_array[i--] = NULL;
1633
        for (slist = string_list; slist; slist = slist->next)
1634
                str_array[i--] = slist->data;
1635

    
1636
        g_slist_free(string_list);
1637

    
1638
        return str_array;
1639
}
1640

    
1641
gchar **strsplit_csv(const gchar *str, gchar delim, gint max_tokens)
1642
{
1643
        GSList *string_list = NULL, *slist;
1644
        gchar **str_array, *s, *new_str;
1645
        gchar *tmp, *tmpp, *p;
1646
        guint i, n = 1, len;
1647

    
1648
        g_return_val_if_fail(str != NULL, NULL);
1649

    
1650
        if (max_tokens < 1)
1651
                max_tokens = G_MAXINT;
1652

    
1653
        s = strchr_with_skip_quote(str, '"', delim);
1654
        if (s) {
1655
                do {
1656
                        len = s - str;
1657
                        tmpp = tmp = g_strndup(str, len);
1658

    
1659
                        if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1660
                                tmp[len - 1] = '\0';
1661
                                ++tmpp;
1662
                                p = new_str = g_malloc(len - 1);
1663
                                while (*tmpp) {
1664
                                        if (*tmpp == '"' && *(tmpp + 1) == '"')
1665
                                                ++tmpp;
1666
                                        *p++ = *tmpp++;
1667
                                }
1668
                                *p = '\0';
1669
                                g_free(tmp);
1670
                        } else
1671
                                new_str = tmp;
1672

    
1673
                        string_list = g_slist_prepend(string_list, new_str);
1674
                        n++;
1675
                        str = s + 1;
1676
                        s = strchr_with_skip_quote(str, '"', delim);
1677
                } while (--max_tokens && s);
1678
        }
1679

    
1680
        if (*str) {
1681
                len = strlen(str);
1682
                tmpp = tmp = g_strdup(str);
1683

    
1684
                if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1685
                        tmp[len - 1] = '\0';
1686
                        ++tmpp;
1687
                        p = new_str = g_malloc(len - 1);
1688
                        while (*tmpp) {
1689
                                if (*tmpp == '"' && *(tmpp + 1) == '"')
1690
                                        ++tmpp;
1691
                                *p++ = *tmpp++;
1692
                        }
1693
                        *p = '\0';
1694
                        g_free(tmp);
1695
                } else
1696
                        new_str = tmp;
1697

    
1698
                string_list = g_slist_prepend(string_list, new_str);
1699
                n++;
1700
        }
1701

    
1702
        str_array = g_new(gchar*, n);
1703

    
1704
        i = n - 1;
1705
        str_array[i--] = NULL;
1706
        for (slist = string_list; slist; slist = slist->next)
1707
                str_array[i--] = slist->data;
1708

    
1709
        g_slist_free(string_list);
1710

    
1711
        return str_array;
1712
}
1713

    
1714
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1715
{
1716
        gchar *abbrev_group;
1717
        gchar *ap;
1718
        const gchar *p = group;
1719
        const gchar *last;
1720

    
1721
        last = group + strlen(group);
1722
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1723

    
1724
        while (*p) {
1725
                while (*p == '.')
1726
                        *ap++ = *p++;
1727
                if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1728
                        *ap++ = *p++;
1729
                        while (*p != '.') p++;
1730
                } else {
1731
                        strcpy(ap, p);
1732
                        return abbrev_group;
1733
                }
1734
        }
1735

    
1736
        *ap = '\0';
1737
        return abbrev_group;
1738
}
1739

    
1740
gchar *trim_string(const gchar *str, gint len)
1741
{
1742
        const gchar *p = str;
1743
        gint mb_len;
1744
        gchar *new_str;
1745
        gint new_len = 0;
1746

    
1747
        if (!str) return NULL;
1748
        if (strlen(str) <= len)
1749
                return g_strdup(str);
1750
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1751
                return g_strdup(str);
1752

    
1753
        while (*p != '\0') {
1754
                mb_len = g_utf8_skip[*(guchar *)p];
1755
                if (mb_len == 0)
1756
                        break;
1757
                else if (new_len + mb_len > len)
1758
                        break;
1759

    
1760
                new_len += mb_len;
1761
                p += mb_len;
1762
        }
1763

    
1764
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1765
        return g_strconcat(new_str, "...", NULL);
1766
}
1767

    
1768
gchar *trim_string_before(const gchar *str, gint len)
1769
{
1770
        const gchar *p = str;
1771
        gint mb_len;
1772
        gint new_len;
1773

    
1774
        if (!str) return NULL;
1775
        if ((new_len = strlen(str)) <= len)
1776
                return g_strdup(str);
1777
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1778
                return g_strdup(str);
1779

    
1780
        while (*p != '\0') {
1781
                mb_len = g_utf8_skip[*(guchar *)p];
1782
                if (mb_len == 0)
1783
                        break;
1784

    
1785
                new_len -= mb_len;
1786
                p += mb_len;
1787

    
1788
                if (new_len <= len)
1789
                        break;
1790
        }
1791

    
1792
        return g_strconcat("...", p, NULL);
1793
}
1794

    
1795
GList *uri_list_extract_filenames(const gchar *uri_list)
1796
{
1797
        GList *result = NULL;
1798
        gchar *file;
1799

    
1800
#if GLIB_CHECK_VERSION(2, 6, 0)
1801
        gchar **uris;
1802
        gint i;
1803

    
1804
        uris = g_uri_list_extract_uris(uri_list);
1805
        g_return_val_if_fail(uris != NULL, NULL);
1806

    
1807
        for (i = 0; uris[i] != NULL; i++) {
1808
                file = g_filename_from_uri(uris[i], NULL, NULL);
1809
                if (file)
1810
                        result = g_list_append(result, file);
1811
        }
1812

    
1813
        g_strfreev(uris);
1814

    
1815
        return result;
1816
#else
1817
        const gchar *p, *q;
1818

    
1819
        p = uri_list;
1820

    
1821
        while (p) {
1822
                if (*p != '#') {
1823
                        while (g_ascii_isspace(*p)) p++;
1824
                        if (!strncmp(p, "file:", 5)) {
1825
                                p += 5;
1826
                                while (*p == '/' && *(p + 1) == '/') p++;
1827
                                q = p;
1828
                                while (*q && *q != '\n' && *q != '\r') q++;
1829

    
1830
                                if (q > p) {
1831
                                        q--;
1832
                                        while (q > p && g_ascii_isspace(*q))
1833
                                                q--;
1834
                                        file = g_malloc(q - p + 2);
1835
                                        strncpy(file, p, q - p + 1);
1836
                                        file[q - p + 1] = '\0';
1837
                                        decode_uri(file, file);
1838
                                        result = g_list_append(result, file);
1839
                                }
1840
                        }
1841
                }
1842
                p = strchr(p, '\n');
1843
                if (p) p++;
1844
        }
1845

    
1846
        return result;
1847
#endif
1848
}
1849

    
1850
#define HEX_TO_INT(val, hex) \
1851
{ \
1852
        gchar c = hex; \
1853
 \
1854
        if ('0' <= c && c <= '9') { \
1855
                val = c - '0'; \
1856
        } else if ('a' <= c && c <= 'f') { \
1857
                val = c - 'a' + 10; \
1858
        } else if ('A' <= c && c <= 'F') { \
1859
                val = c - 'A' + 10; \
1860
        } else { \
1861
                val = 0; \
1862
        } \
1863
}
1864

    
1865
#define INT_TO_HEX(hex, val)                \
1866
{                                        \
1867
        if ((val) < 10)                        \
1868
                hex = '0' + (val);        \
1869
        else                                \
1870
                hex = 'a' + (val) - 10;        \
1871
}
1872

    
1873
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1874
 * characters.
1875
 */
1876
static gint axtoi(const gchar *hex_str)
1877
{
1878
        gint hi, lo;
1879

    
1880
        HEX_TO_INT(hi, hex_str[0]);
1881
        HEX_TO_INT(lo, hex_str[1]);
1882

    
1883
        return (hi << 4) + lo;
1884
}
1885

    
1886
static void get_hex_str(gchar *out, guchar ch)
1887
{
1888
        gchar hex;
1889

    
1890
        INT_TO_HEX(hex, ch >> 4);
1891
        *out++ = hex;
1892
        INT_TO_HEX(hex, ch & 0x0f);
1893
        *out++ = hex;
1894
}
1895

    
1896
gboolean is_uri_string(const gchar *str)
1897
{
1898
        return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1899
                g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1900
                g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1901
                g_ascii_strncasecmp(str, "www.", 4) == 0);
1902
}
1903

    
1904
gchar *get_uri_path(const gchar *uri)
1905
{
1906
        if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1907
                return (gchar *)(uri + 7);
1908
        else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1909
                return (gchar *)(uri + 8);
1910
        else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1911
                return (gchar *)(uri + 6);
1912
        else
1913
                return (gchar *)uri;
1914
}
1915

    
1916
gint get_uri_len(const gchar *str)
1917
{
1918
        const gchar *p;
1919

    
1920
        if (is_uri_string(str)) {
1921
                for (p = str; *p != '\0'; p++) {
1922
                        if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1923
                                break;
1924
                }
1925
                return p - str;
1926
        }
1927

    
1928
        return 0;
1929
}
1930

    
1931
/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1932
 * plusses, and escape characters are used)
1933
 * Note: decoded_uri and encoded_uri can point the same location
1934
 */
1935
void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1936
{
1937
        gchar *dec = decoded_uri;
1938
        const gchar *enc = encoded_uri;
1939

    
1940
        while (*enc) {
1941
                if (*enc == '%') {
1942
                        enc++;
1943
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1944
                            g_ascii_isxdigit((guchar)enc[1])) {
1945
                                *dec = axtoi(enc);
1946
                                dec++;
1947
                                enc += 2;
1948
                        }
1949
                } else {
1950
                        if (*enc == '+')
1951
                                *dec = ' ';
1952
                        else
1953
                                *dec = *enc;
1954
                        dec++;
1955
                        enc++;
1956
                }
1957
        }
1958

    
1959
        *dec = '\0';
1960
}
1961

    
1962
void decode_xdigit_encoded_str(gchar *decoded, const gchar *encoded)
1963
{
1964
        gchar *dec = decoded;
1965
        const gchar *enc = encoded;
1966

    
1967
        while (*enc) {
1968
                if (*enc == '%') {
1969
                        enc++;
1970
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1971
                            g_ascii_isxdigit((guchar)enc[1])) {
1972
                                *dec++ = axtoi(enc);
1973
                                enc += 2;
1974
                        }
1975
                } else
1976
                        *dec++ = *enc++;
1977
        }
1978

    
1979
        *dec = '\0';
1980
}
1981

    
1982
gchar *encode_uri(const gchar *filename)
1983
{
1984
        gchar *uri;
1985

    
1986
        uri = g_filename_to_uri(filename, NULL, NULL);
1987
        if (!uri)
1988
                uri = g_strconcat("file://", filename, NULL);
1989

    
1990
        return uri;
1991
}
1992

    
1993
gchar *uriencode_for_filename(const gchar *filename)
1994
{
1995
        const gchar *p = filename;
1996
        gchar *enc, *outp;
1997

    
1998
        outp = enc = g_malloc(strlen(filename) * 3 + 1);
1999

    
2000
        for (p = filename; *p != '\0'; p++) {
2001
                if (strchr("\t\r\n\"'\\/:;*?<>|", *p)) {
2002
                        *outp++ = '%';
2003
                        get_hex_str(outp, *p);
2004
                        outp += 2;
2005
                } else
2006
                        *outp++ = *p;
2007
        }
2008

    
2009
        *outp = '\0';
2010
        return enc;
2011
}
2012

    
2013
gchar *uriencode_for_mailto(const gchar *mailto)
2014
{
2015
        const gchar *p = mailto;
2016
        gchar *enc, *outp;
2017

    
2018
        outp = enc = g_malloc(strlen(mailto) * 3 + 1);
2019

    
2020
        for (p = mailto; *p != '\0'; p++) {
2021
                if (*p == '+') {
2022
                        *outp++ = '%';
2023
                        get_hex_str(outp, *p);
2024
                        outp += 2;
2025
                } else
2026
                        *outp++ = *p;
2027
        }
2028

    
2029
        *outp = '\0';
2030
        return enc;
2031
}
2032

    
2033
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
2034
                     gchar **subject, gchar **inreplyto, gchar **body)
2035
{
2036
        gchar *tmp_mailto;
2037
        gchar *p;
2038

    
2039
        Xstrdup_a(tmp_mailto, mailto, return -1);
2040

    
2041
        if (!strncmp(tmp_mailto, "mailto:", 7))
2042
                tmp_mailto += 7;
2043

    
2044
        p = strchr(tmp_mailto, '?');
2045
        if (p) {
2046
                *p = '\0';
2047
                p++;
2048
        }
2049

    
2050
        if (to && !*to) {
2051
                *to = g_malloc(strlen(tmp_mailto) + 1);
2052
                decode_uri(*to, tmp_mailto);
2053
        }
2054

    
2055
        while (p) {
2056
                gchar *field, *value;
2057

    
2058
                field = p;
2059

    
2060
                p = strchr(p, '=');
2061
                if (!p) break;
2062
                *p = '\0';
2063
                p++;
2064

    
2065
                value = p;
2066

    
2067
                p = strchr(p, '&');
2068
                if (p) {
2069
                        *p = '\0';
2070
                        p++;
2071
                }
2072

    
2073
                if (*value == '\0') continue;
2074

    
2075
                if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
2076
                        *cc = g_malloc(strlen(value) + 1);
2077
                        decode_uri(*cc, value);
2078
                } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
2079
                        *bcc = g_malloc(strlen(value) + 1);
2080
                        decode_uri(*bcc, value);
2081
                } else if (subject && !*subject &&
2082
                           !g_ascii_strcasecmp(field, "subject")) {
2083
                        *subject = g_malloc(strlen(value) + 1);
2084
                        decode_uri(*subject, value);
2085
                } else if (inreplyto && !*inreplyto &&
2086
                           !g_ascii_strcasecmp(field, "in-reply-to")) {
2087
                        *inreplyto = g_malloc(strlen(value) + 1);
2088
                        decode_uri(*inreplyto, value);
2089
                } else if (body && !*body &&
2090
                           !g_ascii_strcasecmp(field, "body")) {
2091
                        *body = g_malloc(strlen(value) + 1);
2092
                        decode_uri(*body, value);
2093
                }
2094
        }
2095

    
2096
        return 0;
2097
}
2098

    
2099
static gchar *startup_dir = NULL;
2100
static gchar *rc_dir = NULL;
2101

    
2102
void set_startup_dir(void)
2103
{
2104
#ifdef G_OS_WIN32
2105
        if (!startup_dir) {
2106
                startup_dir = g_win32_get_package_installation_directory
2107
                        (NULL, NULL);
2108
                if (startup_dir) {
2109
                        if (g_chdir(startup_dir) < 0) {
2110
                                FILE_OP_ERROR(startup_dir, "chdir");
2111
                                g_free(startup_dir);
2112
                                startup_dir = g_get_current_dir();
2113
                        }
2114
                } else
2115
                        startup_dir = g_get_current_dir();
2116
        }
2117
#else
2118
        if (!startup_dir)
2119
                startup_dir = g_get_current_dir();
2120
#endif
2121
}
2122

    
2123
void set_rc_dir(const gchar *dir)
2124
{
2125
        if (rc_dir)
2126
                g_free(rc_dir);
2127

    
2128
        if (dir) {
2129
                if (g_path_is_absolute(dir))
2130
                        rc_dir = g_strdup(dir);
2131
                else
2132
                        rc_dir = g_strconcat(get_startup_dir(),
2133
                                             G_DIR_SEPARATOR_S, dir, NULL);
2134
        } else
2135
                rc_dir = NULL;
2136
}
2137

    
2138
const gchar *get_startup_dir(void)
2139
{
2140
        if (!startup_dir)
2141
                set_startup_dir();
2142

    
2143
        return startup_dir;
2144
}
2145

    
2146
#ifdef G_OS_WIN32
2147
static gchar *get_win32_special_folder_path(gint nfolder)
2148
{
2149
        gchar *folder = NULL;
2150
        HRESULT hr;
2151

    
2152
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2153
                wchar_t path[MAX_PATH + 1];
2154
                hr = SHGetFolderPathW(NULL, nfolder, NULL, 0, path);
2155
                if (hr == S_OK)
2156
                        folder = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
2157
        } else {
2158
                gchar path[MAX_PATH + 1];
2159
                hr = SHGetFolderPathA(NULL, nfolder, NULL, 0, path);
2160
                if (hr == S_OK)
2161
                        folder = g_locale_to_utf8(path, -1, NULL, NULL, NULL);
2162
        }
2163

    
2164
        return folder;
2165
}
2166
#endif
2167

    
2168
const gchar *get_home_dir(void)
2169
{
2170
#ifdef G_OS_WIN32
2171
        static const gchar *home_dir = NULL;
2172

    
2173
        if (!home_dir) {
2174
                home_dir = g_get_home_dir();
2175
                if (!home_dir)
2176
                        home_dir = "C:\\Sylpheed";
2177
        }
2178

    
2179
        return home_dir;
2180
#else
2181
        return g_get_home_dir();
2182
#endif
2183
}
2184

    
2185
const gchar *get_document_dir(void)
2186
{
2187
#ifdef G_OS_WIN32
2188
        static const gchar *document_dir = NULL;
2189
        HRESULT hr;
2190

    
2191
        if (!document_dir) {
2192
                document_dir = get_win32_special_folder_path(CSIDL_PERSONAL);
2193
                if (!document_dir)
2194
                        document_dir = get_home_dir();
2195
        }
2196

    
2197
        return document_dir;
2198
#elif defined(__APPLE__)
2199
        static const gchar *document_dir = NULL;
2200

    
2201
        if (!document_dir) {
2202
                document_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2203
                                           "Documents", NULL);
2204
        }
2205

    
2206
        return document_dir;
2207
#else
2208
        return get_home_dir();
2209
#endif
2210
}
2211

    
2212
const gchar *get_rc_dir(void)
2213
{
2214
        if (!rc_dir) {
2215
#ifdef G_OS_WIN32
2216
                gchar *appdata;
2217

    
2218
                appdata = get_win32_special_folder_path(CSIDL_APPDATA);
2219
                if (appdata)
2220
                        rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S,
2221
                                             RC_DIR, NULL);
2222
                else
2223
                        rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2224
                                             RC_DIR, NULL);
2225
                g_free(appdata);
2226
#elif defined(__APPLE__)
2227
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2228
                                     "Library", G_DIR_SEPARATOR_S,
2229
                                     "Application Support", G_DIR_SEPARATOR_S,
2230
                                     RC_DIR, NULL);
2231
#else
2232
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2233
                                     RC_DIR, NULL);
2234
#endif
2235
        }
2236

    
2237
        return rc_dir;
2238
}
2239

    
2240
const gchar *get_old_rc_dir(void)
2241
{
2242
        static gchar *old_rc_dir = NULL;
2243

    
2244
        if (!old_rc_dir)
2245
                old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2246
                                         OLD_RC_DIR, NULL);
2247

    
2248
        return old_rc_dir;
2249
}
2250

    
2251
const gchar *get_mail_base_dir(void)
2252
{
2253
#if defined(G_OS_WIN32) || defined(__APPLE__)
2254
        static gchar *mail_base_dir = NULL;
2255

    
2256
        if (!mail_base_dir)
2257
                mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2258
                                            "Mailboxes", NULL);
2259

    
2260
        return mail_base_dir;
2261
#else
2262
        return get_home_dir();
2263
#endif
2264
}
2265

    
2266
const gchar *get_news_cache_dir(void)
2267
{
2268
        static gchar *news_cache_dir = NULL;
2269

    
2270
        if (!news_cache_dir)
2271
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2272
                                             NEWS_CACHE_DIR, NULL);
2273

    
2274
        return news_cache_dir;
2275
}
2276

    
2277
const gchar *get_imap_cache_dir(void)
2278
{
2279
        static gchar *imap_cache_dir = NULL;
2280

    
2281
        if (!imap_cache_dir)
2282
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2283
                                             IMAP_CACHE_DIR, NULL);
2284

    
2285
        return imap_cache_dir;
2286
}
2287

    
2288
const gchar *get_mime_tmp_dir(void)
2289
{
2290
        static gchar *mime_tmp_dir = NULL;
2291

    
2292
        if (!mime_tmp_dir)
2293
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2294
                                           MIME_TMP_DIR, NULL);
2295

    
2296
        return mime_tmp_dir;
2297
}
2298

    
2299
const gchar *get_template_dir(void)
2300
{
2301
        static gchar *template_dir = NULL;
2302

    
2303
        if (!template_dir)
2304
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2305
                                           TEMPLATE_DIR, NULL);
2306

    
2307
        return template_dir;
2308
}
2309

    
2310
const gchar *get_tmp_dir(void)
2311
{
2312
        static gchar *tmp_dir = NULL;
2313

    
2314
        if (!tmp_dir)
2315
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2316
                                      TMP_DIR, NULL);
2317

    
2318
        return tmp_dir;
2319
}
2320

    
2321
gchar *get_tmp_file(void)
2322
{
2323
        gchar *tmp_file;
2324
        static guint32 id = 0;
2325

    
2326
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2327
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
2328

    
2329
        return tmp_file;
2330
}
2331

    
2332
off_t get_file_size(const gchar *file)
2333
{
2334
        struct stat s;
2335

    
2336
        if (g_stat(file, &s) < 0) {
2337
                FILE_OP_ERROR(file, "stat");
2338
                return -1;
2339
        }
2340

    
2341
        return s.st_size;
2342
}
2343

    
2344
off_t get_file_size_as_crlf(const gchar *file)
2345
{
2346
        FILE *fp;
2347
        off_t size = 0;
2348
        gchar buf[BUFFSIZE];
2349

    
2350
        if ((fp = g_fopen(file, "rb")) == NULL) {
2351
                FILE_OP_ERROR(file, "fopen");
2352
                return -1;
2353
        }
2354

    
2355
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2356
                strretchomp(buf);
2357
                size += strlen(buf) + 2;
2358
        }
2359

    
2360
        if (ferror(fp)) {
2361
                FILE_OP_ERROR(file, "fgets");
2362
                size = -1;
2363
        }
2364

    
2365
        fclose(fp);
2366

    
2367
        return size;
2368
}
2369

    
2370
off_t get_left_file_size(FILE *fp)
2371
{
2372
        glong pos;
2373
        glong end;
2374
        off_t size;
2375

    
2376
        if ((pos = ftell(fp)) < 0) {
2377
                perror("ftell");
2378
                return -1;
2379
        }
2380
        if (fseek(fp, 0L, SEEK_END) < 0) {
2381
                perror("fseek");
2382
                return -1;
2383
        }
2384
        if ((end = ftell(fp)) < 0) {
2385
                perror("fseek");
2386
                return -1;
2387
        }
2388
        size = end - pos;
2389
        if (fseek(fp, pos, SEEK_SET) < 0) {
2390
                perror("fseek");
2391
                return -1;
2392
        }
2393

    
2394
        return size;
2395
}
2396

    
2397
gint get_last_empty_line_size(FILE *fp, off_t size)
2398
{
2399
        glong pos;
2400
        gint lsize = 0;
2401
        gchar buf[4];
2402
        size_t nread;
2403

    
2404
        if (size < 4)
2405
                return -1;
2406

    
2407
        if ((pos = ftell(fp)) < 0) {
2408
                perror("ftell");
2409
                return -1;
2410
        }
2411
        if (fseek(fp, size - 4, SEEK_CUR) < 0) {
2412
                perror("fseek");
2413
                return -1;
2414
        }
2415

    
2416
        /* read last 4 bytes */
2417
        nread = fread(buf, sizeof(buf), 1, fp);
2418
        if (nread != 1) {
2419
                perror("fread");
2420
                return -1;
2421
        }
2422
        /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */
2423
        if (buf[3] == '\n') {
2424
                if (buf[2] == '\n')
2425
                        lsize = 1;
2426
                else if (buf[2] == '\r') {
2427
                        if (buf[1] == '\n')
2428
                                lsize = 2;
2429
                }
2430
        }
2431

    
2432
        if (fseek(fp, pos, SEEK_SET) < 0) {
2433
                perror("fseek");
2434
                return -1;
2435
        }
2436

    
2437
        return lsize;
2438
}
2439

    
2440
gboolean file_exist(const gchar *file, gboolean allow_fifo)
2441
{
2442
        if (file == NULL)
2443
                return FALSE;
2444

    
2445
        if (allow_fifo) {
2446
                struct stat s;
2447

    
2448
                if (g_stat(file, &s) < 0) {
2449
                        if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2450
                        return FALSE;
2451
                }
2452
                if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode))
2453
                        return TRUE;
2454
        } else {
2455
                return g_file_test(file, G_FILE_TEST_IS_REGULAR);
2456
        }
2457

    
2458
        return FALSE;
2459
}
2460

    
2461
gboolean is_dir_exist(const gchar *dir)
2462
{
2463
        if (dir == NULL)
2464
                return FALSE;
2465

    
2466
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
2467
}
2468

    
2469
gboolean is_file_entry_exist(const gchar *file)
2470
{
2471
        if (file == NULL)
2472
                return FALSE;
2473

    
2474
        return g_file_test(file, G_FILE_TEST_EXISTS);
2475
}
2476

    
2477
gboolean dirent_is_regular_file(struct dirent *d)
2478
{
2479
#ifdef HAVE_DIRENT_D_TYPE
2480
        if (d->d_type == DT_REG)
2481
                return TRUE;
2482
        else if (d->d_type != DT_UNKNOWN)
2483
                return FALSE;
2484
#endif
2485

    
2486
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2487
}
2488

    
2489
gboolean dirent_is_directory(struct dirent *d)
2490
{
2491
#ifdef HAVE_DIRENT_D_TYPE
2492
        if (d->d_type == DT_DIR)
2493
                return TRUE;
2494
        else if (d->d_type != DT_UNKNOWN)
2495
                return FALSE;
2496
#endif
2497

    
2498
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2499
}
2500

    
2501
gint change_dir(const gchar *dir)
2502
{
2503
        gchar *prevdir = NULL;
2504

    
2505
        if (debug_mode)
2506
                prevdir = g_get_current_dir();
2507

    
2508
        if (g_chdir(dir) < 0) {
2509
                FILE_OP_ERROR(dir, "chdir");
2510
                if (debug_mode) g_free(prevdir);
2511
                return -1;
2512
        } else if (debug_mode) {
2513
                gchar *cwd;
2514

    
2515
                cwd = g_get_current_dir();
2516
                if (strcmp(prevdir, cwd) != 0)
2517
                        g_print("current dir: %s\n", cwd);
2518
                g_free(cwd);
2519
                g_free(prevdir);
2520
        }
2521

    
2522
        return 0;
2523
}
2524

    
2525
/* convert line endings into CRLF. If the last line doesn't end with
2526
 * linebreak, add it.
2527
 */
2528
gchar *canonicalize_str(const gchar *str)
2529
{
2530
        const gchar *p;
2531
        guint new_len = 0;
2532
        gchar *out, *outp;
2533

    
2534
        for (p = str; *p != '\0'; ++p) {
2535
                if (*p != '\r') {
2536
                        ++new_len;
2537
                        if (*p == '\n')
2538
                                ++new_len;
2539
                }
2540
        }
2541
        if (p == str || *(p - 1) != '\n')
2542
                new_len += 2;
2543

    
2544
        out = outp = g_malloc(new_len + 1);
2545
        for (p = str; *p != '\0'; ++p) {
2546
                if (*p != '\r') {
2547
                        if (*p == '\n')
2548
                                *outp++ = '\r';
2549
                        *outp++ = *p;
2550
                }
2551
        }
2552
        if (p == str || *(p - 1) != '\n') {
2553
                *outp++ = '\r';
2554
                *outp++ = '\n';
2555
        }
2556
        *outp = '\0';
2557

    
2558
        return out;
2559
}
2560

    
2561
gint canonicalize_file(const gchar *src, const gchar *dest)
2562
{
2563
        FILE *src_fp, *dest_fp;
2564
        gchar buf[BUFFSIZE];
2565
        gint len;
2566
        gboolean err = FALSE;
2567
        gboolean last_linebreak = FALSE;
2568

    
2569
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
2570
                FILE_OP_ERROR(src, "fopen");
2571
                return -1;
2572
        }
2573

    
2574
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2575
                FILE_OP_ERROR(dest, "fopen");
2576
                fclose(src_fp);
2577
                return -1;
2578
        }
2579

    
2580
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2581
                FILE_OP_ERROR(dest, "chmod");
2582
                g_warning("can't change file mode\n");
2583
        }
2584

    
2585
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2586
                gint r = 0;
2587

    
2588
                len = strlen(buf);
2589
                if (len == 0) break;
2590
                last_linebreak = FALSE;
2591

    
2592
                if (buf[len - 1] != '\n') {
2593
                        last_linebreak = TRUE;
2594
                        r = fputs(buf, dest_fp);
2595
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2596
                        r = fputs(buf, dest_fp);
2597
                } else {
2598
                        if (len > 1) {
2599
                                r = fwrite(buf, len - 1, 1, dest_fp);
2600
                                if (r != 1)
2601
                                        r = EOF;
2602
                        }
2603
                        if (r != EOF)
2604
                                r = fputs("\r\n", dest_fp);
2605
                }
2606

    
2607
                if (r == EOF) {
2608
                        g_warning("writing to %s failed.\n", dest);
2609
                        fclose(dest_fp);
2610
                        fclose(src_fp);
2611
                        g_unlink(dest);
2612
                        return -1;
2613
                }
2614
        }
2615

    
2616
        if (last_linebreak == TRUE) {
2617
                if (fputs("\r\n", dest_fp) == EOF)
2618
                        err = TRUE;
2619
        }
2620

    
2621
        if (ferror(src_fp)) {
2622
                FILE_OP_ERROR(src, "fgets");
2623
                err = TRUE;
2624
        }
2625
        fclose(src_fp);
2626
        if (fclose(dest_fp) == EOF) {
2627
                FILE_OP_ERROR(dest, "fclose");
2628
                err = TRUE;
2629
        }
2630

    
2631
        if (err) {
2632
                g_unlink(dest);
2633
                return -1;
2634
        }
2635

    
2636
        return 0;
2637
}
2638

    
2639
FILE *canonicalize_file_stream(FILE *src_fp, gint *length)
2640
{
2641
        FILE *dest_fp;
2642
        gchar buf[BUFFSIZE];
2643
        gint len;
2644
        gint length_ = 0;
2645
        gboolean err = FALSE;
2646
        gboolean last_linebreak = FALSE;
2647

    
2648
        if ((dest_fp = my_tmpfile()) == NULL)
2649
                return NULL;
2650

    
2651
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2652
                gint r = 0;
2653

    
2654
                len = strlen(buf);
2655
                if (len == 0) break;
2656
                last_linebreak = FALSE;
2657

    
2658
                if (buf[len - 1] != '\n') {
2659
                        last_linebreak = TRUE;
2660
                        r = fputs(buf, dest_fp);
2661
                        length_ += len;
2662
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2663
                        r = fputs(buf, dest_fp);
2664
                        length_ += len;
2665
                } else {
2666
                        if (len > 1) {
2667
                                r = fwrite(buf, len - 1, 1, dest_fp);
2668
                                if (r != 1)
2669
                                        r = EOF;
2670
                                else
2671
                                        length_ += len - 1;
2672
                        }
2673
                        if (r != EOF) {
2674
                                r = fputs("\r\n", dest_fp);
2675
                                length_ += 2;
2676
                        }
2677
                }
2678

    
2679
                if (r == EOF) {
2680
                        g_warning("writing to temporary file failed.\n");
2681
                        fclose(dest_fp);
2682
                        return NULL;
2683
                }
2684
        }
2685

    
2686
        if (last_linebreak == TRUE) {
2687
                if (fputs("\r\n", dest_fp) == EOF)
2688
                        err = TRUE;
2689
                else
2690
                        length_ += 2;
2691
        }
2692

    
2693
        if (ferror(src_fp)) {
2694
                FILE_OP_ERROR("canonicalize_file_stream", "fgets");
2695
                err = TRUE;
2696
        }
2697
        if (fflush(dest_fp) == EOF) {
2698
                FILE_OP_ERROR("canonicalize_file_stream", "fflush");
2699
                err = TRUE;
2700
        }
2701

    
2702
        if (err) {
2703
                fclose(dest_fp);
2704
                return NULL;
2705
        }
2706

    
2707
        if (length)
2708
                *length = length_;
2709

    
2710
        rewind(dest_fp);
2711
        return dest_fp;
2712
}
2713

    
2714
gint uncanonicalize_file(const gchar *src, const gchar *dest)
2715
{
2716
        FILE *src_fp, *dest_fp;
2717
        gchar buf[BUFFSIZE];
2718
        gboolean err = FALSE;
2719

    
2720
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
2721
                FILE_OP_ERROR(src, "fopen");
2722
                return -1;
2723
        }
2724

    
2725
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2726
                FILE_OP_ERROR(dest, "fopen");
2727
                fclose(src_fp);
2728
                return -1;
2729
        }
2730

    
2731
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2732
                FILE_OP_ERROR(dest, "chmod");
2733
                g_warning("can't change file mode\n");
2734
        }
2735

    
2736
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2737
                strcrchomp(buf);
2738
                if (fputs(buf, dest_fp) == EOF) {
2739
                        g_warning("writing to %s failed.\n", dest);
2740
                        fclose(dest_fp);
2741
                        fclose(src_fp);
2742
                        g_unlink(dest);
2743
                        return -1;
2744
                }
2745
        }
2746

    
2747
        if (ferror(src_fp)) {
2748
                FILE_OP_ERROR(src, "fgets");
2749
                err = TRUE;
2750
        }
2751
        fclose(src_fp);
2752
        if (fclose(dest_fp) == EOF) {
2753
                FILE_OP_ERROR(dest, "fclose");
2754
                err = TRUE;
2755
        }
2756

    
2757
        if (err) {
2758
                g_unlink(dest);
2759
                return -1;
2760
        }
2761

    
2762
        return 0;
2763
}
2764

    
2765
gchar *normalize_newlines(const gchar *str)
2766
{
2767
        const gchar *p = str;
2768
        gchar *out, *outp;
2769

    
2770
        out = outp = g_malloc(strlen(str) + 1);
2771
        for (p = str; *p != '\0'; ++p) {
2772
                if (*p == '\r') {
2773
                        if (*(p + 1) != '\n')
2774
                                *outp++ = '\n';
2775
                } else
2776
                        *outp++ = *p;
2777
        }
2778

    
2779
        *outp = '\0';
2780

    
2781
        return out;
2782
}
2783

    
2784
gchar *strchomp_all(const gchar *str)
2785
{
2786
        const gchar *p = str;
2787
        const gchar *newline, *last;
2788
        gchar *out, *outp;
2789

    
2790
        out = outp = g_malloc(strlen(str) + 1);
2791
        while (*p != '\0') {
2792
                newline = strchr(p, '\n');
2793
                if (newline) {
2794
                        for (last = newline;
2795
                             p < last && g_ascii_isspace(*(last - 1)); --last)
2796
                                ;
2797
                        strncpy(outp, p, last - p);
2798
                        outp += last - p;
2799

    
2800
                        if (p < newline && *(newline - 1) == '\r') {
2801
                                strncpy(outp, newline - 1, 2);
2802
                                outp += 2;
2803
                        } else {
2804
                                *outp++ = *newline;
2805
                        }
2806

    
2807
                        p = newline + 1;
2808
                } else {
2809
                        for (last = p + strlen(p);
2810
                             p < last && g_ascii_isspace(*(last - 1)); --last)
2811
                                ;
2812
                        strncpy(outp, p, last - p);
2813
                        outp += last - p;
2814
                        break;
2815
                }
2816
        }
2817

    
2818
        *outp = '\0';
2819

    
2820
        return out;
2821
}
2822

    
2823
FILE *get_outgoing_rfc2822_file(FILE *fp)
2824
{
2825
        gchar buf[BUFFSIZE];
2826
        FILE *outfp;
2827

    
2828
        outfp = my_tmpfile();
2829
        if (!outfp) {
2830
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile");
2831
                return NULL;
2832
        }
2833

    
2834
        /* output header part */
2835
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2836
                strretchomp(buf);
2837
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2838
                        gint next;
2839

    
2840
                        for (;;) {
2841
                                next = fgetc(fp);
2842
                                if (next == EOF)
2843
                                        break;
2844
                                else if (next != ' ' && next != '\t') {
2845
                                        ungetc(next, fp);
2846
                                        break;
2847
                                }
2848
                                if (fgets(buf, sizeof(buf), fp) == NULL)
2849
                                        break;
2850
                        }
2851
                } else {
2852
                        if (fputs(buf, outfp) == EOF)
2853
                                goto file_error;
2854
                        if (fputs("\r\n", outfp) == EOF)
2855
                                goto file_error;
2856
                        if (buf[0] == '\0')
2857
                                break;
2858
                }
2859
        }
2860

    
2861
        /* output body part */
2862
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2863
                strretchomp(buf);
2864
                if (buf[0] == '.') {
2865
                        if (fputc('.', outfp) == EOF)
2866
                                goto file_error;
2867
                }
2868
                if (fputs(buf, outfp) == EOF)
2869
                        goto file_error;
2870
                if (fputs("\r\n", outfp) == EOF)
2871
                        goto file_error;
2872
        }
2873

    
2874
        if (fflush(outfp) == EOF) {
2875
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush");
2876
                goto file_error;
2877
        }
2878

    
2879
        rewind(outfp);
2880
        return outfp;
2881

    
2882
file_error:
2883
        g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n");
2884
        fclose(outfp);
2885
        return NULL;
2886
}
2887

    
2888
gchar *get_outgoing_rfc2822_str(FILE *fp)
2889
{
2890
        gchar buf[BUFFSIZE];
2891
        GString *str;
2892
        gchar *ret;
2893

    
2894
        str = g_string_new(NULL);
2895

    
2896
        /* output header part */
2897
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2898
                strretchomp(buf);
2899
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2900
                        gint next;
2901

    
2902
                        for (;;) {
2903
                                next = fgetc(fp);
2904
                                if (next == EOF)
2905
                                        break;
2906
                                else if (next != ' ' && next != '\t') {
2907
                                        ungetc(next, fp);
2908
                                        break;
2909
                                }
2910
                                if (fgets(buf, sizeof(buf), fp) == NULL)
2911
                                        break;
2912
                        }
2913
#if 0
2914
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
2915
                        get_rfc822_date(buf, sizeof(buf));
2916
                        g_string_append_printf(str, "Date: %s\r\n", buf);
2917
#endif
2918
                } else {
2919
                        g_string_append(str, buf);
2920
                        g_string_append(str, "\r\n");
2921
                        if (buf[0] == '\0')
2922
                                break;
2923
                }
2924
        }
2925

    
2926
        /* output body part */
2927
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2928
                strretchomp(buf);
2929
                if (buf[0] == '.')
2930
                        g_string_append_c(str, '.');
2931
                g_string_append(str, buf);
2932
                g_string_append(str, "\r\n");
2933
        }
2934

    
2935
        ret = str->str;
2936
        g_string_free(str, FALSE);
2937

    
2938
        return ret;
2939
}
2940

    
2941
/*
2942
 * Create a new boundary in a way that it is very unlikely that this
2943
 * will occur in the following text.  It would be easy to ensure
2944
 * uniqueness if everything is either quoted-printable or base64
2945
 * encoded (note that conversion is allowed), but because MIME bodies
2946
 * may be nested, it may happen that the same boundary has already
2947
 * been used. We avoid scanning the message for conflicts and hope the
2948
 * best.
2949
 *
2950
 *   boundary := 0*69<bchars> bcharsnospace
2951
 *   bchars := bcharsnospace / " "
2952
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2953
 *                    "+" / "_" / "," / "-" / "." /
2954
 *                    "/" / ":" / "=" / "?"
2955
 *
2956
 * some special characters removed because of buggy MTAs
2957
 */
2958

    
2959
gchar *generate_mime_boundary(const gchar *prefix)
2960
{
2961
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2962
                             "abcdefghijklmnopqrstuvwxyz"
2963
                             "1234567890+_./=";
2964
        gchar buf_uniq[17];
2965
        gchar buf_date[64];
2966
        gint i;
2967

    
2968
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2969
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2970
        buf_uniq[i] = '\0';
2971

    
2972
        get_rfc822_date(buf_date, sizeof(buf_date));
2973
        subst_chars(buf_date, " ,:", '_');
2974

    
2975
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
2976
                               buf_date, buf_uniq);
2977
}
2978

    
2979
gint change_file_mode_rw(FILE *fp, const gchar *file)
2980
{
2981
#ifdef G_OS_WIN32
2982
        DWORD attr;
2983
        BOOL retval;
2984

    
2985
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2986
                wchar_t *wpath;
2987

    
2988
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
2989
                if (wpath == NULL)
2990
                        return -1;
2991

    
2992
                attr = GetFileAttributesW(wpath);
2993
                retval = SetFileAttributesW
2994
                        (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
2995

    
2996
                g_free(wpath);
2997
        } else {
2998
                gchar *cp_path;
2999

    
3000
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3001
                if (cp_path == NULL)
3002
                        return -1;
3003

    
3004
                attr = GetFileAttributesA(cp_path);
3005
                retval = SetFileAttributesA
3006
                        (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3007

    
3008
                g_free(cp_path);
3009
        }
3010

    
3011
        if (retval)
3012
                return 0;
3013
        else
3014
                return -1;
3015
#else
3016
#if HAVE_FCHMOD
3017
        if (fp)
3018
                return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3019
        else
3020
#endif
3021
                return g_chmod(file, S_IRUSR|S_IWUSR);
3022
#endif
3023
}
3024

    
3025
#ifdef G_OS_WIN32
3026
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
3027
{
3028
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3029
                wchar_t *wpath;
3030
                wchar_t *wprefix;
3031
                wchar_t *wname;
3032
                gint save_errno;
3033
                gchar *name;
3034

    
3035
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
3036
                if (wpath == NULL) {
3037
                        errno = EINVAL;
3038
                        return NULL;
3039
                }
3040
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
3041
                if (wprefix == NULL) {
3042
                        errno = EINVAL;
3043
                        g_free(wpath);
3044
                        return NULL;
3045
                }
3046

    
3047
                wname = _wtempnam(wpath, wprefix);
3048
                save_errno = errno;
3049

    
3050
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
3051
                if (name == NULL) {
3052
                        save_errno = EINVAL;
3053
                }
3054

    
3055
                g_free(wname);
3056
                g_free(wprefix);
3057
                g_free(wpath);
3058

    
3059
                errno = save_errno;
3060
                return name;
3061
        } else {
3062
                gchar *cp_path;
3063
                gchar *cp_prefix;
3064
                gchar *cp_name;
3065
                gint save_errno;
3066
                gchar *name;
3067

    
3068
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
3069
                if (cp_path == NULL) {
3070
                        errno = EINVAL;
3071
                        return NULL;
3072
                }
3073

    
3074
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
3075
                if (cp_prefix == NULL) {
3076
                        errno = EINVAL;
3077
                        g_free(cp_path);
3078
                        return NULL;
3079
                }
3080

    
3081
                cp_name = _tempnam(cp_path, cp_prefix);
3082
                save_errno = errno;
3083

    
3084
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
3085
                if (name == NULL) {
3086
                        save_errno = EINVAL;
3087
                }
3088

    
3089
                g_free(cp_name);
3090
                g_free(cp_prefix);
3091
                g_free(cp_path);
3092

    
3093
                errno = save_errno;
3094
                return name;
3095
        }
3096
}
3097
#endif
3098

    
3099
FILE *my_tmpfile(void)
3100
{
3101
#ifdef G_OS_WIN32
3102
        const gchar *tmpdir;
3103
        gchar *fname;
3104
        gint fd;
3105
        FILE *fp;
3106

    
3107
        tmpdir = get_tmp_dir();
3108
        fname = _s_tempnam(tmpdir, "sylph");
3109
        if (!fname)
3110
                return NULL;
3111

    
3112
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
3113
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
3114
        if (fd < 0) {
3115
                g_free(fname);
3116
                return NULL;
3117
        }
3118

    
3119
        fp = fdopen(fd, "w+b");
3120
        if (!fp) {
3121
                perror("fdopen");
3122
                close(fd);
3123
        }
3124

    
3125
        g_free(fname);
3126

    
3127
        return fp;
3128
#else
3129
        const gchar suffix[] = ".XXXXXX";
3130
        const gchar *tmpdir;
3131
        guint tmplen;
3132
        const gchar *progname;
3133
        guint proglen;
3134
        gchar *fname;
3135
        gint fd;
3136
        FILE *fp;
3137

    
3138
        tmpdir = get_tmp_dir();
3139
        tmplen = strlen(tmpdir);
3140
        progname = g_get_prgname();
3141
        if (!progname)
3142
                progname = "sylph";
3143
        proglen = strlen(progname);
3144
        fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix));
3145

    
3146
        memcpy(fname, tmpdir, tmplen);
3147
        fname[tmplen] = G_DIR_SEPARATOR;
3148
        memcpy(fname + tmplen + 1, progname, proglen);
3149
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3150

    
3151
        fd = g_mkstemp(fname);
3152
        if (fd < 0) {
3153
                g_free(fname);
3154
                return tmpfile();
3155
        }
3156

    
3157
        g_unlink(fname);
3158

    
3159
        fp = fdopen(fd, "w+b");
3160
        if (!fp) {
3161
                perror("fdopen");
3162
                close(fd);
3163
        }
3164

    
3165
        g_free(fname);
3166

    
3167
        return fp;
3168
#endif
3169
}
3170

    
3171
FILE *str_open_as_stream(const gchar *str)
3172
{
3173
        FILE *fp;
3174
        size_t len;
3175

    
3176
        g_return_val_if_fail(str != NULL, NULL);
3177

    
3178
        fp = my_tmpfile();
3179
        if (!fp) {
3180
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3181
                return NULL;
3182
        }
3183

    
3184
        len = strlen(str);
3185
        if (len == 0) return fp;
3186

    
3187
        if (fwrite(str, len, 1, fp) != 1) {
3188
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3189
                fclose(fp);
3190
                return NULL;
3191
        }
3192
        if (fflush(fp) == EOF) {
3193
                FILE_OP_ERROR("str_open_as_stream", "fflush");
3194
                fclose(fp);
3195
                return NULL;
3196
        }
3197

    
3198
        rewind(fp);
3199
        return fp;
3200
}
3201

    
3202
gint str_write_to_file(const gchar *str, const gchar *file)
3203
{
3204
        FILE *fp;
3205
        size_t len;
3206

    
3207
        g_return_val_if_fail(str != NULL, -1);
3208
        g_return_val_if_fail(file != NULL, -1);
3209

    
3210
        if ((fp = g_fopen(file, "wb")) == NULL) {
3211
                FILE_OP_ERROR(file, "fopen");
3212
                return -1;
3213
        }
3214

    
3215
        len = strlen(str);
3216
        if (len == 0) {
3217
                fclose(fp);
3218
                return 0;
3219
        }
3220

    
3221
        if (fwrite(str, len, 1, fp) != 1) {
3222
                FILE_OP_ERROR(file, "fwrite");
3223
                fclose(fp);
3224
                g_unlink(file);
3225
                return -1;
3226
        }
3227

    
3228
        if (fclose(fp) == EOF) {
3229
                FILE_OP_ERROR(file, "fclose");
3230
                g_unlink(file);
3231
                return -1;
3232
        }
3233

    
3234
        return 0;
3235
}
3236

    
3237
gchar *file_read_to_str(const gchar *file)
3238
{
3239
        FILE *fp;
3240
        gchar *str;
3241

    
3242
        g_return_val_if_fail(file != NULL, NULL);
3243

    
3244
        if ((fp = g_fopen(file, "rb")) == NULL) {
3245
                FILE_OP_ERROR(file, "fopen");
3246
                return NULL;
3247
        }
3248

    
3249
        str = file_read_stream_to_str(fp);
3250

    
3251
        fclose(fp);
3252

    
3253
        return str;
3254
}
3255

    
3256
gchar *file_read_stream_to_str(FILE *fp)
3257
{
3258
        GByteArray *array;
3259
        guchar buf[BUFSIZ];
3260
        gint n_read;
3261
        gchar *str;
3262

    
3263
        g_return_val_if_fail(fp != NULL, NULL);
3264

    
3265
        array = g_byte_array_new();
3266

    
3267
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3268
                if (n_read < sizeof(buf) && ferror(fp))
3269
                        break;
3270
                g_byte_array_append(array, buf, n_read);
3271
        }
3272

    
3273
        if (ferror(fp)) {
3274
                FILE_OP_ERROR("file stream", "fread");
3275
                g_byte_array_free(array, TRUE);
3276
                return NULL;
3277
        }
3278

    
3279
        buf[0] = '\0';
3280
        g_byte_array_append(array, buf, 1);
3281
        str = (gchar *)array->data;
3282
        g_byte_array_free(array, FALSE);
3283

    
3284
        return str;
3285
}
3286

    
3287
time_t remote_tzoffset_sec(const gchar *zone)
3288
{
3289
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3290
        gchar zone3[4];
3291
        gchar *p;
3292
        gchar c;
3293
        gint iustz;
3294
        gint offset;
3295
        time_t remoteoffset;
3296

    
3297
        strncpy(zone3, zone, 3);
3298
        zone3[3] = '\0';
3299
        remoteoffset = 0;
3300

    
3301
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3302
            (c == '+' || c == '-')) {
3303
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3304
                if (c == '-')
3305
                        remoteoffset = -remoteoffset;
3306
        } else if (!strncmp(zone, "UT" , 2) ||
3307
                   !strncmp(zone, "GMT", 2)) {
3308
                remoteoffset = 0;
3309
        } else if (strlen(zone3) == 3) {
3310
                for (p = ustzstr; *p != '\0'; p += 3) {
3311
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
3312
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3313
                                remoteoffset = iustz * 3600;
3314
                                break;
3315
                        }
3316
                }
3317
                if (*p == '\0')
3318
                        return -1;
3319
        } else if (strlen(zone3) == 1) {
3320
                switch (zone[0]) {
3321
                case 'Z': remoteoffset =   0; break;
3322
                case 'A': remoteoffset =  -1; break;
3323
                case 'B': remoteoffset =  -2; break;
3324
                case 'C': remoteoffset =  -3; break;
3325
                case 'D': remoteoffset =  -4; break;
3326
                case 'E': remoteoffset =  -5; break;
3327
                case 'F': remoteoffset =  -6; break;
3328
                case 'G': remoteoffset =  -7; break;
3329
                case 'H': remoteoffset =  -8; break;
3330
                case 'I': remoteoffset =  -9; break;
3331
                case 'K': remoteoffset = -10; break; /* J is not used */
3332
                case 'L': remoteoffset = -11; break;
3333
                case 'M': remoteoffset = -12; break;
3334
                case 'N': remoteoffset =   1; break;
3335
                case 'O': remoteoffset =   2; break;
3336
                case 'P': remoteoffset =   3; break;
3337
                case 'Q': remoteoffset =   4; break;
3338
                case 'R': remoteoffset =   5; break;
3339
                case 'S': remoteoffset =   6; break;
3340
                case 'T': remoteoffset =   7; break;
3341
                case 'U': remoteoffset =   8; break;
3342
                case 'V': remoteoffset =   9; break;
3343
                case 'W': remoteoffset =  10; break;
3344
                case 'X': remoteoffset =  11; break;
3345
                case 'Y': remoteoffset =  12; break;
3346
                default:  remoteoffset =   0; break;
3347
                }
3348
                remoteoffset = remoteoffset * 3600;
3349
        } else
3350
                return -1;
3351

    
3352
        return remoteoffset;
3353
}
3354

    
3355
time_t tzoffset_sec(time_t *now)
3356
{
3357
        struct tm gmt, *tmp, *lt;
3358
        gint off;
3359

    
3360
        tmp = gmtime(now);
3361
        g_return_val_if_fail(tmp != NULL, -1);
3362
        gmt = *tmp;
3363
        lt = localtime(now);
3364
        g_return_val_if_fail(lt != NULL, -1);
3365

    
3366
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3367

    
3368
        if (lt->tm_year < gmt.tm_year)
3369
                off -= 24 * 60;
3370
        else if (lt->tm_year > gmt.tm_year)
3371
                off += 24 * 60;
3372
        else if (lt->tm_yday < gmt.tm_yday)
3373
                off -= 24 * 60;
3374
        else if (lt->tm_yday > gmt.tm_yday)
3375
                off += 24 * 60;
3376

    
3377
        if (off >= 24 * 60)                /* should be impossible */
3378
                off = 23 * 60 + 59;        /* if not, insert silly value */
3379
        if (off <= -24 * 60)
3380
                off = -(23 * 60 + 59);
3381

    
3382
        return off * 60;
3383
}
3384

    
3385
/* calculate timezone offset (buf must not be less than 6 bytes) */
3386
gchar *tzoffset_buf(gchar *buf, time_t *now)
3387
{
3388
        struct tm gmt, *tmp, *lt;
3389
        gint off;
3390
        gchar sign = '+';
3391

    
3392
        tmp = gmtime(now);
3393
        g_return_val_if_fail(tmp != NULL, NULL);
3394
        gmt = *tmp;
3395
        lt = localtime(now);
3396
        g_return_val_if_fail(lt != NULL, NULL);
3397

    
3398
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3399

    
3400
        if (lt->tm_year < gmt.tm_year)
3401
                off -= 24 * 60;
3402
        else if (lt->tm_year > gmt.tm_year)
3403
                off += 24 * 60;
3404
        else if (lt->tm_yday < gmt.tm_yday)
3405
                off -= 24 * 60;
3406
        else if (lt->tm_yday > gmt.tm_yday)
3407
                off += 24 * 60;
3408

    
3409
        if (off < 0) {
3410
                sign = '-';
3411
                off = -off;
3412
        }
3413

    
3414
        if (off >= 24 * 60)                /* should be impossible */
3415
                off = 23 * 60 + 59;        /* if not, insert silly value */
3416

    
3417
        g_snprintf(buf, 6, "%c%02d%02d", sign, off / 60, off % 60);
3418

    
3419
        return buf;
3420
}
3421

    
3422
gchar *tzoffset(time_t *now)
3423
{
3424
        static gchar offset_string[6];
3425

    
3426
        return tzoffset_buf(offset_string, now);
3427
}
3428

    
3429
void get_rfc822_date(gchar *buf, gint len)
3430
{
3431
        struct tm *lt;
3432
        time_t t;
3433
        gchar day[4], mon[4];
3434
        gint dd, hh, mm, ss, yyyy;
3435
        gchar off[6];
3436

    
3437
        t = time(NULL);
3438
        lt = localtime(&t);
3439

    
3440
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3441
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
3442
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3443
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t));
3444
}
3445

    
3446
/* just a wrapper to suppress the warning of gcc about %c */
3447
size_t my_strftime(gchar *s, size_t max, const gchar *format,
3448
                   const struct tm *tm)
3449
{
3450
        return strftime(s, max, format, tm);
3451
}
3452

    
3453
/* logging */
3454

    
3455
gboolean get_debug_mode(void)
3456
{
3457
        return debug_mode;
3458
}
3459

    
3460
void set_debug_mode(gboolean enable)
3461
{
3462
        debug_mode = enable;
3463
}
3464

    
3465
void debug_print(const gchar *format, ...)
3466
{
3467
        va_list args;
3468
        gchar buf[BUFFSIZE];
3469

    
3470
        if (!debug_mode) return;
3471

    
3472
        va_start(args, format);
3473
        g_vsnprintf(buf, sizeof(buf), format, args);
3474
        va_end(args);
3475

    
3476
        g_print("%s", buf);
3477
}