2007-10-01 [colin] 3.0.1cvs2-stable
[claws.git] / src / common / utils.c
blob200c7c494ce562fdbcd0dd4534f5fdb2eda84bf8
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include "defs.h"
26 #include <glib.h>
28 #include <glib/gi18n.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
37 # include <wchar.h>
38 # include <wctype.h>
39 #endif
40 #include <stdlib.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <stdarg.h>
44 #include <sys/types.h>
45 #if HAVE_SYS_WAIT_H
46 # include <sys/wait.h>
47 #endif
48 #include <dirent.h>
49 #include <time.h>
50 #include <regex.h>
52 #ifdef G_OS_UNIX
53 #include <sys/utsname.h>
54 #endif
56 #ifdef G_OS_WIN32
57 # include <direct.h>
58 # include <io.h>
59 # include <fcntl.h>
60 # include <w32lib.h>
61 #endif
63 #ifdef MAEMO
64 #include <libosso.h>
65 #include <osso-browser-interface.h>
66 #endif
68 #include "utils.h"
69 #include "socket.h"
70 #include "../codeconv.h"
72 #define BUFFSIZE 8192
74 static gboolean debug_mode = FALSE;
75 #ifdef G_OS_WIN32
76 static GSList *tempfiles=NULL;
77 #endif
79 /* Return true if we are running as root. This function should beused
80 instead of getuid () == 0. */
81 gboolean superuser_p (void)
83 #ifdef G_OS_WIN32
84 return w32_is_administrator ();
85 #else
86 return !getuid();
87 #endif
92 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
93 gint g_chdir(const gchar *path)
95 #ifdef G_OS_WIN32
96 if (G_WIN32_HAVE_WIDECHAR_API()) {
97 wchar_t *wpath;
98 gint retval;
99 gint save_errno;
101 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
102 if (wpath == NULL) {
103 errno = EINVAL;
104 return -1;
107 retval = _wchdir(wpath);
108 save_errno = errno;
110 g_free(wpath);
112 errno = save_errno;
113 return retval;
114 } else {
115 gchar *cp_path;
116 gint retval;
117 gint save_errno;
119 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
120 if (cp_path == NULL) {
121 errno = EINVAL;
122 return -1;
125 retval = chdir(cp_path);
126 save_errno = errno;
128 g_free(cp_path);
130 errno = save_errno;
131 return retval;
133 #else
134 return chdir(path);
135 #endif
138 gint g_chmod(const gchar *path, gint mode)
140 #ifdef G_OS_WIN32
141 if (G_WIN32_HAVE_WIDECHAR_API()) {
142 wchar_t *wpath;
143 gint retval;
144 gint save_errno;
146 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
147 if (wpath == NULL) {
148 errno = EINVAL;
149 return -1;
152 retval = _wchmod(wpath, mode);
153 save_errno = errno;
155 g_free(wpath);
157 errno = save_errno;
158 return retval;
159 } else {
160 gchar *cp_path;
161 gint retval;
162 gint save_errno;
164 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
165 if (cp_path == NULL) {
166 errno = EINVAL;
167 return -1;
170 retval = chmod(cp_path, mode);
171 save_errno = errno;
173 g_free(cp_path);
175 errno = save_errno;
176 return retval;
178 #else
179 return chmod(path, mode);
180 #endif
182 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
185 #ifdef G_OS_WIN32
186 gint mkstemp_name(const gchar *template, gchar **name_used)
188 static gulong count=0; /* W32-_mktemp only supports up to 27
189 tempfiles... */
190 int tmpfd;
192 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
193 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
194 (S_IRUSR | S_IWUSR));
196 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
197 if (tmpfd<0) {
198 perror(g_strdup_printf("cant create %s",*name_used));
199 return -1;
201 else
202 return tmpfd;
204 #endif /* G_OS_WIN32 */
206 #ifdef G_OS_WIN32
207 gint mkstemp(const gchar *template)
209 gchar *dummyname;
210 gint res = mkstemp_name(template, &dummyname);
211 g_free(dummyname);
212 return res;
214 #endif /* G_OS_WIN32 */
216 void list_free_strings(GList *list)
218 list = g_list_first(list);
220 while (list != NULL) {
221 g_free(list->data);
222 list = list->next;
226 void slist_free_strings(GSList *list)
228 while (list != NULL) {
229 g_free(list->data);
230 list = list->next;
234 GSList *slist_concat_unique (GSList *first, GSList *second)
236 GSList *tmp, *ret;
237 if (first == NULL) {
238 if (second == NULL)
239 return NULL;
240 else
241 return second;
242 } else if (second == NULL)
243 return first;
244 ret = first;
245 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
246 if (g_slist_find(ret, tmp->data) == NULL)
247 ret = g_slist_prepend(ret, tmp->data);
249 return ret;
252 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
254 g_free(key);
257 void hash_free_strings(GHashTable *table)
259 g_hash_table_foreach(table, hash_free_strings_func, NULL);
262 static void hash_free_value_mem_func(gpointer key, gpointer value,
263 gpointer data)
265 g_free(value);
268 void hash_free_value_mem(GHashTable *table)
270 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
273 gint str_case_equal(gconstpointer v, gconstpointer v2)
275 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
278 guint str_case_hash(gconstpointer key)
280 const gchar *p = key;
281 guint h = *p;
283 if (h) {
284 h = g_ascii_tolower(h);
285 for (p += 1; *p != '\0'; p++)
286 h = (h << 5) - h + g_ascii_tolower(*p);
289 return h;
292 void ptr_array_free_strings(GPtrArray *array)
294 gint i;
295 gchar *str;
297 g_return_if_fail(array != NULL);
299 for (i = 0; i < array->len; i++) {
300 str = g_ptr_array_index(array, i);
301 g_free(str);
305 gboolean str_find(const gchar *haystack, const gchar *needle)
307 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
310 gboolean str_case_find(const gchar *haystack, const gchar *needle)
312 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
315 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
317 return strcmp(haystack, needle) == 0;
320 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
322 return g_ascii_strcasecmp(haystack, needle) == 0;
325 gint to_number(const gchar *nstr)
327 register const gchar *p;
329 if (*nstr == '\0') return -1;
331 for (p = nstr; *p != '\0'; p++)
332 if (!g_ascii_isdigit(*p)) return -1;
334 return atoi(nstr);
337 /* convert integer into string,
338 nstr must be not lower than 11 characters length */
339 gchar *itos_buf(gchar *nstr, gint n)
341 g_snprintf(nstr, 11, "%d", n);
342 return nstr;
345 /* convert integer into string */
346 gchar *itos(gint n)
348 static gchar nstr[11];
350 return itos_buf(nstr, n);
353 #define divide(num,divisor,i,d) \
355 i = num >> divisor; \
356 d = num & ((1<<divisor)-1); \
357 d = (d*100) >> divisor; \
360 gchar *to_human_readable(off_t size)
362 static gchar str[14];
363 static gchar *b_format = NULL, *kb_format = NULL,
364 *mb_format = NULL, *gb_format = NULL;
365 register int t = 0, r = 0;
366 if (b_format == NULL) {
367 b_format = _("%dB");
368 kb_format = _("%d.%02dKB");
369 mb_format = _("%d.%02dMB");
370 gb_format = _("%.2fGB");
373 if (size < (off_t)1024) {
374 g_snprintf(str, sizeof(str), b_format, (gint)size);
375 return str;
376 } else if (size >> 10 < (off_t)1024) {
377 divide(size, 10, t, r);
378 g_snprintf(str, sizeof(str), kb_format, t, r);
379 return str;
380 } else if (size >> 20 < (off_t)1024) {
381 divide(size, 20, t, r);
382 g_snprintf(str, sizeof(str), mb_format, t, r);
383 return str;
384 } else {
385 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
386 return str;
390 /* strcmp with NULL-checking */
391 gint strcmp2(const gchar *s1, const gchar *s2)
393 if (s1 == NULL || s2 == NULL)
394 return -1;
395 else
396 return strcmp(s1, s2);
398 /* strstr with NULL-checking */
399 gchar *strstr2(const gchar *s1, const gchar *s2)
401 if (s1 == NULL || s2 == NULL)
402 return NULL;
403 else
404 return strstr(s1, s2);
406 /* compare paths */
407 gint path_cmp(const gchar *s1, const gchar *s2)
409 gint len1, len2;
410 int rc;
411 #ifdef G_OS_WIN32
412 gchar *s1buf, *s2buf;
413 #endif
415 if (s1 == NULL || s2 == NULL) return -1;
416 if (*s1 == '\0' || *s2 == '\0') return -1;
418 #ifdef G_OS_WIN32
419 s1buf = g_strdup (s1);
420 s2buf = g_strdup (s2);
421 subst_char (s1buf, '/', G_DIR_SEPARATOR);
422 subst_char (s2buf, '/', G_DIR_SEPARATOR);
423 s1 = s1buf;
424 s2 = s2buf;
425 #endif /* !G_OS_WIN32 */
427 len1 = strlen(s1);
428 len2 = strlen(s2);
430 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
431 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
433 rc = strncmp(s1, s2, MAX(len1, len2));
434 #ifdef G_OS_WIN32
435 g_free (s1buf);
436 g_free (s2buf);
437 #endif /* !G_OS_WIN32 */
438 return rc;
441 /* remove trailing return code */
442 gchar *strretchomp(gchar *str)
444 register gchar *s;
446 if (!*str) return str;
448 for (s = str + strlen(str) - 1;
449 s >= str && (*s == '\n' || *s == '\r');
450 s--)
451 *s = '\0';
453 return str;
456 /* remove trailing character */
457 gchar *strtailchomp(gchar *str, gchar tail_char)
459 register gchar *s;
461 if (!*str) return str;
462 if (tail_char == '\0') return str;
464 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
465 *s = '\0';
467 return str;
470 /* remove CR (carriage return) */
471 gchar *strcrchomp(gchar *str)
473 register gchar *s;
475 if (!*str) return str;
477 s = str + strlen(str) - 1;
478 if (*s == '\n' && s > str && *(s - 1) == '\r') {
479 *(s - 1) = '\n';
480 *s = '\0';
483 return str;
486 void file_strip_crs(const gchar *file)
488 FILE *fp = NULL, *outfp = NULL;
489 gchar buf[4096];
490 gchar *out = get_tmp_file();
491 if (file == NULL)
492 goto freeout;
494 fp = fopen(file, "rb");
495 if (!fp)
496 goto freeout;
498 outfp = fopen(out, "wb");
499 if (!outfp) {
500 fclose(fp);
501 goto freeout;
504 while (fgets(buf, sizeof (buf), fp) != NULL) {
505 strcrchomp(buf);
506 fputs(buf, outfp);
509 fclose(fp);
510 fclose(outfp);
511 rename_force(out, file);
512 freeout:
513 g_free(out);
516 /* Similar to `strstr' but this function ignores the case of both strings. */
517 gchar *strcasestr(const gchar *haystack, const gchar *needle)
519 register size_t haystack_len, needle_len;
521 haystack_len = strlen(haystack);
522 needle_len = strlen(needle);
524 if (haystack_len < needle_len || needle_len == 0)
525 return NULL;
527 while (haystack_len >= needle_len) {
528 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
529 return (gchar *)haystack;
530 else {
531 haystack++;
532 haystack_len--;
536 return NULL;
539 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
540 gconstpointer needle, size_t needlelen)
542 const gchar *haystack_ = (const gchar *)haystack;
543 const gchar *needle_ = (const gchar *)needle;
544 const gchar *haystack_cur = (const gchar *)haystack;
546 if (needlelen == 1)
547 return memchr(haystack_, *needle_, haystacklen);
549 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
550 != NULL) {
551 if (haystacklen - (haystack_cur - haystack_) < needlelen)
552 break;
553 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
554 return (gpointer)haystack_cur;
555 else
556 haystack_cur++;
559 return NULL;
562 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
563 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
565 register const gchar *s = src;
566 register gchar *d = dest;
568 while (--n && *s)
569 *d++ = *s++;
570 *d = '\0';
572 return dest;
576 /* Examine if next block is non-ASCII string */
577 gboolean is_next_nonascii(const gchar *s)
579 const gchar *p;
581 /* skip head space */
582 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
584 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
585 if (*(guchar *)p > 127 || *(guchar *)p < 32)
586 return TRUE;
589 return FALSE;
592 gint get_next_word_len(const gchar *s)
594 gint len = 0;
596 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
599 return len;
602 /* compare subjects */
603 gint subject_compare(const gchar *s1, const gchar *s2)
605 gchar *str1, *str2;
607 if (!s1 || !s2) return -1;
608 if (!*s1 || !*s2) return -1;
610 Xstrdup_a(str1, s1, return -1);
611 Xstrdup_a(str2, s2, return -1);
613 trim_subject_for_compare(str1);
614 trim_subject_for_compare(str2);
616 if (!*str1 || !*str2) return -1;
618 return strcmp(str1, str2);
621 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
623 gchar *str1, *str2;
625 if (!s1 || !s2) return -1;
627 Xstrdup_a(str1, s1, return -1);
628 Xstrdup_a(str2, s2, return -1);
630 trim_subject_for_sort(str1);
631 trim_subject_for_sort(str2);
633 return g_utf8_collate(str1, str2);
636 void trim_subject_for_compare(gchar *str)
638 gchar *srcp;
640 eliminate_parenthesis(str, '[', ']');
641 eliminate_parenthesis(str, '(', ')');
642 g_strstrip(str);
644 srcp = str + subject_get_prefix_length(str);
645 if (srcp != str)
646 memmove(str, srcp, strlen(srcp) + 1);
649 void trim_subject_for_sort(gchar *str)
651 gchar *srcp;
653 g_strstrip(str);
655 srcp = str + subject_get_prefix_length(str);
656 if (srcp != str)
657 memmove(str, srcp, strlen(srcp) + 1);
660 void trim_subject(gchar *str)
662 register gchar *srcp;
663 gchar op, cl;
664 gint in_brace;
666 g_strstrip(str);
668 srcp = str + subject_get_prefix_length(str);
670 if (*srcp == '[') {
671 op = '[';
672 cl = ']';
673 } else if (*srcp == '(') {
674 op = '(';
675 cl = ')';
676 } else
677 op = 0;
679 if (op) {
680 ++srcp;
681 in_brace = 1;
682 while (*srcp) {
683 if (*srcp == op)
684 in_brace++;
685 else if (*srcp == cl)
686 in_brace--;
687 srcp++;
688 if (in_brace == 0)
689 break;
692 while (g_ascii_isspace(*srcp)) srcp++;
693 memmove(str, srcp, strlen(srcp) + 1);
696 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
698 register gchar *srcp, *destp;
699 gint in_brace;
701 srcp = destp = str;
703 while ((destp = strchr(destp, op))) {
704 in_brace = 1;
705 srcp = destp + 1;
706 while (*srcp) {
707 if (*srcp == op)
708 in_brace++;
709 else if (*srcp == cl)
710 in_brace--;
711 srcp++;
712 if (in_brace == 0)
713 break;
715 while (g_ascii_isspace(*srcp)) srcp++;
716 memmove(destp, srcp, strlen(srcp) + 1);
720 void extract_parenthesis(gchar *str, gchar op, gchar cl)
722 register gchar *srcp, *destp;
723 gint in_brace;
725 srcp = destp = str;
727 while ((srcp = strchr(destp, op))) {
728 if (destp > str)
729 *destp++ = ' ';
730 memmove(destp, srcp + 1, strlen(srcp));
731 in_brace = 1;
732 while(*destp) {
733 if (*destp == op)
734 in_brace++;
735 else if (*destp == cl)
736 in_brace--;
738 if (in_brace == 0)
739 break;
741 destp++;
744 *destp = '\0';
747 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
748 gchar op, gchar cl)
750 register gchar *srcp, *destp;
751 gint in_brace;
752 gboolean in_quote = FALSE;
754 srcp = destp = str;
756 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
757 if (destp > str)
758 *destp++ = ' ';
759 memmove(destp, srcp + 1, strlen(srcp));
760 in_brace = 1;
761 while(*destp) {
762 if (*destp == op && !in_quote)
763 in_brace++;
764 else if (*destp == cl && !in_quote)
765 in_brace--;
766 else if (*destp == quote_chr)
767 in_quote ^= TRUE;
769 if (in_brace == 0)
770 break;
772 destp++;
775 *destp = '\0';
778 void eliminate_quote(gchar *str, gchar quote_chr)
780 register gchar *srcp, *destp;
782 srcp = destp = str;
784 while ((destp = strchr(destp, quote_chr))) {
785 if ((srcp = strchr(destp + 1, quote_chr))) {
786 srcp++;
787 while (g_ascii_isspace(*srcp)) srcp++;
788 memmove(destp, srcp, strlen(srcp) + 1);
789 } else {
790 *destp = '\0';
791 break;
796 void extract_quote(gchar *str, gchar quote_chr)
798 register gchar *p;
800 if ((str = strchr(str, quote_chr))) {
801 p = str;
802 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
803 memmove(p - 1, p, strlen(p) + 1);
804 p--;
806 if(p) {
807 *p = '\0';
808 memmove(str, str + 1, p - str);
813 void eliminate_address_comment(gchar *str)
815 register gchar *srcp, *destp;
816 gint in_brace;
818 srcp = destp = str;
820 while ((destp = strchr(destp, '"'))) {
821 if ((srcp = strchr(destp + 1, '"'))) {
822 srcp++;
823 if (*srcp == '@') {
824 destp = srcp + 1;
825 } else {
826 while (g_ascii_isspace(*srcp)) srcp++;
827 memmove(destp, srcp, strlen(srcp) + 1);
829 } else {
830 *destp = '\0';
831 break;
835 srcp = destp = str;
837 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 in_brace = 1;
839 srcp = destp + 1;
840 while (*srcp) {
841 if (*srcp == '(')
842 in_brace++;
843 else if (*srcp == ')')
844 in_brace--;
845 srcp++;
846 if (in_brace == 0)
847 break;
849 while (g_ascii_isspace(*srcp)) srcp++;
850 memmove(destp, srcp, strlen(srcp) + 1);
854 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
856 gboolean in_quote = FALSE;
858 while (*str) {
859 if (*str == c && !in_quote)
860 return (gchar *)str;
861 if (*str == quote_chr)
862 in_quote ^= TRUE;
863 str++;
866 return NULL;
869 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
871 gboolean in_quote = FALSE;
872 const gchar *p;
874 p = str + strlen(str) - 1;
875 while (p >= str) {
876 if (*p == c && !in_quote)
877 return (gchar *)p;
878 if (*p == quote_chr)
879 in_quote ^= TRUE;
880 p--;
883 return NULL;
886 void extract_address(gchar *str)
888 eliminate_address_comment(str);
889 if (strchr_with_skip_quote(str, '"', '<'))
890 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
891 g_strstrip(str);
894 void extract_list_id_str(gchar *str)
896 if (strchr_with_skip_quote(str, '"', '<'))
897 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
898 g_strstrip(str);
901 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
903 gchar *work;
904 gchar *workp;
906 if (!str) return addr_list;
908 Xstrdup_a(work, str, return addr_list);
910 if (removecomments)
911 eliminate_address_comment(work);
912 workp = work;
914 while (workp && *workp) {
915 gchar *p, *next;
917 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
918 *p = '\0';
919 next = p + 1;
920 } else
921 next = NULL;
923 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
924 extract_parenthesis_with_skip_quote
925 (workp, '"', '<', '>');
927 g_strstrip(workp);
928 if (*workp)
929 addr_list = g_slist_append(addr_list, g_strdup(workp));
931 workp = next;
934 return addr_list;
937 GSList *address_list_append(GSList *addr_list, const gchar *str)
939 return address_list_append_real(addr_list, str, TRUE);
942 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
944 return address_list_append_real(addr_list, str, FALSE);
947 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
949 const gchar *strp;
951 if (!str) return msgid_list;
952 strp = str;
954 while (strp && *strp) {
955 const gchar *start, *end;
956 gchar *msgid;
958 if ((start = strchr(strp, '<')) != NULL) {
959 end = strchr(start + 1, '>');
960 if (!end) break;
961 } else
962 break;
964 msgid = g_strndup(start + 1, end - start - 1);
965 g_strstrip(msgid);
966 if (*msgid)
967 msgid_list = g_slist_prepend(msgid_list, msgid);
968 else
969 g_free(msgid);
971 strp = end + 1;
974 return msgid_list;
977 GSList *references_list_append(GSList *msgid_list, const gchar *str)
979 GSList *list;
981 list = references_list_prepend(NULL, str);
982 list = g_slist_reverse(list);
983 msgid_list = g_slist_concat(msgid_list, list);
985 return msgid_list;
988 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
990 gchar *work;
991 gchar *workp;
993 if (!str) return group_list;
995 Xstrdup_a(work, str, return group_list);
997 workp = work;
999 while (workp && *workp) {
1000 gchar *p, *next;
1002 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1003 *p = '\0';
1004 next = p + 1;
1005 } else
1006 next = NULL;
1008 g_strstrip(workp);
1009 if (*workp)
1010 group_list = g_slist_append(group_list,
1011 g_strdup(workp));
1013 workp = next;
1016 return group_list;
1019 GList *add_history(GList *list, const gchar *str)
1021 GList *old;
1023 g_return_val_if_fail(str != NULL, list);
1025 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1026 if (old) {
1027 g_free(old->data);
1028 list = g_list_remove(list, old->data);
1029 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1030 GList *last;
1032 last = g_list_last(list);
1033 if (last) {
1034 g_free(last->data);
1035 list = g_list_remove(list, last->data);
1039 list = g_list_prepend(list, g_strdup(str));
1041 return list;
1044 void remove_return(gchar *str)
1046 register gchar *p = str;
1048 while (*p) {
1049 if (*p == '\n' || *p == '\r')
1050 memmove(p, p + 1, strlen(p));
1051 else
1052 p++;
1056 void remove_space(gchar *str)
1058 register gchar *p = str;
1059 register gint spc;
1061 while (*p) {
1062 spc = 0;
1063 while (g_ascii_isspace(*(p + spc)))
1064 spc++;
1065 if (spc)
1066 memmove(p, p + spc, strlen(p + spc) + 1);
1067 else
1068 p++;
1072 void unfold_line(gchar *str)
1074 register gchar *p = str;
1075 register gint spc;
1077 while (*p) {
1078 if (*p == '\n' || *p == '\r') {
1079 *p++ = ' ';
1080 spc = 0;
1081 while (g_ascii_isspace(*(p + spc)))
1082 spc++;
1083 if (spc)
1084 memmove(p, p + spc, strlen(p + spc) + 1);
1085 } else
1086 p++;
1090 void subst_char(gchar *str, gchar orig, gchar subst)
1092 register gchar *p = str;
1094 while (*p) {
1095 if (*p == orig)
1096 *p = subst;
1097 p++;
1101 void subst_chars(gchar *str, gchar *orig, gchar subst)
1103 register gchar *p = str;
1105 while (*p) {
1106 if (strchr(orig, *p) != NULL)
1107 *p = subst;
1108 p++;
1112 void subst_for_filename(gchar *str)
1114 if (!str)
1115 return;
1116 #ifdef G_OS_WIN32
1117 subst_chars(str, "\t\r\n\\/*:", '_');
1118 #else
1119 subst_chars(str, "\t\r\n\\/*", '_');
1120 #endif
1123 void subst_for_shellsafe_filename(gchar *str)
1125 if (!str)
1126 return;
1127 subst_for_filename(str);
1128 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1131 gboolean is_header_line(const gchar *str)
1133 if (str[0] == ':') return FALSE;
1135 while (*str != '\0' && *str != ' ') {
1136 if (*str == ':')
1137 return TRUE;
1138 str++;
1141 return FALSE;
1144 gboolean is_ascii_str(const gchar *str)
1146 const guchar *p = (const guchar *)str;
1148 while (*p != '\0') {
1149 if (*p != '\t' && *p != ' ' &&
1150 *p != '\r' && *p != '\n' &&
1151 (*p < 32 || *p >= 127))
1152 return FALSE;
1153 p++;
1156 return TRUE;
1159 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1161 const gchar *first_pos;
1162 const gchar *last_pos;
1163 const gchar *p = str;
1164 gint quote_level = -1;
1166 /* speed up line processing by only searching to the last '>' */
1167 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1168 /* skip a line if it contains a '<' before the initial '>' */
1169 if (memchr(str, '<', first_pos - str) != NULL)
1170 return -1;
1171 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1172 } else
1173 return -1;
1175 while (p <= last_pos) {
1176 while (p < last_pos) {
1177 if (g_ascii_isspace(*p))
1178 p++;
1179 else
1180 break;
1183 if (strchr(quote_chars, *p))
1184 quote_level++;
1185 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1186 /* any characters are allowed except '-' and space */
1187 while (*p != '-'
1188 && !strchr(quote_chars, *p)
1189 && !g_ascii_isspace(*p)
1190 && p < last_pos)
1191 p++;
1192 if (strchr(quote_chars, *p))
1193 quote_level++;
1194 else
1195 break;
1198 p++;
1201 return quote_level;
1204 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1206 const gchar *p = str, *q;
1207 gint cur_line = 0, len;
1209 while ((q = strchr(p, '\n')) != NULL) {
1210 len = q - p + 1;
1211 if (len > max_chars) {
1212 if (line)
1213 *line = cur_line;
1214 return -1;
1216 p = q + 1;
1217 ++cur_line;
1220 len = strlen(p);
1221 if (len > max_chars) {
1222 if (line)
1223 *line = cur_line;
1224 return -1;
1227 return 0;
1230 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1232 gchar * position = NULL;
1233 gchar * tmp_pos = NULL;
1234 int i;
1236 if (quote_chars == NULL)
1237 return FALSE;
1239 for (i = 0; i < strlen(quote_chars); i++) {
1240 tmp_pos = strchr (str, quote_chars[i]);
1241 if(position == NULL
1242 || (tmp_pos != NULL && position >= tmp_pos) )
1243 position = tmp_pos;
1245 return position;
1248 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1250 gchar * position = NULL;
1251 gchar * tmp_pos = NULL;
1252 int i;
1254 if (quote_chars == NULL)
1255 return FALSE;
1257 for (i = 0; i < strlen(quote_chars); i++) {
1258 tmp_pos = strrchr (str, quote_chars[i]);
1259 if(position == NULL
1260 || (tmp_pos != NULL && position <= tmp_pos) )
1261 position = tmp_pos;
1263 return position;
1266 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1268 register guint haystack_len, needle_len;
1269 gboolean in_squote = FALSE, in_dquote = FALSE;
1271 haystack_len = strlen(haystack);
1272 needle_len = strlen(needle);
1274 if (haystack_len < needle_len || needle_len == 0)
1275 return NULL;
1277 while (haystack_len >= needle_len) {
1278 if (!in_squote && !in_dquote &&
1279 !strncmp(haystack, needle, needle_len))
1280 return (gchar *)haystack;
1282 /* 'foo"bar"' -> foo"bar"
1283 "foo'bar'" -> foo'bar' */
1284 if (*haystack == '\'') {
1285 if (in_squote)
1286 in_squote = FALSE;
1287 else if (!in_dquote)
1288 in_squote = TRUE;
1289 } else if (*haystack == '\"') {
1290 if (in_dquote)
1291 in_dquote = FALSE;
1292 else if (!in_squote)
1293 in_dquote = TRUE;
1296 haystack++;
1297 haystack_len--;
1300 return NULL;
1303 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1305 const gchar *p;
1306 gchar quote_chr = '"';
1307 gint in_brace;
1308 gboolean in_quote = FALSE;
1310 p = str;
1312 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1313 p++;
1314 in_brace = 1;
1315 while (*p) {
1316 if (*p == op && !in_quote)
1317 in_brace++;
1318 else if (*p == cl && !in_quote)
1319 in_brace--;
1320 else if (*p == quote_chr)
1321 in_quote ^= TRUE;
1323 if (in_brace == 0)
1324 return (gchar *)p;
1326 p++;
1330 return NULL;
1333 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1334 gint max_tokens)
1336 GSList *string_list = NULL, *slist;
1337 gchar **str_array;
1338 const gchar *s_op, *s_cl;
1339 guint i, n = 1;
1341 g_return_val_if_fail(str != NULL, NULL);
1343 if (max_tokens < 1)
1344 max_tokens = G_MAXINT;
1346 s_op = strchr_with_skip_quote(str, '"', op);
1347 if (!s_op) return NULL;
1348 str = s_op;
1349 s_cl = strchr_parenthesis_close(str, op, cl);
1350 if (s_cl) {
1351 do {
1352 guint len;
1353 gchar *new_string;
1355 str++;
1356 len = s_cl - str;
1357 new_string = g_new(gchar, len + 1);
1358 strncpy(new_string, str, len);
1359 new_string[len] = 0;
1360 string_list = g_slist_prepend(string_list, new_string);
1361 n++;
1362 str = s_cl + 1;
1364 while (*str && g_ascii_isspace(*str)) str++;
1365 if (*str != op) {
1366 string_list = g_slist_prepend(string_list,
1367 g_strdup(""));
1368 n++;
1369 s_op = strchr_with_skip_quote(str, '"', op);
1370 if (!--max_tokens || !s_op) break;
1371 str = s_op;
1372 } else
1373 s_op = str;
1374 s_cl = strchr_parenthesis_close(str, op, cl);
1375 } while (--max_tokens && s_cl);
1378 str_array = g_new(gchar*, n);
1380 i = n - 1;
1382 str_array[i--] = NULL;
1383 for (slist = string_list; slist; slist = slist->next)
1384 str_array[i--] = slist->data;
1386 g_slist_free(string_list);
1388 return str_array;
1391 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1392 gint max_tokens)
1394 GSList *string_list = NULL, *slist;
1395 gchar **str_array, *s, *new_str;
1396 guint i, n = 1, len;
1398 g_return_val_if_fail(str != NULL, NULL);
1399 g_return_val_if_fail(delim != NULL, NULL);
1401 if (max_tokens < 1)
1402 max_tokens = G_MAXINT;
1404 s = strstr_with_skip_quote(str, delim);
1405 if (s) {
1406 guint delimiter_len = strlen(delim);
1408 do {
1409 len = s - str;
1410 new_str = g_strndup(str, len);
1412 if (new_str[0] == '\'' || new_str[0] == '\"') {
1413 if (new_str[len - 1] == new_str[0]) {
1414 new_str[len - 1] = '\0';
1415 memmove(new_str, new_str + 1, len - 1);
1418 string_list = g_slist_prepend(string_list, new_str);
1419 n++;
1420 str = s + delimiter_len;
1421 s = strstr_with_skip_quote(str, delim);
1422 } while (--max_tokens && s);
1425 if (*str) {
1426 new_str = g_strdup(str);
1427 if (new_str[0] == '\'' || new_str[0] == '\"') {
1428 len = strlen(str);
1429 if (new_str[len - 1] == new_str[0]) {
1430 new_str[len - 1] = '\0';
1431 memmove(new_str, new_str + 1, len - 1);
1434 string_list = g_slist_prepend(string_list, new_str);
1435 n++;
1438 str_array = g_new(gchar*, n);
1440 i = n - 1;
1442 str_array[i--] = NULL;
1443 for (slist = string_list; slist; slist = slist->next)
1444 str_array[i--] = slist->data;
1446 g_slist_free(string_list);
1448 return str_array;
1451 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1453 gchar *abbrev_group;
1454 gchar *ap;
1455 const gchar *p = group;
1456 const gchar *last;
1458 g_return_val_if_fail(group != NULL, NULL);
1460 last = group + strlen(group);
1461 abbrev_group = ap = g_malloc(strlen(group) + 1);
1463 while (*p) {
1464 while (*p == '.')
1465 *ap++ = *p++;
1466 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1467 *ap++ = *p++;
1468 while (*p != '.') p++;
1469 } else {
1470 strcpy(ap, p);
1471 return abbrev_group;
1475 *ap = '\0';
1476 return abbrev_group;
1479 gchar *trim_string(const gchar *str, gint len)
1481 const gchar *p = str;
1482 gint mb_len;
1483 gchar *new_str;
1484 gint new_len = 0;
1486 if (!str) return NULL;
1487 if (strlen(str) <= len)
1488 return g_strdup(str);
1489 if (g_utf8_validate(str, -1, NULL) == FALSE)
1490 return g_strdup(str);
1492 while (*p != '\0') {
1493 mb_len = g_utf8_skip[*(guchar *)p];
1494 if (mb_len == 0)
1495 break;
1496 else if (new_len + mb_len > len)
1497 break;
1499 new_len += mb_len;
1500 p += mb_len;
1503 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1504 return g_strconcat(new_str, "...", NULL);
1507 GList *uri_list_extract_filenames(const gchar *uri_list)
1509 GList *result = NULL;
1510 const gchar *p, *q;
1511 gchar *escaped_utf8uri;
1513 p = uri_list;
1515 while (p) {
1516 if (*p != '#') {
1517 while (g_ascii_isspace(*p)) p++;
1518 if (!strncmp(p, "file:", 5)) {
1519 q = p;
1520 q += 5;
1521 while (*q && *q != '\n' && *q != '\r') q++;
1523 if (q > p) {
1524 gchar *file, *locale_file = NULL;
1525 q--;
1526 while (q > p && g_ascii_isspace(*q))
1527 q--;
1528 Xalloca(escaped_utf8uri, q - p + 2,
1529 return result);
1530 Xalloca(file, q - p + 2,
1531 return result);
1532 *file = '\0';
1533 strncpy(escaped_utf8uri, p, q - p + 1);
1534 escaped_utf8uri[q - p + 1] = '\0';
1535 decode_uri(file, escaped_utf8uri);
1537 * g_filename_from_uri() rejects escaped/locale encoded uri
1538 * string which come from Nautilus.
1540 if (g_utf8_validate(file, -1, NULL))
1541 locale_file
1542 = conv_codeset_strdup(
1543 file + 5,
1544 CS_UTF_8,
1545 conv_get_locale_charset_str());
1546 if (!locale_file)
1547 locale_file = g_strdup(file + 5);
1548 result = g_list_append(result, locale_file);
1552 p = strchr(p, '\n');
1553 if (p) p++;
1556 return result;
1559 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1560 * characters
1562 static gint axtoi(const gchar *hexstr)
1564 gint hi, lo, result;
1566 hi = hexstr[0];
1567 if ('0' <= hi && hi <= '9') {
1568 hi -= '0';
1569 } else
1570 if ('a' <= hi && hi <= 'f') {
1571 hi -= ('a' - 10);
1572 } else
1573 if ('A' <= hi && hi <= 'F') {
1574 hi -= ('A' - 10);
1577 lo = hexstr[1];
1578 if ('0' <= lo && lo <= '9') {
1579 lo -= '0';
1580 } else
1581 if ('a' <= lo && lo <= 'f') {
1582 lo -= ('a'-10);
1583 } else
1584 if ('A' <= lo && lo <= 'F') {
1585 lo -= ('A' - 10);
1587 result = lo + (16 * hi);
1588 return result;
1591 gboolean is_uri_string(const gchar *str)
1593 while (str && *str && g_ascii_isspace(*str))
1594 str++;
1595 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1596 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1597 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1598 g_ascii_strncasecmp(str, "www.", 4) == 0);
1601 gchar *get_uri_path(const gchar *uri)
1603 while (uri && *uri && g_ascii_isspace(*uri))
1604 uri++;
1605 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1606 return (gchar *)(uri + 7);
1607 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1608 return (gchar *)(uri + 8);
1609 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1610 return (gchar *)(uri + 6);
1611 else
1612 return (gchar *)uri;
1615 gint get_uri_len(const gchar *str)
1617 const gchar *p;
1619 if (is_uri_string(str)) {
1620 for (p = str; *p != '\0'; p++) {
1621 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1622 break;
1624 return p - str;
1627 return 0;
1630 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1631 * plusses, and escape characters are used)
1633 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1635 gchar *dec = decoded_uri;
1636 const gchar *enc = encoded_uri;
1638 while (*enc) {
1639 if (*enc == '%') {
1640 enc++;
1641 if (isxdigit((guchar)enc[0]) &&
1642 isxdigit((guchar)enc[1])) {
1643 *dec = axtoi(enc);
1644 dec++;
1645 enc += 2;
1647 } else {
1648 if (with_plus && *enc == '+')
1649 *dec = ' ';
1650 else
1651 *dec = *enc;
1652 dec++;
1653 enc++;
1657 *dec = '\0';
1660 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1662 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1665 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1666 gchar **subject, gchar **body, gchar **attach)
1668 gchar *tmp_mailto;
1669 gchar *p;
1670 const gchar *forbidden_uris[] = { ".gnupg/",
1671 "/etc/passwd",
1672 "/etc/shadow",
1673 NULL };
1675 Xstrdup_a(tmp_mailto, mailto, return -1);
1677 if (!strncmp(tmp_mailto, "mailto:", 7))
1678 tmp_mailto += 7;
1680 p = strchr(tmp_mailto, '?');
1681 if (p) {
1682 *p = '\0';
1683 p++;
1686 if (to && !*to)
1687 *to = g_strdup(tmp_mailto);
1689 while (p) {
1690 gchar *field, *value;
1692 field = p;
1694 p = strchr(p, '=');
1695 if (!p) break;
1696 *p = '\0';
1697 p++;
1699 value = p;
1701 p = strchr(p, '&');
1702 if (p) {
1703 *p = '\0';
1704 p++;
1707 if (*value == '\0') continue;
1709 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1710 *cc = g_strdup(value);
1711 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1712 *bcc = g_strdup(value);
1713 } else if (subject && !*subject &&
1714 !g_ascii_strcasecmp(field, "subject")) {
1715 *subject = g_malloc(strlen(value) + 1);
1716 decode_uri(*subject, value);
1717 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1718 *body = g_malloc(strlen(value) + 1);
1719 decode_uri(*body, value);
1720 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1721 int i = 0;
1722 *attach = g_malloc(strlen(value) + 1);
1723 decode_uri(*attach, value);
1724 for (; forbidden_uris[i]; i++) {
1725 if (strstr(*attach, forbidden_uris[i])) {
1726 g_print("Refusing to attach '%s', potential private data leak\n",
1727 *attach);
1728 g_free(*attach);
1729 *attach = NULL;
1730 break;
1736 return 0;
1740 #ifdef G_OS_WIN32
1741 #include <windows.h>
1742 #ifndef CSIDL_APPDATA
1743 #define CSIDL_APPDATA 0x001a
1744 #endif
1745 #ifndef CSIDL_LOCAL_APPDATA
1746 #define CSIDL_LOCAL_APPDATA 0x001c
1747 #endif
1748 #ifndef CSIDL_FLAG_CREATE
1749 #define CSIDL_FLAG_CREATE 0x8000
1750 #endif
1751 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1753 #define RTLD_LAZY 0
1754 const char *
1755 w32_strerror (int w32_errno)
1757 static char strerr[256];
1758 int ec = (int)GetLastError ();
1760 if (w32_errno == 0)
1761 w32_errno = ec;
1762 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1763 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1764 strerr, DIM (strerr)-1, NULL);
1765 return strerr;
1768 static __inline__ void *
1769 dlopen (const char * name, int flag)
1771 void * hd = LoadLibrary (name);
1772 return hd;
1775 static __inline__ void *
1776 dlsym (void * hd, const char * sym)
1778 if (hd && sym)
1780 void * fnc = GetProcAddress (hd, sym);
1781 if (!fnc)
1782 return NULL;
1783 return fnc;
1785 return NULL;
1789 static __inline__ const char *
1790 dlerror (void)
1792 return w32_strerror (0);
1796 static __inline__ int
1797 dlclose (void * hd)
1799 if (hd)
1801 FreeLibrary (hd);
1802 return 0;
1804 return -1;
1807 static HRESULT
1808 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1810 static int initialized;
1811 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1813 if (!initialized)
1815 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1816 void *handle;
1817 int i;
1819 initialized = 1;
1821 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1823 handle = dlopen (dllnames[i], RTLD_LAZY);
1824 if (handle)
1826 func = dlsym (handle, "SHGetFolderPathA");
1827 if (!func)
1829 dlclose (handle);
1830 handle = NULL;
1836 if (func)
1837 return func (a,b,c,d,e);
1838 else
1839 return -1;
1842 /* Returns a static string with the directroy from which the module
1843 has been loaded. Returns an empty string on error. */
1844 static char *w32_get_module_dir(void)
1846 static char *moddir;
1848 if (!moddir) {
1849 char name[MAX_PATH+10];
1850 char *p;
1852 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1853 *name = 0;
1854 else {
1855 p = strrchr (name, '\\');
1856 if (p)
1857 *p = 0;
1858 else
1859 *name = 0;
1861 moddir = g_strdup (name);
1863 return moddir;
1865 #endif /* G_OS_WIN32 */
1867 /* Return a static string with the locale dir. */
1868 const gchar *get_locale_dir(void)
1870 static gchar *loc_dir;
1872 #ifdef G_OS_WIN32
1873 if (!loc_dir)
1874 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1875 "\\share\\locale", NULL);
1876 #endif
1877 if (!loc_dir)
1878 loc_dir = LOCALEDIR;
1880 return loc_dir;
1884 const gchar *get_home_dir(void)
1886 #ifdef G_OS_WIN32
1887 static char home_dir[MAX_PATH] = "";
1889 if (home_dir[0] == '\0') {
1890 if (w32_shgetfolderpath
1891 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1892 NULL, 0, home_dir) < 0)
1893 strcpy (home_dir, "C:\\Sylpheed");
1895 return home_dir;
1896 #else
1897 static const gchar *homeenv = NULL;
1899 if (homeenv)
1900 return homeenv;
1902 if (!homeenv && g_getenv("HOME") != NULL)
1903 homeenv = g_strdup(g_getenv("HOME"));
1904 if (!homeenv)
1905 homeenv = g_get_home_dir();
1907 return homeenv;
1908 #endif
1911 const gchar *get_rc_dir(void)
1913 static gchar *rc_dir = NULL;
1915 if (!rc_dir)
1916 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1917 RC_DIR, NULL);
1919 return rc_dir;
1922 const gchar *get_mail_base_dir(void)
1924 #ifdef G_OS_WIN32
1925 static gchar *mail_base_dir = NULL;
1927 if (!mail_base_dir)
1928 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1929 "Mailboxes", NULL);
1931 return mail_base_dir;
1932 #else
1933 return get_home_dir();
1934 #endif
1937 #ifdef MAEMO
1938 const gchar *prefs_common_get_data_root(void);
1939 gchar *last_data_root = NULL;
1940 #endif
1942 const gchar *get_news_cache_dir(void)
1944 static gchar *news_cache_dir = NULL;
1945 #ifdef MAEMO
1946 const gchar *data_root = prefs_common_get_data_root();
1947 if (strcmp2(data_root, last_data_root)) {
1948 g_free(news_cache_dir);
1949 news_cache_dir = NULL;
1951 #endif
1952 if (!news_cache_dir)
1953 #ifndef MAEMO
1954 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1955 NEWS_CACHE_DIR, NULL);
1956 #else
1958 if (data_root) {
1959 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1960 "Claws", G_DIR_SEPARATOR_S,
1961 g_get_user_name(), G_DIR_SEPARATOR_S,
1962 NEWS_CACHE_DIR, NULL);
1963 g_free(last_data_root);
1964 last_data_root = g_strdup(last_data_root);
1965 } else {
1966 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 NEWS_CACHE_DIR, NULL);
1968 g_free(last_data_root);
1969 last_data_root = NULL;
1972 #endif
1973 return news_cache_dir;
1976 const gchar *get_imap_cache_dir(void)
1978 static gchar *imap_cache_dir = NULL;
1979 #ifdef MAEMO
1980 const gchar *data_root = prefs_common_get_data_root();
1981 if (strcmp2(data_root, last_data_root)) {
1982 g_free(imap_cache_dir);
1983 imap_cache_dir = NULL;
1985 #endif
1987 if (!imap_cache_dir)
1988 #ifndef MAEMO
1989 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1990 IMAP_CACHE_DIR, NULL);
1991 #else
1993 if (data_root) {
1994 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1995 "Claws", G_DIR_SEPARATOR_S,
1996 g_get_user_name(), G_DIR_SEPARATOR_S,
1997 IMAP_CACHE_DIR, NULL);
1998 g_free(last_data_root);
1999 last_data_root = g_strdup(last_data_root);
2000 } else {
2001 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2002 IMAP_CACHE_DIR, NULL);
2003 g_free(last_data_root);
2004 last_data_root = NULL;
2007 #endif
2009 return imap_cache_dir;
2012 const gchar *get_mbox_cache_dir(void)
2014 static gchar *mbox_cache_dir = NULL;
2015 #ifdef MAEMO
2016 const gchar *data_root = prefs_common_get_data_root();
2017 if (strcmp2(data_root, last_data_root)) {
2018 g_free(mbox_cache_dir);
2019 mbox_cache_dir = NULL;
2021 #endif
2022 if (!mbox_cache_dir)
2023 #ifndef MAEMO
2024 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2025 MBOX_CACHE_DIR, NULL);
2026 #else
2028 if (data_root) {
2029 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2030 "Claws", G_DIR_SEPARATOR_S,
2031 g_get_user_name(), G_DIR_SEPARATOR_S,
2032 MBOX_CACHE_DIR, NULL);
2033 g_free(last_data_root);
2034 last_data_root = g_strdup(last_data_root);
2035 } else {
2036 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2037 MBOX_CACHE_DIR, NULL);
2038 g_free(last_data_root);
2039 last_data_root = NULL;
2042 #endif
2044 return mbox_cache_dir;
2047 const gchar *get_mime_tmp_dir(void)
2049 static gchar *mime_tmp_dir = NULL;
2051 if (!mime_tmp_dir)
2052 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2053 MIME_TMP_DIR, NULL);
2055 return mime_tmp_dir;
2058 const gchar *get_template_dir(void)
2060 static gchar *template_dir = NULL;
2062 if (!template_dir)
2063 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2064 TEMPLATE_DIR, NULL);
2066 return template_dir;
2069 const gchar *get_header_cache_dir(void)
2071 static gchar *header_dir = NULL;
2073 if (!header_dir)
2074 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2075 HEADER_CACHE_DIR, NULL);
2077 return header_dir;
2080 /* Return the default directory for Plugins. */
2081 const gchar *get_plugin_dir(void)
2083 #ifdef G_OS_WIN32
2084 static gchar *plugin_dir = NULL;
2086 if (!plugin_dir)
2087 plugin_dir = g_strconcat(w32_get_module_dir(),
2088 "\\lib\\claws-mail\\plugins\\",
2089 NULL);
2090 return plugin_dir;
2091 #else
2092 if (is_dir_exist(PLUGINDIR))
2093 return PLUGINDIR;
2094 else {
2095 static gchar *plugin_dir = NULL;
2096 if (!plugin_dir)
2097 plugin_dir = g_strconcat(get_rc_dir(),
2098 G_DIR_SEPARATOR_S, "plugins",
2099 G_DIR_SEPARATOR_S, NULL);
2100 return plugin_dir;
2102 #endif
2105 const gchar *get_tmp_dir(void)
2107 static gchar *tmp_dir = NULL;
2109 if (!tmp_dir)
2110 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2111 TMP_DIR, NULL);
2113 return tmp_dir;
2116 gchar *get_tmp_file(void)
2118 gchar *tmp_file;
2119 static guint32 id = 0;
2121 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2122 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2124 return tmp_file;
2127 const gchar *get_domain_name(void)
2129 #ifdef G_OS_UNIX
2130 static gchar *domain_name = NULL;
2132 if (!domain_name) {
2133 struct hostent *hp;
2134 char hostname[256];
2136 if (gethostname(hostname, sizeof(hostname)) != 0) {
2137 perror("gethostname");
2138 domain_name = "unknown";
2139 } else {
2140 hostname[sizeof(hostname) - 1] = '\0';
2141 if ((hp = my_gethostbyname(hostname)) == NULL) {
2142 perror("gethostbyname");
2143 domain_name = g_strdup(hostname);
2144 } else {
2145 domain_name = g_strdup(hp->h_name);
2148 debug_print("domain name = %s\n", domain_name);
2151 return domain_name;
2152 #else
2153 return "unknown";
2154 #endif
2157 off_t get_file_size(const gchar *file)
2159 struct stat s;
2161 if (g_stat(file, &s) < 0) {
2162 FILE_OP_ERROR(file, "stat");
2163 return -1;
2166 return s.st_size;
2169 time_t get_file_mtime(const gchar *file)
2171 struct stat s;
2173 if (g_stat(file, &s) < 0) {
2174 FILE_OP_ERROR(file, "stat");
2175 return -1;
2178 return s.st_mtime;
2181 off_t get_file_size_as_crlf(const gchar *file)
2183 FILE *fp;
2184 off_t size = 0;
2185 gchar buf[BUFFSIZE];
2187 if ((fp = g_fopen(file, "rb")) == NULL) {
2188 FILE_OP_ERROR(file, "fopen");
2189 return -1;
2192 while (fgets(buf, sizeof(buf), fp) != NULL) {
2193 strretchomp(buf);
2194 size += strlen(buf) + 2;
2197 if (ferror(fp)) {
2198 FILE_OP_ERROR(file, "fgets");
2199 size = -1;
2202 fclose(fp);
2204 return size;
2207 off_t get_left_file_size(FILE *fp)
2209 glong pos;
2210 glong end;
2211 off_t size;
2213 if ((pos = ftell(fp)) < 0) {
2214 perror("ftell");
2215 return -1;
2217 if (fseek(fp, 0L, SEEK_END) < 0) {
2218 perror("fseek");
2219 return -1;
2221 if ((end = ftell(fp)) < 0) {
2222 perror("fseek");
2223 return -1;
2225 size = end - pos;
2226 if (fseek(fp, pos, SEEK_SET) < 0) {
2227 perror("fseek");
2228 return -1;
2231 return size;
2234 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2236 struct stat s;
2238 if (file == NULL)
2239 return FALSE;
2241 if (g_stat(file, &s) < 0) {
2242 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2243 return FALSE;
2246 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2247 return TRUE;
2249 return FALSE;
2253 /* Test on whether FILE is a relative file name. This is
2254 * straightforward for Unix but more complex for Windows. */
2255 gboolean is_relative_filename(const gchar *file)
2257 if (!file)
2258 return TRUE;
2259 #ifdef G_OS_WIN32
2260 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2261 return FALSE; /* Prefixed with a hostname - this can't
2262 * be a relative name. */
2264 if ( ((*file >= 'a' && *file <= 'z')
2265 || (*file >= 'A' && *file <= 'Z'))
2266 && file[1] == ':')
2267 file += 2; /* Skip drive letter. */
2269 return !(*file == '\\' || *file == '/');
2270 #else
2271 return !(*file == G_DIR_SEPARATOR);
2272 #endif
2276 gboolean is_dir_exist(const gchar *dir)
2278 if (dir == NULL)
2279 return FALSE;
2281 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2284 gboolean is_file_entry_exist(const gchar *file)
2286 if (file == NULL)
2287 return FALSE;
2289 return g_file_test(file, G_FILE_TEST_EXISTS);
2292 gboolean dirent_is_regular_file(struct dirent *d)
2294 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2295 if (d->d_type == DT_REG)
2296 return TRUE;
2297 else if (d->d_type != DT_UNKNOWN)
2298 return FALSE;
2299 #endif
2301 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2304 gboolean dirent_is_directory(struct dirent *d)
2306 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2307 if (d->d_type == DT_DIR)
2308 return TRUE;
2309 else if (d->d_type != DT_UNKNOWN)
2310 return FALSE;
2311 #endif
2313 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2316 gint change_dir(const gchar *dir)
2318 gchar *prevdir = NULL;
2320 if (debug_mode)
2321 prevdir = g_get_current_dir();
2323 if (g_chdir(dir) < 0) {
2324 FILE_OP_ERROR(dir, "chdir");
2325 if (debug_mode) g_free(prevdir);
2326 return -1;
2327 } else if (debug_mode) {
2328 gchar *cwd;
2330 cwd = g_get_current_dir();
2331 if (strcmp(prevdir, cwd) != 0)
2332 g_print("current dir: %s\n", cwd);
2333 g_free(cwd);
2334 g_free(prevdir);
2337 return 0;
2340 gint make_dir(const gchar *dir)
2342 if (g_mkdir(dir, S_IRWXU) < 0) {
2343 FILE_OP_ERROR(dir, "mkdir");
2344 return -1;
2346 if (g_chmod(dir, S_IRWXU) < 0)
2347 FILE_OP_ERROR(dir, "chmod");
2349 return 0;
2352 gint make_dir_hier(const gchar *dir)
2354 gchar *parent_dir;
2355 const gchar *p;
2357 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2358 parent_dir = g_strndup(dir, p - dir);
2359 if (*parent_dir != '\0') {
2360 if (!is_dir_exist(parent_dir)) {
2361 if (make_dir(parent_dir) < 0) {
2362 g_free(parent_dir);
2363 return -1;
2367 g_free(parent_dir);
2370 if (!is_dir_exist(dir)) {
2371 if (make_dir(dir) < 0)
2372 return -1;
2375 return 0;
2378 gint remove_all_files(const gchar *dir)
2380 GDir *dp;
2381 const gchar *dir_name;
2382 gchar *prev_dir;
2384 prev_dir = g_get_current_dir();
2386 if (g_chdir(dir) < 0) {
2387 FILE_OP_ERROR(dir, "chdir");
2388 g_free(prev_dir);
2389 return -1;
2392 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2393 g_warning("failed to open directory: %s\n", dir);
2394 g_free(prev_dir);
2395 return -1;
2398 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2399 if (g_unlink(dir_name) < 0)
2400 FILE_OP_ERROR(dir_name, "unlink");
2403 g_dir_close(dp);
2405 if (g_chdir(prev_dir) < 0) {
2406 FILE_OP_ERROR(prev_dir, "chdir");
2407 g_free(prev_dir);
2408 return -1;
2411 g_free(prev_dir);
2413 return 0;
2416 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2418 GDir *dp;
2419 const gchar *dir_name;
2420 gchar *prev_dir;
2421 gint file_no;
2423 prev_dir = g_get_current_dir();
2425 if (g_chdir(dir) < 0) {
2426 FILE_OP_ERROR(dir, "chdir");
2427 g_free(prev_dir);
2428 return -1;
2431 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2432 g_warning("failed to open directory: %s\n", dir);
2433 g_free(prev_dir);
2434 return -1;
2437 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2438 file_no = to_number(dir_name);
2439 if (file_no > 0 && first <= file_no && file_no <= last) {
2440 if (is_dir_exist(dir_name))
2441 continue;
2442 if (g_unlink(dir_name) < 0)
2443 FILE_OP_ERROR(dir_name, "unlink");
2447 g_dir_close(dp);
2449 if (g_chdir(prev_dir) < 0) {
2450 FILE_OP_ERROR(prev_dir, "chdir");
2451 g_free(prev_dir);
2452 return -1;
2455 g_free(prev_dir);
2457 return 0;
2460 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2462 GDir *dp;
2463 const gchar *dir_name;
2464 gchar *prev_dir;
2465 gint file_no;
2467 prev_dir = g_get_current_dir();
2469 if (g_chdir(dir) < 0) {
2470 FILE_OP_ERROR(dir, "chdir");
2471 g_free(prev_dir);
2472 return -1;
2475 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2476 FILE_OP_ERROR(dir, "opendir");
2477 g_free(prev_dir);
2478 return -1;
2481 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2482 file_no = to_number(dir_name);
2483 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2484 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2485 if (is_dir_exist(dir_name))
2486 continue;
2487 if (g_unlink(dir_name) < 0)
2488 FILE_OP_ERROR(dir_name, "unlink");
2492 g_dir_close(dp);
2494 if (g_chdir(prev_dir) < 0) {
2495 FILE_OP_ERROR(prev_dir, "chdir");
2496 g_free(prev_dir);
2497 return -1;
2500 g_free(prev_dir);
2502 return 0;
2505 gint remove_all_numbered_files(const gchar *dir)
2507 return remove_numbered_files(dir, 0, UINT_MAX);
2510 gint remove_expired_files(const gchar *dir, guint hours)
2512 GDir *dp;
2513 const gchar *dir_name;
2514 struct stat s;
2515 gchar *prev_dir;
2516 gint file_no;
2517 time_t mtime, now, expire_time;
2519 prev_dir = g_get_current_dir();
2521 if (g_chdir(dir) < 0) {
2522 FILE_OP_ERROR(dir, "chdir");
2523 g_free(prev_dir);
2524 return -1;
2527 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2528 g_warning("failed to open directory: %s\n", dir);
2529 g_free(prev_dir);
2530 return -1;
2533 now = time(NULL);
2534 expire_time = hours * 60 * 60;
2536 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2537 file_no = to_number(dir_name);
2538 if (file_no > 0) {
2539 if (g_stat(dir_name, &s) < 0) {
2540 FILE_OP_ERROR(dir_name, "stat");
2541 continue;
2543 if (S_ISDIR(s.st_mode))
2544 continue;
2545 mtime = MAX(s.st_mtime, s.st_atime);
2546 if (now - mtime > expire_time) {
2547 if (g_unlink(dir_name) < 0)
2548 FILE_OP_ERROR(dir_name, "unlink");
2553 g_dir_close(dp);
2555 if (g_chdir(prev_dir) < 0) {
2556 FILE_OP_ERROR(prev_dir, "chdir");
2557 g_free(prev_dir);
2558 return -1;
2561 g_free(prev_dir);
2563 return 0;
2566 gint remove_dir_recursive(const gchar *dir)
2568 struct stat s;
2569 GDir *dp;
2570 const gchar *dir_name;
2571 gchar *prev_dir;
2573 if (g_stat(dir, &s) < 0) {
2574 FILE_OP_ERROR(dir, "stat");
2575 if (ENOENT == errno) return 0;
2576 return -1;
2579 if (!S_ISDIR(s.st_mode)) {
2580 if (g_unlink(dir) < 0) {
2581 FILE_OP_ERROR(dir, "unlink");
2582 return -1;
2585 return 0;
2588 prev_dir = g_get_current_dir();
2589 /* g_print("prev_dir = %s\n", prev_dir); */
2591 if (!path_cmp(prev_dir, dir)) {
2592 g_free(prev_dir);
2593 if (g_chdir("..") < 0) {
2594 FILE_OP_ERROR(dir, "chdir");
2595 return -1;
2597 prev_dir = g_get_current_dir();
2600 if (g_chdir(dir) < 0) {
2601 FILE_OP_ERROR(dir, "chdir");
2602 g_free(prev_dir);
2603 return -1;
2606 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2607 g_warning("failed to open directory: %s\n", dir);
2608 g_chdir(prev_dir);
2609 g_free(prev_dir);
2610 return -1;
2613 /* remove all files in the directory */
2614 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2615 /* g_print("removing %s\n", dir_name); */
2617 if (is_dir_exist(dir_name)) {
2618 if (remove_dir_recursive(dir_name) < 0) {
2619 g_warning("can't remove directory\n");
2620 return -1;
2622 } else {
2623 if (g_unlink(dir_name) < 0)
2624 FILE_OP_ERROR(dir_name, "unlink");
2628 g_dir_close(dp);
2630 if (g_chdir(prev_dir) < 0) {
2631 FILE_OP_ERROR(prev_dir, "chdir");
2632 g_free(prev_dir);
2633 return -1;
2636 g_free(prev_dir);
2638 if (g_rmdir(dir) < 0) {
2639 FILE_OP_ERROR(dir, "rmdir");
2640 return -1;
2643 return 0;
2646 gint rename_force(const gchar *oldpath, const gchar *newpath)
2648 #ifndef G_OS_UNIX
2649 if (!is_file_entry_exist(oldpath)) {
2650 errno = ENOENT;
2651 return -1;
2653 if (is_file_exist(newpath)) {
2654 if (g_unlink(newpath) < 0)
2655 FILE_OP_ERROR(newpath, "unlink");
2657 #endif
2658 return g_rename(oldpath, newpath);
2662 * Append src file body to the tail of dest file.
2663 * Now keep_backup has no effects.
2665 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2667 FILE *src_fp, *dest_fp;
2668 gint n_read;
2669 gchar buf[BUFSIZ];
2671 gboolean err = FALSE;
2673 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2674 FILE_OP_ERROR(src, "fopen");
2675 return -1;
2678 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2679 FILE_OP_ERROR(dest, "fopen");
2680 fclose(src_fp);
2681 return -1;
2684 if (change_file_mode_rw(dest_fp, dest) < 0) {
2685 FILE_OP_ERROR(dest, "chmod");
2686 g_warning("can't change file mode\n");
2689 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2690 if (n_read < sizeof(buf) && ferror(src_fp))
2691 break;
2692 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2693 g_warning("writing to %s failed.\n", dest);
2694 fclose(dest_fp);
2695 fclose(src_fp);
2696 g_unlink(dest);
2697 return -1;
2701 if (ferror(src_fp)) {
2702 FILE_OP_ERROR(src, "fread");
2703 err = TRUE;
2705 fclose(src_fp);
2706 if (fclose(dest_fp) == EOF) {
2707 FILE_OP_ERROR(dest, "fclose");
2708 err = TRUE;
2711 if (err) {
2712 g_unlink(dest);
2713 return -1;
2716 return 0;
2719 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2721 FILE *src_fp, *dest_fp;
2722 gint n_read;
2723 gchar buf[BUFSIZ];
2724 gchar *dest_bak = NULL;
2725 gboolean err = FALSE;
2727 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2728 FILE_OP_ERROR(src, "fopen");
2729 return -1;
2731 if (is_file_exist(dest)) {
2732 dest_bak = g_strconcat(dest, ".bak", NULL);
2733 if (rename_force(dest, dest_bak) < 0) {
2734 FILE_OP_ERROR(dest, "rename");
2735 fclose(src_fp);
2736 g_free(dest_bak);
2737 return -1;
2741 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2742 FILE_OP_ERROR(dest, "fopen");
2743 fclose(src_fp);
2744 if (dest_bak) {
2745 if (rename_force(dest_bak, dest) < 0)
2746 FILE_OP_ERROR(dest_bak, "rename");
2747 g_free(dest_bak);
2749 return -1;
2752 if (change_file_mode_rw(dest_fp, dest) < 0) {
2753 FILE_OP_ERROR(dest, "chmod");
2754 g_warning("can't change file mode\n");
2757 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2758 if (n_read < sizeof(buf) && ferror(src_fp))
2759 break;
2760 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2761 g_warning("writing to %s failed.\n", dest);
2762 fclose(dest_fp);
2763 fclose(src_fp);
2764 g_unlink(dest);
2765 if (dest_bak) {
2766 if (rename_force(dest_bak, dest) < 0)
2767 FILE_OP_ERROR(dest_bak, "rename");
2768 g_free(dest_bak);
2770 return -1;
2774 if (ferror(src_fp)) {
2775 FILE_OP_ERROR(src, "fread");
2776 err = TRUE;
2778 fclose(src_fp);
2779 if (fclose(dest_fp) == EOF) {
2780 FILE_OP_ERROR(dest, "fclose");
2781 err = TRUE;
2784 if (err) {
2785 g_unlink(dest);
2786 if (dest_bak) {
2787 if (rename_force(dest_bak, dest) < 0)
2788 FILE_OP_ERROR(dest_bak, "rename");
2789 g_free(dest_bak);
2791 return -1;
2794 if (keep_backup == FALSE && dest_bak)
2795 g_unlink(dest_bak);
2797 g_free(dest_bak);
2799 return 0;
2802 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2804 if (overwrite == FALSE && is_file_exist(dest)) {
2805 g_warning("move_file(): file %s already exists.", dest);
2806 return -1;
2809 if (rename_force(src, dest) == 0) return 0;
2811 if (EXDEV != errno) {
2812 FILE_OP_ERROR(src, "rename");
2813 return -1;
2816 if (copy_file(src, dest, FALSE) < 0) return -1;
2818 g_unlink(src);
2820 return 0;
2823 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2825 gint n_read;
2826 gint bytes_left, to_read;
2827 gchar buf[BUFSIZ];
2829 if (fseek(fp, offset, SEEK_SET) < 0) {
2830 perror("fseek");
2831 return -1;
2834 bytes_left = length;
2835 to_read = MIN(bytes_left, sizeof(buf));
2837 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2838 if (n_read < to_read && ferror(fp))
2839 break;
2840 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2841 return -1;
2843 bytes_left -= n_read;
2844 if (bytes_left == 0)
2845 break;
2846 to_read = MIN(bytes_left, sizeof(buf));
2849 if (ferror(fp)) {
2850 perror("fread");
2851 return -1;
2854 return 0;
2857 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2859 FILE *dest_fp;
2860 gboolean err = FALSE;
2862 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2863 FILE_OP_ERROR(dest, "fopen");
2864 return -1;
2867 if (change_file_mode_rw(dest_fp, dest) < 0) {
2868 FILE_OP_ERROR(dest, "chmod");
2869 g_warning("can't change file mode\n");
2872 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2873 err = TRUE;
2875 if (!err && fclose(dest_fp) == EOF) {
2876 FILE_OP_ERROR(dest, "fclose");
2877 err = TRUE;
2880 if (err) {
2881 g_warning("writing to %s failed.\n", dest);
2882 g_unlink(dest);
2883 return -1;
2886 return 0;
2889 /* convert line endings into CRLF. If the last line doesn't end with
2890 * linebreak, add it.
2892 gchar *canonicalize_str(const gchar *str)
2894 const gchar *p;
2895 guint new_len = 0;
2896 gchar *out, *outp;
2898 for (p = str; *p != '\0'; ++p) {
2899 if (*p != '\r') {
2900 ++new_len;
2901 if (*p == '\n')
2902 ++new_len;
2905 if (p == str || *(p - 1) != '\n')
2906 new_len += 2;
2908 out = outp = g_malloc(new_len + 1);
2909 for (p = str; *p != '\0'; ++p) {
2910 if (*p != '\r') {
2911 if (*p == '\n')
2912 *outp++ = '\r';
2913 *outp++ = *p;
2916 if (p == str || *(p - 1) != '\n') {
2917 *outp++ = '\r';
2918 *outp++ = '\n';
2920 *outp = '\0';
2922 return out;
2925 gint canonicalize_file(const gchar *src, const gchar *dest)
2927 FILE *src_fp, *dest_fp;
2928 gchar buf[BUFFSIZE];
2929 gint len;
2930 gboolean err = FALSE;
2931 gboolean last_linebreak = FALSE;
2933 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2934 FILE_OP_ERROR(src, "fopen");
2935 return -1;
2938 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2939 FILE_OP_ERROR(dest, "fopen");
2940 fclose(src_fp);
2941 return -1;
2944 if (change_file_mode_rw(dest_fp, dest) < 0) {
2945 FILE_OP_ERROR(dest, "chmod");
2946 g_warning("can't change file mode\n");
2949 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2950 gint r = 0;
2952 len = strlen(buf);
2953 if (len == 0) break;
2954 last_linebreak = FALSE;
2956 if (buf[len - 1] != '\n') {
2957 last_linebreak = TRUE;
2958 r = fputs(buf, dest_fp);
2959 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2960 r = fputs(buf, dest_fp);
2961 } else {
2962 if (len > 1) {
2963 r = fwrite(buf, 1, len - 1, dest_fp);
2964 if (r != (len -1))
2965 r = EOF;
2967 if (r != EOF)
2968 r = fputs("\r\n", dest_fp);
2971 if (r == EOF) {
2972 g_warning("writing to %s failed.\n", dest);
2973 fclose(dest_fp);
2974 fclose(src_fp);
2975 g_unlink(dest);
2976 return -1;
2980 if (last_linebreak == TRUE) {
2981 if (fputs("\r\n", dest_fp) == EOF)
2982 err = TRUE;
2985 if (ferror(src_fp)) {
2986 FILE_OP_ERROR(src, "fgets");
2987 err = TRUE;
2989 fclose(src_fp);
2990 if (fclose(dest_fp) == EOF) {
2991 FILE_OP_ERROR(dest, "fclose");
2992 err = TRUE;
2995 if (err) {
2996 g_unlink(dest);
2997 return -1;
3000 return 0;
3003 gint canonicalize_file_replace(const gchar *file)
3005 gchar *tmp_file;
3007 tmp_file = get_tmp_file();
3009 if (canonicalize_file(file, tmp_file) < 0) {
3010 g_free(tmp_file);
3011 return -1;
3014 if (move_file(tmp_file, file, TRUE) < 0) {
3015 g_warning("can't replace %s .\n", file);
3016 g_unlink(tmp_file);
3017 g_free(tmp_file);
3018 return -1;
3021 g_free(tmp_file);
3022 return 0;
3025 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3027 FILE *src_fp, *dest_fp;
3028 gchar buf[BUFFSIZE];
3029 gboolean err = FALSE;
3031 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3032 FILE_OP_ERROR(src, "fopen");
3033 return -1;
3036 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3037 FILE_OP_ERROR(dest, "fopen");
3038 fclose(src_fp);
3039 return -1;
3042 if (change_file_mode_rw(dest_fp, dest) < 0) {
3043 FILE_OP_ERROR(dest, "chmod");
3044 g_warning("can't change file mode\n");
3047 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3048 strcrchomp(buf);
3049 if (fputs(buf, dest_fp) == EOF) {
3050 g_warning("writing to %s failed.\n", dest);
3051 fclose(dest_fp);
3052 fclose(src_fp);
3053 g_unlink(dest);
3054 return -1;
3058 if (ferror(src_fp)) {
3059 FILE_OP_ERROR(src, "fgets");
3060 err = TRUE;
3062 fclose(src_fp);
3063 if (fclose(dest_fp) == EOF) {
3064 FILE_OP_ERROR(dest, "fclose");
3065 err = TRUE;
3068 if (err) {
3069 g_unlink(dest);
3070 return -1;
3073 return 0;
3076 gint uncanonicalize_file_replace(const gchar *file)
3078 gchar *tmp_file;
3080 tmp_file = get_tmp_file();
3082 if (uncanonicalize_file(file, tmp_file) < 0) {
3083 g_free(tmp_file);
3084 return -1;
3087 if (move_file(tmp_file, file, TRUE) < 0) {
3088 g_warning("can't replace %s .\n", file);
3089 g_unlink(tmp_file);
3090 g_free(tmp_file);
3091 return -1;
3094 g_free(tmp_file);
3095 return 0;
3098 gchar *normalize_newlines(const gchar *str)
3100 const gchar *p = str;
3101 gchar *out, *outp;
3103 out = outp = g_malloc(strlen(str) + 1);
3104 for (p = str; *p != '\0'; ++p) {
3105 if (*p == '\r') {
3106 if (*(p + 1) != '\n')
3107 *outp++ = '\n';
3108 } else
3109 *outp++ = *p;
3112 *outp = '\0';
3114 return out;
3117 gchar *get_outgoing_rfc2822_str(FILE *fp)
3119 gchar buf[BUFFSIZE];
3120 GString *str;
3121 gchar *ret;
3123 str = g_string_new(NULL);
3125 /* output header part */
3126 while (fgets(buf, sizeof(buf), fp) != NULL) {
3127 strretchomp(buf);
3128 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3129 gint next;
3131 for (;;) {
3132 next = fgetc(fp);
3133 if (next == EOF)
3134 break;
3135 else if (next != ' ' && next != '\t') {
3136 ungetc(next, fp);
3137 break;
3139 if (fgets(buf, sizeof(buf), fp) == NULL)
3140 break;
3142 } else {
3143 g_string_append(str, buf);
3144 g_string_append(str, "\r\n");
3145 if (buf[0] == '\0')
3146 break;
3150 /* output body part */
3151 while (fgets(buf, sizeof(buf), fp) != NULL) {
3152 strretchomp(buf);
3153 if (buf[0] == '.')
3154 g_string_append_c(str, '.');
3155 g_string_append(str, buf);
3156 g_string_append(str, "\r\n");
3159 ret = str->str;
3160 g_string_free(str, FALSE);
3162 return ret;
3166 * Create a new boundary in a way that it is very unlikely that this
3167 * will occur in the following text. It would be easy to ensure
3168 * uniqueness if everything is either quoted-printable or base64
3169 * encoded (note that conversion is allowed), but because MIME bodies
3170 * may be nested, it may happen that the same boundary has already
3171 * been used.
3173 * boundary := 0*69<bchars> bcharsnospace
3174 * bchars := bcharsnospace / " "
3175 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3176 * "+" / "_" / "," / "-" / "." /
3177 * "/" / ":" / "=" / "?"
3179 * some special characters removed because of buggy MTAs
3182 gchar *generate_mime_boundary(const gchar *prefix)
3184 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3185 "abcdefghijklmnopqrstuvwxyz"
3186 "1234567890+_./=";
3187 gchar buf_uniq[24];
3188 gint i;
3190 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3191 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3192 buf_uniq[i] = '\0';
3194 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3195 buf_uniq);
3198 gint change_file_mode_rw(FILE *fp, const gchar *file)
3200 #if HAVE_FCHMOD
3201 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3202 #else
3203 return g_chmod(file, S_IRUSR|S_IWUSR);
3204 #endif
3207 FILE *my_tmpfile(void)
3209 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3210 const gchar suffix[] = ".XXXXXX";
3211 const gchar *tmpdir;
3212 guint tmplen;
3213 const gchar *progname;
3214 guint proglen;
3215 gchar *fname;
3216 gint fd;
3217 FILE *fp;
3218 gchar buf[2]="\0";
3220 tmpdir = get_tmp_dir();
3221 tmplen = strlen(tmpdir);
3222 progname = g_get_prgname();
3223 if (progname == NULL)
3224 progname = "claws-mail";
3225 proglen = strlen(progname);
3226 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3227 return tmpfile());
3229 memcpy(fname, tmpdir, tmplen);
3230 fname[tmplen] = G_DIR_SEPARATOR;
3231 memcpy(fname + tmplen + 1, progname, proglen);
3232 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3234 fd = mkstemp(fname);
3235 if (fd < 0)
3236 return tmpfile();
3238 #ifndef G_OS_WIN32
3239 g_unlink(fname);
3241 /* verify that we can write in the file after unlinking */
3242 if (write(fd, buf, 1) < 0) {
3243 close(fd);
3244 return tmpfile();
3247 #endif
3249 fp = fdopen(fd, "w+b");
3250 if (!fp)
3251 close(fd);
3252 else {
3253 rewind(fp);
3254 return fp;
3257 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3259 return tmpfile();
3262 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3264 int fd;
3265 #ifdef G_OS_WIN32
3266 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3267 dir, G_DIR_SEPARATOR);
3268 fd = mkstemp_name(template, filename);
3269 g_free(template);
3270 #else
3271 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3272 fd = mkstemp(*filename);
3273 #endif
3274 return fdopen(fd, "w+");
3277 FILE *str_open_as_stream(const gchar *str)
3279 FILE *fp;
3280 size_t len;
3282 g_return_val_if_fail(str != NULL, NULL);
3284 fp = my_tmpfile();
3285 if (!fp) {
3286 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3287 return NULL;
3290 len = strlen(str);
3291 if (len == 0) return fp;
3293 if (fwrite(str, 1, len, fp) != len) {
3294 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3295 fclose(fp);
3296 return NULL;
3299 rewind(fp);
3300 return fp;
3303 gint str_write_to_file(const gchar *str, const gchar *file)
3305 FILE *fp;
3306 size_t len;
3308 g_return_val_if_fail(str != NULL, -1);
3309 g_return_val_if_fail(file != NULL, -1);
3311 if ((fp = g_fopen(file, "wb")) == NULL) {
3312 FILE_OP_ERROR(file, "fopen");
3313 return -1;
3316 len = strlen(str);
3317 if (len == 0) {
3318 fclose(fp);
3319 return 0;
3322 if (fwrite(str, 1, len, fp) != len) {
3323 FILE_OP_ERROR(file, "fwrite");
3324 fclose(fp);
3325 g_unlink(file);
3326 return -1;
3329 if (fclose(fp) == EOF) {
3330 FILE_OP_ERROR(file, "fclose");
3331 g_unlink(file);
3332 return -1;
3335 return 0;
3338 gchar *file_read_to_str(const gchar *file)
3340 FILE *fp;
3341 gchar *str;
3343 g_return_val_if_fail(file != NULL, NULL);
3345 if ((fp = g_fopen(file, "rb")) == NULL) {
3346 FILE_OP_ERROR(file, "fopen");
3347 return NULL;
3350 str = file_read_stream_to_str(fp);
3352 fclose(fp);
3354 return str;
3357 gchar *file_read_stream_to_str(FILE *fp)
3359 GByteArray *array;
3360 guchar buf[BUFSIZ];
3361 gint n_read;
3362 gchar *str;
3364 g_return_val_if_fail(fp != NULL, NULL);
3366 array = g_byte_array_new();
3368 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3369 if (n_read < sizeof(buf) && ferror(fp))
3370 break;
3371 g_byte_array_append(array, buf, n_read);
3374 if (ferror(fp)) {
3375 FILE_OP_ERROR("file stream", "fread");
3376 g_byte_array_free(array, TRUE);
3377 return NULL;
3380 buf[0] = '\0';
3381 g_byte_array_append(array, buf, 1);
3382 str = (gchar *)array->data;
3383 g_byte_array_free(array, FALSE);
3385 if (!g_utf8_validate(str, -1, NULL)) {
3386 const gchar *src_codeset, *dest_codeset;
3387 gchar *tmp = NULL;
3388 src_codeset = conv_get_locale_charset_str();
3389 dest_codeset = CS_UTF_8;
3390 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3391 g_free(str);
3392 str = tmp;
3395 return str;
3399 char *fgets_crlf(char *buf, int size, FILE *stream)
3401 gboolean is_cr = FALSE;
3402 gboolean last_was_cr = FALSE;
3403 int c = 0;
3404 char *cs;
3406 cs = buf;
3407 while (--size > 0 && (c = getc(stream)) != EOF)
3409 *cs++ = c;
3410 is_cr = (c == '\r');
3411 if (c == '\n') {
3412 break;
3414 if (last_was_cr) {
3415 *(--cs) = '\n';
3416 cs++;
3417 ungetc(c, stream);
3418 break;
3420 last_was_cr = is_cr;
3422 if (c == EOF && cs == buf)
3423 return NULL;
3425 *cs = '\0';
3427 return buf;
3430 static gint execute_async(gchar *const argv[])
3432 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3434 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3435 NULL, NULL, NULL, FALSE) == FALSE) {
3436 g_warning("Couldn't execute command: %s\n", argv[0]);
3437 return -1;
3440 return 0;
3443 static gint execute_sync(gchar *const argv[])
3445 gint status;
3447 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3449 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3450 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3451 g_warning("Couldn't execute command: %s\n", argv[0]);
3452 return -1;
3455 #ifdef G_OS_UNIX
3456 if (WIFEXITED(status))
3457 return WEXITSTATUS(status);
3458 else
3459 return -1;
3460 #else
3461 return status;
3462 #endif
3465 gint execute_command_line(const gchar *cmdline, gboolean async)
3467 gchar **argv;
3468 gint ret;
3470 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3472 argv = strsplit_with_quote(cmdline, " ", 0);
3474 if (async)
3475 ret = execute_async(argv);
3476 else
3477 ret = execute_sync(argv);
3479 g_strfreev(argv);
3481 return ret;
3484 gchar *get_command_output(const gchar *cmdline)
3486 gchar *child_stdout;
3487 gint status;
3489 g_return_val_if_fail(cmdline != NULL, NULL);
3491 debug_print("get_command_output(): executing: %s\n", cmdline);
3493 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3494 NULL) == FALSE) {
3495 g_warning("Couldn't execute command: %s\n", cmdline);
3496 return NULL;
3499 return child_stdout;
3502 static gint is_unchanged_uri_char(char c)
3504 switch (c) {
3505 case '(':
3506 case ')':
3507 return 0;
3508 default:
3509 return 1;
3513 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3515 int i;
3516 int k;
3518 k = 0;
3519 for(i = 0; i < strlen(uri) ; i++) {
3520 if (is_unchanged_uri_char(uri[i])) {
3521 if (k + 2 >= bufsize)
3522 break;
3523 encoded_uri[k++] = uri[i];
3525 else {
3526 char * hexa = "0123456789ABCDEF";
3528 if (k + 4 >= bufsize)
3529 break;
3530 encoded_uri[k++] = '%';
3531 encoded_uri[k++] = hexa[uri[i] / 16];
3532 encoded_uri[k++] = hexa[uri[i] % 16];
3535 encoded_uri[k] = 0;
3538 gint open_uri(const gchar *uri, const gchar *cmdline)
3540 #ifndef MAEMO
3541 gchar buf[BUFFSIZE];
3542 gchar *p;
3543 gchar encoded_uri[BUFFSIZE];
3544 g_return_val_if_fail(uri != NULL, -1);
3546 /* an option to choose whether to use encode_uri or not ? */
3547 encode_uri(encoded_uri, BUFFSIZE, uri);
3549 if (cmdline &&
3550 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3551 !strchr(p + 2, '%'))
3552 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3553 else {
3554 if (cmdline)
3555 g_warning("Open URI command line is invalid "
3556 "(there must be only one '%%s'): %s",
3557 cmdline);
3558 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3561 execute_command_line(buf, TRUE);
3562 #else
3563 extern osso_context_t *get_osso_context(void);
3564 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3565 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3566 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3567 #endif
3568 return 0;
3571 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3573 gchar buf[BUFFSIZE];
3574 gchar *p;
3576 g_return_val_if_fail(filepath != NULL, -1);
3578 if (cmdline &&
3579 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3580 !strchr(p + 2, '%'))
3581 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3582 else {
3583 if (cmdline)
3584 g_warning("Open Text Editor command line is invalid "
3585 "(there must be only one '%%s'): %s",
3586 cmdline);
3587 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3590 execute_command_line(buf, TRUE);
3592 return 0;
3595 time_t remote_tzoffset_sec(const gchar *zone)
3597 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3598 gchar zone3[4];
3599 gchar *p;
3600 gchar c;
3601 gint iustz;
3602 gint offset;
3603 time_t remoteoffset;
3605 strncpy(zone3, zone, 3);
3606 zone3[3] = '\0';
3607 remoteoffset = 0;
3609 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3610 (c == '+' || c == '-')) {
3611 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3612 if (c == '-')
3613 remoteoffset = -remoteoffset;
3614 } else if (!strncmp(zone, "UT" , 2) ||
3615 !strncmp(zone, "GMT", 2)) {
3616 remoteoffset = 0;
3617 } else if (strlen(zone3) == 3) {
3618 for (p = ustzstr; *p != '\0'; p += 3) {
3619 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3620 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3621 remoteoffset = iustz * 3600;
3622 break;
3625 if (*p == '\0')
3626 return -1;
3627 } else if (strlen(zone3) == 1) {
3628 switch (zone[0]) {
3629 case 'Z': remoteoffset = 0; break;
3630 case 'A': remoteoffset = -1; break;
3631 case 'B': remoteoffset = -2; break;
3632 case 'C': remoteoffset = -3; break;
3633 case 'D': remoteoffset = -4; break;
3634 case 'E': remoteoffset = -5; break;
3635 case 'F': remoteoffset = -6; break;
3636 case 'G': remoteoffset = -7; break;
3637 case 'H': remoteoffset = -8; break;
3638 case 'I': remoteoffset = -9; break;
3639 case 'K': remoteoffset = -10; break; /* J is not used */
3640 case 'L': remoteoffset = -11; break;
3641 case 'M': remoteoffset = -12; break;
3642 case 'N': remoteoffset = 1; break;
3643 case 'O': remoteoffset = 2; break;
3644 case 'P': remoteoffset = 3; break;
3645 case 'Q': remoteoffset = 4; break;
3646 case 'R': remoteoffset = 5; break;
3647 case 'S': remoteoffset = 6; break;
3648 case 'T': remoteoffset = 7; break;
3649 case 'U': remoteoffset = 8; break;
3650 case 'V': remoteoffset = 9; break;
3651 case 'W': remoteoffset = 10; break;
3652 case 'X': remoteoffset = 11; break;
3653 case 'Y': remoteoffset = 12; break;
3654 default: remoteoffset = 0; break;
3656 remoteoffset = remoteoffset * 3600;
3657 } else
3658 return -1;
3660 return remoteoffset;
3663 time_t tzoffset_sec(time_t *now)
3665 struct tm gmt, *lt;
3666 gint off;
3667 struct tm buf1, buf2;
3669 gmt = *gmtime_r(now, &buf1);
3670 lt = localtime_r(now, &buf2);
3672 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3674 if (lt->tm_year < gmt.tm_year)
3675 off -= 24 * 60;
3676 else if (lt->tm_year > gmt.tm_year)
3677 off += 24 * 60;
3678 else if (lt->tm_yday < gmt.tm_yday)
3679 off -= 24 * 60;
3680 else if (lt->tm_yday > gmt.tm_yday)
3681 off += 24 * 60;
3683 if (off >= 24 * 60) /* should be impossible */
3684 off = 23 * 60 + 59; /* if not, insert silly value */
3685 if (off <= -24 * 60)
3686 off = -(23 * 60 + 59);
3688 return off * 60;
3691 /* calculate timezone offset */
3692 gchar *tzoffset(time_t *now)
3694 static gchar offset_string[6];
3695 struct tm gmt, *lt;
3696 gint off;
3697 gchar sign = '+';
3698 struct tm buf1, buf2;
3700 gmt = *gmtime_r(now, &buf1);
3701 lt = localtime_r(now, &buf2);
3703 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3705 if (lt->tm_year < gmt.tm_year)
3706 off -= 24 * 60;
3707 else if (lt->tm_year > gmt.tm_year)
3708 off += 24 * 60;
3709 else if (lt->tm_yday < gmt.tm_yday)
3710 off -= 24 * 60;
3711 else if (lt->tm_yday > gmt.tm_yday)
3712 off += 24 * 60;
3714 if (off < 0) {
3715 sign = '-';
3716 off = -off;
3719 if (off >= 24 * 60) /* should be impossible */
3720 off = 23 * 60 + 59; /* if not, insert silly value */
3722 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3724 return offset_string;
3727 void get_rfc822_date(gchar *buf, gint len)
3729 struct tm *lt;
3730 time_t t;
3731 gchar day[4], mon[4];
3732 gint dd, hh, mm, ss, yyyy;
3733 struct tm buf1;
3734 gchar buf2[BUFFSIZE];
3736 t = time(NULL);
3737 lt = localtime_r(&t, &buf1);
3739 #ifdef SOLARIS
3740 sscanf(asctime_r(lt, buf2, sizeof(buf2)), "%3s %3s %d %d:%d:%d %d\n",
3741 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3742 #else
3743 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3744 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3745 #endif
3746 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3747 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3750 /* just a wrapper to suppress the warning of gcc about %c */
3751 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3752 const struct tm *tm)
3754 return strftime(s, max, format, tm);
3757 void debug_set_mode(gboolean mode)
3759 debug_mode = mode;
3762 gboolean debug_get_mode(void)
3764 return debug_mode;
3767 void debug_print_real(const gchar *format, ...)
3769 va_list args;
3770 gchar buf[BUFFSIZE];
3772 if (!debug_mode) return;
3774 va_start(args, format);
3775 g_vsnprintf(buf, sizeof(buf), format, args);
3776 va_end(args);
3778 g_print("%s", buf);
3782 const char * debug_srcname(const char *file)
3784 const char *s = strrchr (file, '/');
3785 return s? s+1:file;
3789 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3791 if (subject == NULL)
3792 subject = "";
3793 else
3794 subject += subject_get_prefix_length(subject);
3796 return g_hash_table_lookup(subject_table, subject);
3799 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3800 void * data)
3802 if (subject == NULL || *subject == 0)
3803 return;
3804 subject += subject_get_prefix_length(subject);
3805 g_hash_table_insert(subject_table, subject, data);
3808 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3810 if (subject == NULL)
3811 return;
3813 subject += subject_get_prefix_length(subject);
3814 g_hash_table_remove(subject_table, subject);
3818 *\brief Check if a string is prefixed with known (combinations)
3819 * of prefixes. The function assumes that each prefix
3820 * is terminated by zero or exactly _one_ space.
3822 *\param str String to check for a prefixes
3824 *\return int Number of chars in the prefix that should be skipped
3825 * for a "clean" subject line. If no prefix was found, 0
3826 * is returned.
3828 int subject_get_prefix_length(const gchar *subject)
3830 /*!< Array with allowable reply prefixes regexps. */
3831 static const gchar * const prefixes[] = {
3832 "Re\\:", /* "Re:" */
3833 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3834 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3835 "Aw\\:", /* "Aw:" (German) */
3836 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3837 "Res\\:", /* "Res:" (Brazilian Outlook) */
3838 "Fw\\:", /* "Fw:" Forward */
3839 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3840 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3841 "Rif\\:", /* "Rif:" (Italian Outlook) */
3842 "Sv\\:", /* "Sv" (Norwegian) */
3843 "Vs\\:", /* "Vs" (Norwegian) */
3844 "Ad\\:", /* "Ad" (Norwegian) */
3845 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3846 /* add more */
3848 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3849 int n;
3850 regmatch_t pos;
3851 static regex_t regex;
3852 static gboolean init_;
3854 if (!subject) return 0;
3855 if (!*subject) return 0;
3857 if (!init_) {
3858 GString *s = g_string_new("");
3860 for (n = 0; n < PREFIXES; n++)
3861 /* Terminate each prefix regexpression by a
3862 * "\ ?" (zero or ONE space), and OR them */
3863 g_string_append_printf(s, "(%s\\ ?)%s",
3864 prefixes[n],
3865 n < PREFIXES - 1 ?
3866 "|" : "");
3868 g_string_prepend(s, "(");
3869 g_string_append(s, ")+"); /* match at least once */
3870 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3873 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3874 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3875 if (regcomp(&regex, s->str, REG_EXTENDED | REG_ICASE)) {
3876 debug_print("Error compiling regexp %s\n", s->str);
3877 g_string_free(s, TRUE);
3878 return 0;
3879 } else {
3880 init_ = TRUE;
3881 g_string_free(s, TRUE);
3885 if (!regexec(&regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3886 return pos.rm_eo;
3887 else
3888 return 0;
3891 guint g_stricase_hash(gconstpointer gptr)
3893 guint hash_result = 0;
3894 const char *str;
3896 for (str = gptr; str && *str; str++) {
3897 hash_result += toupper(*str);
3900 return hash_result;
3903 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3905 const char *str1 = gptr1;
3906 const char *str2 = gptr2;
3908 return !strcasecmp(str1, str2);
3911 gint g_int_compare(gconstpointer a, gconstpointer b)
3913 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3916 gchar *generate_msgid(gchar *buf, gint len)
3918 struct tm *lt;
3919 time_t t;
3920 gchar *addr;
3921 struct tm buft;
3923 t = time(NULL);
3924 lt = localtime_r(&t, &buft);
3926 if (strcmp(buf, "") == 0) {
3927 addr = g_strconcat("@", get_domain_name(), NULL);
3929 else {
3930 addr = g_strconcat("@", buf, NULL);
3933 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3934 lt->tm_year + 1900, lt->tm_mon + 1,
3935 lt->tm_mday, lt->tm_hour,
3936 lt->tm_min, lt->tm_sec,
3937 (guint) rand(), addr);
3939 g_free(addr);
3940 return buf;
3944 quote_cmd_argument()
3946 return a quoted string safely usable in argument of a command.
3948 code is extracted and adapted from etPan! project -- DINH V. Hoà.
3951 gint quote_cmd_argument(gchar * result, guint size,
3952 const gchar * path)
3954 const gchar * p;
3955 gchar * result_p;
3956 guint remaining;
3958 result_p = result;
3959 remaining = size;
3961 for(p = path ; * p != '\0' ; p ++) {
3963 if (isalnum((guchar)*p) || (* p == '/')) {
3964 if (remaining > 0) {
3965 * result_p = * p;
3966 result_p ++;
3967 remaining --;
3969 else {
3970 result[size - 1] = '\0';
3971 return -1;
3974 else {
3975 if (remaining >= 2) {
3976 * result_p = '\\';
3977 result_p ++;
3978 * result_p = * p;
3979 result_p ++;
3980 remaining -= 2;
3982 else {
3983 result[size - 1] = '\0';
3984 return -1;
3988 if (remaining > 0) {
3989 * result_p = '\0';
3991 else {
3992 result[size - 1] = '\0';
3993 return -1;
3996 return 0;
3999 typedef struct
4001 GNode *parent;
4002 GNodeMapFunc func;
4003 gpointer data;
4004 } GNodeMapData;
4006 static void g_node_map_recursive(GNode *node, gpointer data)
4008 GNodeMapData *mapdata = (GNodeMapData *) data;
4009 GNode *newnode;
4010 GNodeMapData newmapdata;
4011 gpointer newdata;
4013 newdata = mapdata->func(node->data, mapdata->data);
4014 if (newdata != NULL) {
4015 newnode = g_node_new(newdata);
4016 g_node_append(mapdata->parent, newnode);
4018 newmapdata.parent = newnode;
4019 newmapdata.func = mapdata->func;
4020 newmapdata.data = mapdata->data;
4022 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4026 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4028 GNode *root;
4029 GNodeMapData mapdata;
4031 g_return_val_if_fail(node != NULL, NULL);
4032 g_return_val_if_fail(func != NULL, NULL);
4034 root = g_node_new(func(node->data, data));
4036 mapdata.parent = root;
4037 mapdata.func = func;
4038 mapdata.data = data;
4040 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4042 return root;
4045 #define HEX_TO_INT(val, hex) \
4047 gchar c = hex; \
4049 if ('0' <= c && c <= '9') { \
4050 val = c - '0'; \
4051 } else if ('a' <= c && c <= 'f') { \
4052 val = c - 'a' + 10; \
4053 } else if ('A' <= c && c <= 'F') { \
4054 val = c - 'A' + 10; \
4055 } else { \
4056 val = -1; \
4060 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4062 gint hi, lo;
4064 HEX_TO_INT(hi, c1);
4065 HEX_TO_INT(lo, c2);
4067 if (hi == -1 || lo == -1)
4068 return FALSE;
4070 *out = (hi << 4) + lo;
4071 return TRUE;
4074 #define INT_TO_HEX(hex, val) \
4076 if ((val) < 10) \
4077 hex = '0' + (val); \
4078 else \
4079 hex = 'A' + (val) - 10; \
4082 void get_hex_str(gchar *out, guchar ch)
4084 gchar hex;
4086 INT_TO_HEX(hex, ch >> 4);
4087 *out++ = hex;
4088 INT_TO_HEX(hex, ch & 0x0f);
4089 *out++ = hex;
4092 #undef REF_DEBUG
4093 #ifndef REF_DEBUG
4094 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4095 #else
4096 #define G_PRINT_REF g_print
4097 #endif
4100 *\brief Register ref counted pointer. It is based on GBoxed, so should
4101 * work with anything that uses the GType system. The semantics
4102 * are similar to a C++ auto pointer, with the exception that
4103 * C doesn't have automatic closure (calling destructors) when
4104 * exiting a block scope.
4105 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4106 * function directly.
4108 *\return GType A GType type.
4110 GType g_auto_pointer_register(void)
4112 static GType auto_pointer_type;
4113 if (!auto_pointer_type)
4114 auto_pointer_type =
4115 g_boxed_type_register_static
4116 ("G_TYPE_AUTO_POINTER",
4117 (GBoxedCopyFunc) g_auto_pointer_copy,
4118 (GBoxedFreeFunc) g_auto_pointer_free);
4119 return auto_pointer_type;
4123 *\brief Structure with g_new() allocated pointer guarded by the
4124 * auto pointer
4126 typedef struct AutoPointerRef {
4127 void (*free) (gpointer);
4128 gpointer pointer;
4129 glong cnt;
4130 } AutoPointerRef;
4133 *\brief The auto pointer opaque structure that references the
4134 * pointer guard block.
4136 typedef struct AutoPointer {
4137 AutoPointerRef *ref;
4138 gpointer ptr; /*!< access to protected pointer */
4139 } AutoPointer;
4142 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4144 *\code
4146 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4147 * ... when assigning, copying and freeing storage elements
4149 * gtk_list_store_new(N_S_COLUMNS,
4150 * G_TYPE_AUTO_POINTER,
4151 * -1);
4154 * Template *precious_data = g_new0(Template, 1);
4155 * g_pointer protect = g_auto_pointer_new(precious_data);
4157 * gtk_list_store_set(container, &iter,
4158 * S_DATA, protect,
4159 * -1);
4161 * ... the gtk_list_store has copied the pointer and
4162 * ... incremented its reference count, we should free
4163 * ... the auto pointer (in C++ a destructor would do
4164 * ... this for us when leaving block scope)
4166 * g_auto_pointer_free(protect);
4168 * ... gtk_list_store_set() now manages the data. When
4169 * ... *explicitly* requesting a pointer from the list
4170 * ... store, don't forget you get a copy that should be
4171 * ... freed with g_auto_pointer_free() eventually.
4173 *\endcode
4175 *\param pointer Pointer to be guarded.
4177 *\return GAuto * Pointer that should be used in containers with
4178 * GType support.
4180 GAuto *g_auto_pointer_new(gpointer p)
4182 AutoPointerRef *ref;
4183 AutoPointer *ptr;
4185 if (p == NULL)
4186 return NULL;
4188 ref = g_new0(AutoPointerRef, 1);
4189 ptr = g_new0(AutoPointer, 1);
4191 ref->pointer = p;
4192 ref->free = g_free;
4193 ref->cnt = 1;
4195 ptr->ref = ref;
4196 ptr->ptr = p;
4198 #ifdef REF_DEBUG
4199 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4200 #endif
4201 return ptr;
4205 *\brief Allocate an autopointer using the passed \a free function to
4206 * free the guarded pointer
4208 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4210 AutoPointer *aptr;
4212 if (p == NULL)
4213 return NULL;
4215 aptr = g_auto_pointer_new(p);
4216 aptr->ref->free = free_;
4217 return aptr;
4220 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4222 if (auto_ptr == NULL)
4223 return NULL;
4224 return ((AutoPointer *) auto_ptr)->ptr;
4228 *\brief Copies an auto pointer by. It's mostly not necessary
4229 * to call this function directly, unless you copy/assign
4230 * the guarded pointer.
4232 *\param auto_ptr Auto pointer returned by previous call to
4233 * g_auto_pointer_new_XXX()
4235 *\return gpointer An auto pointer
4237 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4239 AutoPointer *ptr;
4240 AutoPointerRef *ref;
4241 AutoPointer *newp;
4243 if (auto_ptr == NULL)
4244 return NULL;
4246 ptr = auto_ptr;
4247 ref = ptr->ref;
4248 newp = g_new0(AutoPointer, 1);
4250 newp->ref = ref;
4251 newp->ptr = ref->pointer;
4252 ++(ref->cnt);
4254 #ifdef REF_DEBUG
4255 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4256 #endif
4257 return newp;
4261 *\brief Free an auto pointer
4263 void g_auto_pointer_free(GAuto *auto_ptr)
4265 AutoPointer *ptr;
4266 AutoPointerRef *ref;
4268 if (auto_ptr == NULL)
4269 return;
4271 ptr = auto_ptr;
4272 ref = ptr->ref;
4274 if (--(ref->cnt) == 0) {
4275 #ifdef REF_DEBUG
4276 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4277 #endif
4278 ref->free(ref->pointer);
4279 g_free(ref);
4281 #ifdef REF_DEBUG
4282 else
4283 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4284 #endif
4285 g_free(ptr);
4288 void replace_returns(gchar *str)
4290 if (!str)
4291 return;
4293 while (strstr(str, "\n")) {
4294 *strstr(str, "\n") = ' ';
4296 while (strstr(str, "\r")) {
4297 *strstr(str, "\r") = ' ';
4301 /* get_uri_part() - retrieves a URI starting from scanpos.
4302 Returns TRUE if succesful */
4303 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4304 const gchar **bp, const gchar **ep, gboolean hdr)
4306 const gchar *ep_;
4307 gint parenthese_cnt = 0;
4309 g_return_val_if_fail(start != NULL, FALSE);
4310 g_return_val_if_fail(scanpos != NULL, FALSE);
4311 g_return_val_if_fail(bp != NULL, FALSE);
4312 g_return_val_if_fail(ep != NULL, FALSE);
4314 *bp = scanpos;
4316 /* find end point of URI */
4317 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4318 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4319 !IS_ASCII(*(const guchar *)ep_) ||
4320 strchr("[]{}<>\"", *ep_)) {
4321 break;
4322 } else if (strchr("(", *ep_)) {
4323 parenthese_cnt++;
4324 } else if (strchr(")", *ep_)) {
4325 if (parenthese_cnt > 0)
4326 parenthese_cnt--;
4327 else
4328 break;
4332 /* no punctuation at end of string */
4334 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4335 * should pass some URI type to this function and decide on that whether
4336 * to perform punctuation stripping */
4338 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4340 for (; ep_ - 1 > scanpos + 1 &&
4341 IS_REAL_PUNCT(*(ep_ - 1));
4342 ep_--)
4345 #undef IS_REAL_PUNCT
4347 *ep = ep_;
4349 return TRUE;
4352 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4354 while (bp && *bp && g_ascii_isspace(*bp))
4355 bp++;
4356 return g_strndup(bp, ep - bp);
4359 /* valid mail address characters */
4360 #define IS_RFC822_CHAR(ch) \
4361 (IS_ASCII(ch) && \
4362 (ch) > 32 && \
4363 (ch) != 127 && \
4364 !g_ascii_isspace(ch) && \
4365 !strchr("(),;<>\"", (ch)))
4367 /* alphabet and number within 7bit ASCII */
4368 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4369 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4371 static GHashTable *create_domain_tab(void)
4373 static const gchar *toplvl_domains [] = {
4374 "museum", "aero",
4375 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4376 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4377 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4378 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4379 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4380 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4381 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4382 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4383 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4384 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4385 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4386 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4387 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4388 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4389 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4390 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4391 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4392 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4393 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4394 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4395 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4396 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4397 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4398 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4399 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4400 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4402 gint n;
4403 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4405 g_return_val_if_fail(htab, NULL);
4406 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4407 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4408 return htab;
4411 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4413 const gint MAX_LVL_DOM_NAME_LEN = 6;
4414 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4415 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4416 register gchar *p;
4418 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4419 return FALSE;
4421 for (p = buf; p < m && first < last; *p++ = *first++)
4423 *p = 0;
4425 return g_hash_table_lookup(tab, buf) != NULL;
4428 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4429 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4430 const gchar **bp, const gchar **ep, gboolean hdr)
4432 /* more complex than the uri part because we need to scan back and forward starting from
4433 * the scan position. */
4434 gboolean result = FALSE;
4435 const gchar *bp_ = NULL;
4436 const gchar *ep_ = NULL;
4437 static GHashTable *dom_tab;
4438 const gchar *last_dot = NULL;
4439 const gchar *prelast_dot = NULL;
4440 const gchar *last_tld_char = NULL;
4442 /* the informative part of the email address (describing the name
4443 * of the email address owner) may contain quoted parts. the
4444 * closure stack stores the last encountered quotes. */
4445 gchar closure_stack[128];
4446 gchar *ptr = closure_stack;
4448 g_return_val_if_fail(start != NULL, FALSE);
4449 g_return_val_if_fail(scanpos != NULL, FALSE);
4450 g_return_val_if_fail(bp != NULL, FALSE);
4451 g_return_val_if_fail(ep != NULL, FALSE);
4453 if (hdr) {
4454 const gchar *start_quote = NULL;
4455 const gchar *end_quote = NULL;
4456 search_again:
4457 /* go to the real start */
4458 if (start[0] == ',')
4459 start++;
4460 if (start[0] == ';')
4461 start++;
4462 while (start[0] == '\n' || start[0] == '\r')
4463 start++;
4464 while (start[0] == ' ' || start[0] == '\t')
4465 start++;
4467 *bp = start;
4469 /* check if there are quotes (to skip , in them) */
4470 if (*start == '"') {
4471 start_quote = start;
4472 start++;
4473 end_quote = strstr(start, "\"");
4474 } else {
4475 start_quote = NULL;
4476 end_quote = NULL;
4479 /* skip anything between quotes */
4480 if (start_quote && end_quote) {
4481 start = end_quote;
4485 /* find end (either , or ; or end of line) */
4486 if (strstr(start, ",") && strstr(start, ";"))
4487 *ep = strstr(start,",") < strstr(start, ";")
4488 ? strstr(start, ",") : strstr(start, ";");
4489 else if (strstr(start, ","))
4490 *ep = strstr(start, ",");
4491 else if (strstr(start, ";"))
4492 *ep = strstr(start, ";");
4493 else
4494 *ep = start+strlen(start);
4496 /* go back to real start */
4497 if (start_quote && end_quote) {
4498 start = start_quote;
4501 /* check there's still an @ in that, or search
4502 * further if possible */
4503 if (strstr(start, "@") && strstr(start, "@") < *ep)
4504 return TRUE;
4505 else if (*ep < start+strlen(start)) {
4506 start = *ep;
4507 goto search_again;
4508 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4509 *bp = start_quote;
4510 return TRUE;
4511 } else
4512 return FALSE;
4515 if (!dom_tab)
4516 dom_tab = create_domain_tab();
4517 g_return_val_if_fail(dom_tab, FALSE);
4519 /* scan start of address */
4520 for (bp_ = scanpos - 1;
4521 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4524 /* TODO: should start with an alnum? */
4525 bp_++;
4526 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4529 if (bp_ != scanpos) {
4530 /* scan end of address */
4531 for (ep_ = scanpos + 1;
4532 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4533 if (*ep_ == '.') {
4534 prelast_dot = last_dot;
4535 last_dot = ep_;
4536 if (*(last_dot + 1) == '.') {
4537 if (prelast_dot == NULL)
4538 return FALSE;
4539 last_dot = prelast_dot;
4540 break;
4544 /* TODO: really should terminate with an alnum? */
4545 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4546 --ep_)
4548 ep_++;
4550 if (last_dot == NULL)
4551 return FALSE;
4552 if (last_dot >= ep_)
4553 last_dot = prelast_dot;
4554 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4555 return FALSE;
4556 last_dot++;
4558 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4559 if (*last_tld_char == '?')
4560 break;
4562 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4563 result = TRUE;
4565 *ep = ep_;
4566 *bp = bp_;
4569 if (!result) return FALSE;
4571 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4572 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4573 && IS_RFC822_CHAR(*(ep_ + 3))) {
4574 /* this informative part with an @ in it is
4575 * followed by the email address */
4576 ep_ += 3;
4578 /* go to matching '>' (or next non-rfc822 char, like \n) */
4579 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4582 /* include the bracket */
4583 if (*ep_ == '>') ep_++;
4585 /* include the leading quote */
4586 bp_--;
4588 *ep = ep_;
4589 *bp = bp_;
4590 return TRUE;
4593 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4594 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4595 return FALSE;
4597 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4598 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4599 return TRUE;
4601 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4602 #define IN_STACK() (ptr > closure_stack)
4603 /* has underrun check */
4604 #define POP_STACK() if(IN_STACK()) --ptr
4605 /* has overrun check */
4606 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4607 /* has underrun check */
4608 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4610 ep_++;
4612 /* scan for the informative part. */
4613 for (bp_ -= 2; bp_ >= start; bp_--) {
4614 /* if closure on the stack keep scanning */
4615 if (PEEK_STACK() == *bp_) {
4616 POP_STACK();
4617 continue;
4619 if (*bp_ == '\'' || *bp_ == '"') {
4620 PUSH_STACK(*bp_);
4621 continue;
4624 /* if nothing in the closure stack, do the special conditions
4625 * the following if..else expression simply checks whether
4626 * a token is acceptable. if not acceptable, the clause
4627 * should terminate the loop with a 'break' */
4628 if (!PEEK_STACK()) {
4629 if (*bp_ == '-'
4630 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4631 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4632 /* hyphens are allowed, but only in
4633 between alnums */
4634 } else if (strchr(" \"'", *bp_)) {
4635 /* but anything not being a punctiation
4636 is ok */
4637 } else {
4638 break; /* anything else is rejected */
4643 bp_++;
4645 /* scan forward (should start with an alnum) */
4646 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4648 #undef PEEK_STACK
4649 #undef PUSH_STACK
4650 #undef POP_STACK
4651 #undef IN_STACK
4652 #undef FULL_STACK
4655 *bp = bp_;
4656 *ep = ep_;
4658 return result;
4661 #undef IS_QUOTE
4662 #undef IS_ASCII_ALNUM
4663 #undef IS_RFC822_CHAR
4665 gchar *make_email_string(const gchar *bp, const gchar *ep)
4667 /* returns a mailto: URI; mailto: is also used to detect the
4668 * uri type later on in the button_pressed signal handler */
4669 gchar *tmp;
4670 gchar *result;
4672 tmp = g_strndup(bp, ep - bp);
4673 result = g_strconcat("mailto:", tmp, NULL);
4674 g_free(tmp);
4676 return result;
4679 gchar *make_http_string(const gchar *bp, const gchar *ep)
4681 /* returns an http: URI; */
4682 gchar *tmp;
4683 gchar *result;
4685 while (bp && *bp && g_ascii_isspace(*bp))
4686 bp++;
4687 tmp = g_strndup(bp, ep - bp);
4688 result = g_strconcat("http://", tmp, NULL);
4689 g_free(tmp);
4691 return result;
4694 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4696 FILE *fp = fopen(path, "rb");
4697 gchar buf[BUFFSIZE];
4698 gchar *result = NULL;
4699 if (!fp)
4700 return NULL;
4701 while (fgets(buf, sizeof (buf), fp) != NULL) {
4702 gchar **parts = g_strsplit(buf, ";", 3);
4703 gchar *trimmed = parts[0];
4704 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4705 trimmed++;
4706 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4707 trimmed[strlen(trimmed)-1] = '\0';
4709 if (!strcmp(trimmed, type)) {
4710 gboolean needsterminal = FALSE;
4711 if (parts[2] && strstr(parts[2], "needsterminal")) {
4712 needsterminal = TRUE;
4714 if (parts[2] && strstr(parts[2], "test=")) {
4715 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4716 gchar *testcmd = orig_testcmd;
4717 if (strstr(testcmd,";"))
4718 *(strstr(testcmd,";")) = '\0';
4719 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4720 testcmd++;
4721 while (testcmd[strlen(testcmd)-1] == '\n')
4722 testcmd[strlen(testcmd)-1] = '\0';
4723 while (testcmd[strlen(testcmd)-1] == '\r')
4724 testcmd[strlen(testcmd)-1] = '\0';
4725 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4726 testcmd[strlen(testcmd)-1] = '\0';
4728 if (strstr(testcmd, "%s")) {
4729 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4730 gint res = system(tmp);
4731 g_free(tmp);
4732 g_free(orig_testcmd);
4734 if (res != 0) {
4735 g_strfreev(parts);
4736 continue;
4738 } else {
4739 gint res = system(testcmd);
4740 g_free(orig_testcmd);
4742 if (res != 0) {
4743 g_strfreev(parts);
4744 continue;
4749 trimmed = parts[1];
4750 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4751 trimmed++;
4752 while (trimmed[strlen(trimmed)-1] == '\n')
4753 trimmed[strlen(trimmed)-1] = '\0';
4754 while (trimmed[strlen(trimmed)-1] == '\r')
4755 trimmed[strlen(trimmed)-1] = '\0';
4756 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4757 trimmed[strlen(trimmed)-1] = '\0';
4758 result = g_strdup(trimmed);
4759 g_strfreev(parts);
4760 fclose(fp);
4761 /* if there are no single quotes around %s, add them.
4762 * '.*%s.*' is ok, as in display 'png:%s'
4764 if (strstr(result, "%s")
4765 && !(strstr(result, "'") < strstr(result,"%s") &&
4766 strstr(strstr(result,"%s"), "'"))) {
4767 gchar *start = g_strdup(result);
4768 gchar *end = g_strdup(strstr(result, "%s")+2);
4769 gchar *tmp;
4770 *strstr(start, "%s") = '\0';
4771 tmp = g_strconcat(start,"'%s'",end, NULL);
4772 g_free(start);
4773 g_free(end);
4774 g_free(result);
4775 result = tmp;
4777 if (needsterminal) {
4778 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4779 g_free(result);
4780 result = tmp;
4782 return result;
4784 g_strfreev(parts);
4786 fclose(fp);
4787 return NULL;
4789 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4791 gchar *result = NULL;
4792 gchar *path = NULL;
4793 if (type == NULL)
4794 return NULL;
4795 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4796 result = mailcap_get_command_in_file(path, type, file_to_open);
4797 g_free(path);
4798 if (result)
4799 return result;
4800 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4801 return result;
4804 void mailcap_update_default(const gchar *type, const gchar *command)
4806 gchar *path = NULL, *outpath = NULL;
4807 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4808 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4809 FILE *fp = fopen(path, "rb");
4810 FILE *outfp = fopen(outpath, "wb");
4811 gchar buf[BUFFSIZE];
4813 if (!outfp) {
4814 g_free(path);
4815 g_free(outpath);
4816 fclose(fp);
4817 return;
4819 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4820 gchar **parts = g_strsplit(buf, ";", 3);
4821 gchar *trimmed = parts[0];
4822 while (trimmed[0] == ' ')
4823 trimmed++;
4824 while (trimmed[strlen(trimmed)-1] == ' ')
4825 trimmed[strlen(trimmed)-1] = '\0';
4827 if (!strcmp(trimmed, type)) {
4828 g_strfreev(parts);
4829 continue;
4831 else {
4832 fputs(buf, outfp);
4834 g_strfreev(parts);
4836 fprintf(outfp, "%s; %s\n", type, command);
4838 if (fp)
4839 fclose(fp);
4841 fclose(outfp);
4842 g_rename(outpath, path);
4845 gint copy_dir(const gchar *src, const gchar *dst)
4847 GDir *dir;
4848 const gchar *name;
4850 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4851 g_warning("failed to open directory: %s\n", src);
4852 return -1;
4855 if (make_dir(dst) < 0)
4856 return -1;
4858 while ((name = g_dir_read_name(dir)) != NULL) {
4859 gchar *old_file, *new_file;
4860 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4861 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4862 debug_print("copying: %s -> %s\n", old_file, new_file);
4863 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4864 gint r = copy_file(old_file, new_file, TRUE);
4865 if (r < 0) {
4866 g_dir_close(dir);
4867 return r;
4870 #ifndef G_OS_WIN32
4871 /* Windows has no symlinks. Or well, Vista seems to
4872 have something like this but the semantics might be
4873 different. Thus we don't use it under Windows. */
4874 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4875 GError *error;
4876 gint r = 0;
4877 gchar *target = g_file_read_link(old_file, &error);
4878 if (target)
4879 r = symlink(target, new_file);
4880 g_free(target);
4881 if (r < 0) {
4882 g_dir_close(dir);
4883 return r;
4886 #endif /*G_OS_WIN32*/
4887 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4888 gint r = copy_dir(old_file, new_file);
4889 if (r < 0) {
4890 g_dir_close(dir);
4891 return r;
4895 g_dir_close(dir);
4896 return 0;
4899 /* crude test to see if a file is an email. */
4900 gboolean file_is_email (const gchar *filename)
4902 FILE *fp = NULL;
4903 gchar buffer[2048];
4904 gint i = 0;
4905 gint score = 0;
4906 if (filename == NULL)
4907 return FALSE;
4908 if ((fp = g_fopen(filename, "rb")) == NULL)
4909 return FALSE;
4910 while (i < 60 && score < 3
4911 && fgets(buffer, sizeof (buffer), fp) > 0) {
4912 if (!strncmp(buffer, "From:", strlen("From:")))
4913 score++;
4914 if (!strncmp(buffer, "To:", strlen("To:")))
4915 score++;
4916 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4917 score++;
4918 i++;
4920 fclose(fp);
4921 return (score >= 3);
4924 gboolean sc_g_list_bigger(GList *list, gint max)
4926 GList *cur = list;
4927 int i = 0;
4928 while (cur && i <= max+1) {
4929 i++;
4930 cur = cur->next;
4932 return (i > max);
4935 gboolean sc_g_slist_bigger(GSList *list, gint max)
4937 GSList *cur = list;
4938 int i = 0;
4939 while (cur && i <= max+1) {
4940 i++;
4941 cur = cur->next;
4943 return (i > max);
4946 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4947 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4948 NULL, NULL, NULL, NULL, NULL, NULL};
4949 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4950 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4951 NULL, NULL, NULL, NULL, NULL, NULL};
4952 const gchar *s_am_up = NULL;
4953 const gchar *s_pm_up = NULL;
4954 const gchar *s_am_low = NULL;
4955 const gchar *s_pm_low = NULL;
4956 const gchar *def_loc_format = NULL;
4957 const gchar *date_loc_format = NULL;
4958 const gchar *time_loc_format = NULL;
4959 const gchar *time_am_pm = NULL;
4961 static gboolean time_names_init_done = FALSE;
4963 static void init_time_names(void)
4965 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4966 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4967 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4968 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4969 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4970 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4971 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4973 monthnames[0] = Q_("Complete month name for use by strftime|January");
4974 monthnames[1] = Q_("Complete month name for use by strftime|February");
4975 monthnames[2] = Q_("Complete month name for use by strftime|March");
4976 monthnames[3] = Q_("Complete month name for use by strftime|April");
4977 monthnames[4] = Q_("Complete month name for use by strftime|May");
4978 monthnames[5] = Q_("Complete month name for use by strftime|June");
4979 monthnames[6] = Q_("Complete month name for use by strftime|July");
4980 monthnames[7] = Q_("Complete month name for use by strftime|August");
4981 monthnames[8] = Q_("Complete month name for use by strftime|September");
4982 monthnames[9] = Q_("Complete month name for use by strftime|October");
4983 monthnames[10] = Q_("Complete month name for use by strftime|November");
4984 monthnames[11] = Q_("Complete month name for use by strftime|December");
4986 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4987 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4988 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4989 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4990 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4991 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4992 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4994 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4995 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4996 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4997 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4998 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4999 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
5000 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
5001 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
5002 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
5003 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
5004 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5005 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5007 s_am_up = Q_("For use by strftime (morning)|AM");
5008 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5009 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5010 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5012 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5013 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5014 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5016 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5018 time_names_init_done = TRUE;
5021 #define CHECK_SIZE() { \
5022 total_done += len; \
5023 if (total_done >= buflen) { \
5024 buf[buflen-1] = '\0'; \
5025 return 0; \
5029 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5031 gchar *curpos = buf;
5032 gint total_done = 0;
5033 gchar subbuf[64], subfmt[64];
5034 static time_t last_tzset = (time_t)0;
5036 if (!time_names_init_done)
5037 init_time_names();
5039 if (format == NULL || lt == NULL)
5040 return 0;
5042 if (last_tzset != time(NULL)) {
5043 tzset();
5044 last_tzset = time(NULL);
5046 while(*format) {
5047 if (*format == '%') {
5048 gint len = 0, tmp = 0;
5049 format++;
5050 switch(*format) {
5051 case '%':
5052 len = 1; CHECK_SIZE();
5053 *curpos = '%';
5054 break;
5055 case 'a':
5056 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5057 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5058 break;
5059 case 'A':
5060 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5061 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5062 break;
5063 case 'b':
5064 case 'h':
5065 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5066 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5067 break;
5068 case 'B':
5069 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5070 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5071 break;
5072 case 'c':
5073 fast_strftime(subbuf, 64, def_loc_format, lt);
5074 len = strlen(subbuf); CHECK_SIZE();
5075 strncpy2(curpos, subbuf, buflen - total_done);
5076 break;
5077 case 'C':
5078 total_done += 2; CHECK_SIZE();
5079 tmp = (lt->tm_year + 1900)/100;
5080 *curpos++ = '0'+(tmp / 10);
5081 *curpos++ = '0'+(tmp % 10);
5082 break;
5083 case 'd':
5084 total_done += 2; CHECK_SIZE();
5085 *curpos++ = '0'+(lt->tm_mday / 10);
5086 *curpos++ = '0'+(lt->tm_mday % 10);
5087 break;
5088 case 'D':
5089 total_done += 8; CHECK_SIZE();
5090 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5091 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5092 *curpos++ = '/';
5093 *curpos++ = '0'+(lt->tm_mday / 10);
5094 *curpos++ = '0'+(lt->tm_mday % 10);
5095 *curpos++ = '/';
5096 tmp = lt->tm_year%100;
5097 *curpos++ = '0'+(tmp / 10);
5098 *curpos++ = '0'+(tmp % 10);
5099 break;
5100 case 'e':
5101 len = 2; CHECK_SIZE();
5102 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5103 break;
5104 case 'F':
5105 len = 10; CHECK_SIZE();
5106 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5107 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5108 break;
5109 case 'H':
5110 total_done += 2; CHECK_SIZE();
5111 *curpos++ = '0'+(lt->tm_hour / 10);
5112 *curpos++ = '0'+(lt->tm_hour % 10);
5113 break;
5114 case 'I':
5115 total_done += 2; CHECK_SIZE();
5116 tmp = lt->tm_hour;
5117 if (tmp > 12)
5118 tmp -= 12;
5119 else if (tmp == 0)
5120 tmp = 12;
5121 *curpos++ = '0'+(tmp / 10);
5122 *curpos++ = '0'+(tmp % 10);
5123 break;
5124 case 'j':
5125 len = 3; CHECK_SIZE();
5126 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5127 break;
5128 case 'k':
5129 len = 2; CHECK_SIZE();
5130 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5131 break;
5132 case 'l':
5133 len = 2; CHECK_SIZE();
5134 tmp = lt->tm_hour;
5135 if (tmp > 12)
5136 tmp -= 12;
5137 else if (tmp == 0)
5138 tmp = 12;
5139 snprintf(curpos, buflen - total_done, "%2d", tmp);
5140 break;
5141 case 'm':
5142 total_done += 2; CHECK_SIZE();
5143 tmp = lt->tm_mon + 1;
5144 *curpos++ = '0'+(tmp / 10);
5145 *curpos++ = '0'+(tmp % 10);
5146 break;
5147 case 'M':
5148 total_done += 2; CHECK_SIZE();
5149 *curpos++ = '0'+(lt->tm_min / 10);
5150 *curpos++ = '0'+(lt->tm_min % 10);
5151 break;
5152 case 'n':
5153 len = 1; CHECK_SIZE();
5154 *curpos = '\n';
5155 break;
5156 case 'p':
5157 if (lt->tm_hour >= 12) {
5158 len = strlen(s_pm_up); CHECK_SIZE();
5159 snprintf(curpos, buflen-total_done, s_pm_up);
5160 } else {
5161 len = strlen(s_am_up); CHECK_SIZE();
5162 snprintf(curpos, buflen-total_done, s_am_up);
5164 break;
5165 case 'P':
5166 if (lt->tm_hour >= 12) {
5167 len = strlen(s_pm_low); CHECK_SIZE();
5168 snprintf(curpos, buflen-total_done, s_pm_low);
5169 } else {
5170 len = strlen(s_am_low); CHECK_SIZE();
5171 snprintf(curpos, buflen-total_done, s_am_low);
5173 break;
5174 case 'r':
5175 fast_strftime(subbuf, 64, time_am_pm, lt);
5176 len = strlen(subbuf); CHECK_SIZE();
5177 strncpy2(curpos, subbuf, buflen - total_done);
5178 break;
5179 case 'R':
5180 total_done += 5; CHECK_SIZE();
5181 *curpos++ = '0'+(lt->tm_hour / 10);
5182 *curpos++ = '0'+(lt->tm_hour % 10);
5183 *curpos++ = ':';
5184 *curpos++ = '0'+(lt->tm_min / 10);
5185 *curpos++ = '0'+(lt->tm_min % 10);
5186 break;
5187 case 's':
5188 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5189 len = strlen(subbuf); CHECK_SIZE();
5190 strncpy2(curpos, subbuf, buflen - total_done);
5191 break;
5192 case 'S':
5193 total_done += 2; CHECK_SIZE();
5194 *curpos++ = '0'+(lt->tm_sec / 10);
5195 *curpos++ = '0'+(lt->tm_sec % 10);
5196 break;
5197 case 't':
5198 len = 1; CHECK_SIZE();
5199 *curpos = '\t';
5200 break;
5201 case 'T':
5202 total_done += 8; CHECK_SIZE();
5203 *curpos++ = '0'+(lt->tm_hour / 10);
5204 *curpos++ = '0'+(lt->tm_hour % 10);
5205 *curpos++ = ':';
5206 *curpos++ = '0'+(lt->tm_min / 10);
5207 *curpos++ = '0'+(lt->tm_min % 10);
5208 *curpos++ = ':';
5209 *curpos++ = '0'+(lt->tm_sec / 10);
5210 *curpos++ = '0'+(lt->tm_sec % 10);
5211 break;
5212 case 'u':
5213 len = 1; CHECK_SIZE();
5214 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5215 break;
5216 case 'w':
5217 len = 1; CHECK_SIZE();
5218 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5219 break;
5220 case 'x':
5221 fast_strftime(subbuf, 64, date_loc_format, lt);
5222 len = strlen(subbuf); CHECK_SIZE();
5223 strncpy2(curpos, subbuf, buflen - total_done);
5224 break;
5225 case 'X':
5226 fast_strftime(subbuf, 64, time_loc_format, lt);
5227 len = strlen(subbuf); CHECK_SIZE();
5228 strncpy2(curpos, subbuf, buflen - total_done);
5229 break;
5230 case 'y':
5231 total_done += 2; CHECK_SIZE();
5232 tmp = lt->tm_year%100;
5233 *curpos++ = '0'+(tmp / 10);
5234 *curpos++ = '0'+(tmp % 10);
5235 break;
5236 case 'Y':
5237 len = 4; CHECK_SIZE();
5238 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5239 break;
5240 case 'G':
5241 case 'g':
5242 case 'U':
5243 case 'V':
5244 case 'W':
5245 case 'z':
5246 case 'Z':
5247 case '+':
5248 /* let these complicated ones be done with the libc */
5249 snprintf(subfmt, 64, "%%%c", *format);
5250 strftime(subbuf, 64, subfmt, lt);
5251 len = strlen(subbuf); CHECK_SIZE();
5252 strncpy2(curpos, subbuf, buflen - total_done);
5253 break;
5254 case 'E':
5255 case 'O':
5256 /* let these complicated modifiers be done with the libc */
5257 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5258 strftime(subbuf, 64, subfmt, lt);
5259 len = strlen(subbuf); CHECK_SIZE();
5260 strncpy2(curpos, subbuf, buflen - total_done);
5261 format++;
5262 break;
5263 default:
5264 g_warning("format error (%c)", *format);
5265 *curpos = '\0';
5266 return total_done;
5268 curpos += len;
5269 format++;
5270 } else {
5271 int len = 1; CHECK_SIZE();
5272 *curpos++ = *format++;
5275 *curpos++ = '\0';
5276 return total_done;