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