2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto & The Sylpheed-Claws 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
46 #include <sys/types.h>
48 # include <sys/wait.h>
55 #include <sys/utsname.h>
66 #include "../codeconv.h"
70 static gboolean debug_mode = FALSE;
72 static GSList *tempfiles=NULL;
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
80 if (G_WIN32_HAVE_WIDECHAR_API()) {
85 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
91 retval = _wchdir(wpath);
103 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104 if (cp_path == NULL) {
109 retval = chdir(cp_path);
122 gint g_chmod(const gchar *path, gint mode)
125 if (G_WIN32_HAVE_WIDECHAR_API()) {
130 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
136 retval = _wchmod(wpath, mode);
148 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149 if (cp_path == NULL) {
154 retval = chmod(cp_path, mode);
163 return chmod(path, mode);
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
170 gint mkstemp_name(const gchar *template, gchar **name_used)
172 static gulong count=0; /* W32-_mktemp only supports up to 27
176 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY
178 | S_IREAD | S_IWRITE));
180 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
182 perror(g_strdup_printf("cant create %s",*name_used));
188 #endif /* G_OS_WIN32 */
191 gint mkstemp(const gchar *template)
194 gint res = mkstemp_name(template, &dummyname);
198 #endif /* G_OS_WIN32 */
200 void list_free_strings(GList *list)
202 list = g_list_first(list);
204 while (list != NULL) {
210 void slist_free_strings(GSList *list)
212 while (list != NULL) {
218 GSList *slist_concat_unique (GSList *first, GSList *second)
226 } else if (second == NULL)
229 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230 if (g_slist_find(ret, tmp->data) == NULL)
231 ret = g_slist_prepend(ret, tmp->data);
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
241 void hash_free_strings(GHashTable *table)
243 g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
252 void hash_free_value_mem(GHashTable *table)
254 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
259 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
262 guint str_case_hash(gconstpointer key)
264 const gchar *p = key;
268 h = g_ascii_tolower(h);
269 for (p += 1; *p != '\0'; p++)
270 h = (h << 5) - h + g_ascii_tolower(*p);
276 void ptr_array_free_strings(GPtrArray *array)
281 g_return_if_fail(array != NULL);
283 for (i = 0; i < array->len; i++) {
284 str = g_ptr_array_index(array, i);
289 gboolean str_find(const gchar *haystack, const gchar *needle)
291 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
296 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
301 return strcmp(haystack, needle) == 0;
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
306 return g_ascii_strcasecmp(haystack, needle) == 0;
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 gchar *to_human_readable(off_t size)
339 static gchar str[10];
342 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
343 else if (size >> 10 < 1024)
344 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
345 else if (size >> 20 < 1024)
346 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
348 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
353 /* strcmp with NULL-checking */
354 gint strcmp2(const gchar *s1, const gchar *s2)
356 if (s1 == NULL || s2 == NULL)
359 return strcmp(s1, s2);
361 /* strstr with NULL-checking */
362 gchar *strstr2(const gchar *s1, const gchar *s2)
364 if (s1 == NULL || s2 == NULL)
367 return strstr(s1, s2);
370 gint path_cmp(const gchar *s1, const gchar *s2)
375 gchar *s1buf, *s2buf;
378 if (s1 == NULL || s2 == NULL) return -1;
379 if (*s1 == '\0' || *s2 == '\0') return -1;
382 s1buf = g_strdup (s1);
383 s2buf = g_strdup (s2);
384 subst_char (s1buf, '/', G_DIR_SEPARATOR);
385 subst_char (s2buf, '/', G_DIR_SEPARATOR);
388 #endif /* !G_OS_WIN32 */
393 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
394 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
396 rc = strncmp(s1, s2, MAX(len1, len2));
400 #endif /* !G_OS_WIN32 */
404 /* remove trailing return code */
405 gchar *strretchomp(gchar *str)
409 if (!*str) return str;
411 for (s = str + strlen(str) - 1;
412 s >= str && (*s == '\n' || *s == '\r');
419 /* remove trailing character */
420 gchar *strtailchomp(gchar *str, gchar tail_char)
424 if (!*str) return str;
425 if (tail_char == '\0') return str;
427 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
433 /* remove CR (carriage return) */
434 gchar *strcrchomp(gchar *str)
438 if (!*str) return str;
440 s = str + strlen(str) - 1;
441 if (*s == '\n' && s > str && *(s - 1) == '\r') {
449 void file_strip_crs(const gchar *file)
451 FILE *fp = NULL, *outfp = NULL;
453 gchar *out = get_tmp_file();
457 fp = fopen(file, "rb");
461 outfp = fopen(out, "wb");
467 while (fgets(buf, sizeof (buf), fp) != NULL) {
474 rename_force(out, file);
479 /* Similar to `strstr' but this function ignores the case of both strings. */
480 gchar *strcasestr(const gchar *haystack, const gchar *needle)
482 register size_t haystack_len, needle_len;
484 haystack_len = strlen(haystack);
485 needle_len = strlen(needle);
487 if (haystack_len < needle_len || needle_len == 0)
490 while (haystack_len >= needle_len) {
491 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
492 return (gchar *)haystack;
502 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
503 gconstpointer needle, size_t needlelen)
505 const gchar *haystack_ = (const gchar *)haystack;
506 const gchar *needle_ = (const gchar *)needle;
507 const gchar *haystack_cur = (const gchar *)haystack;
510 return memchr(haystack_, *needle_, haystacklen);
512 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
514 if (haystacklen - (haystack_cur - haystack_) < needlelen)
516 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
517 return (gpointer)haystack_cur;
525 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
526 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
528 register const gchar *s = src;
529 register gchar *d = dest;
539 int iswalnum(wint_t wc)
541 return g_ascii_isalnum((int)wc);
546 int iswspace(wint_t wc)
548 return g_ascii_isspace((int)wc);
553 wint_t towlower(wint_t wc)
555 if (wc >= L'A' && wc <= L'Z')
556 return wc + L'a' - L'A';
563 size_t wcslen(const wchar_t *s)
575 /* Copy SRC to DEST. */
576 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
584 } while (c != L'\0');
591 /* Copy no more than N wide-characters of SRC to DEST. */
592 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
602 } while (c != L'\0');
613 /* Duplicate S, returning an identical malloc'd string. */
614 wchar_t *wcsdup(const wchar_t *s)
619 new_str = g_new(wchar_t, wcslen(s) + 1);
627 /* Duplicate no more than N wide-characters of S,
628 returning an identical malloc'd string. */
629 wchar_t *wcsndup(const wchar_t *s, size_t n)
634 new_str = g_new(wchar_t, n + 1);
635 wcsncpy(new_str, s, n);
636 new_str[n] = (wchar_t)0;
643 wchar_t *strdup_mbstowcs(const gchar *s)
648 new_str = g_new(wchar_t, strlen(s) + 1);
649 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
653 new_str = g_realloc(new_str,
654 sizeof(wchar_t) * (wcslen(new_str) + 1));
661 gchar *strdup_wcstombs(const wchar_t *s)
667 len = wcslen(s) * MB_CUR_MAX + 1;
668 new_str = g_new(gchar, len);
669 if (wcstombs(new_str, s, len) < 0) {
673 new_str = g_realloc(new_str, strlen(new_str) + 1);
680 /* Compare S1 and S2, ignoring case. */
681 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
687 c1 = towlower(*s1++);
688 c2 = towlower(*s2++);
691 else if (c1 == 0 && c2 == 0)
698 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
699 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
701 register size_t haystack_len, needle_len;
703 haystack_len = wcslen(haystack);
704 needle_len = wcslen(needle);
706 if (haystack_len < needle_len || needle_len == 0)
709 while (haystack_len >= needle_len) {
710 if (!wcsncasecmp(haystack, needle, needle_len))
711 return (wchar_t *)haystack;
721 gint get_mbs_len(const gchar *s)
731 mb_len = g_utf8_skip[*(guchar *)p];
743 /* Examine if next block is non-ASCII string */
744 gboolean is_next_nonascii(const gchar *s)
748 /* skip head space */
749 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
751 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
752 if (*(guchar *)p > 127 || *(guchar *)p < 32)
759 gint get_next_word_len(const gchar *s)
763 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
769 /* compare subjects */
770 gint subject_compare(const gchar *s1, const gchar *s2)
774 if (!s1 || !s2) return -1;
775 if (!*s1 || !*s2) return -1;
777 Xstrdup_a(str1, s1, return -1);
778 Xstrdup_a(str2, s2, return -1);
780 trim_subject_for_compare(str1);
781 trim_subject_for_compare(str2);
783 if (!*str1 || !*str2) return -1;
785 return strcmp(str1, str2);
788 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
792 if (!s1 || !s2) return -1;
794 Xstrdup_a(str1, s1, return -1);
795 Xstrdup_a(str2, s2, return -1);
797 trim_subject_for_sort(str1);
798 trim_subject_for_sort(str2);
800 return g_utf8_collate(str1, str2);
803 void trim_subject_for_compare(gchar *str)
807 eliminate_parenthesis(str, '[', ']');
808 eliminate_parenthesis(str, '(', ')');
811 srcp = str + subject_get_prefix_length(str);
813 memmove(str, srcp, strlen(srcp) + 1);
816 void trim_subject_for_sort(gchar *str)
822 srcp = str + subject_get_prefix_length(str);
824 memmove(str, srcp, strlen(srcp) + 1);
827 void trim_subject(gchar *str)
829 register gchar *srcp;
835 srcp = str + subject_get_prefix_length(str);
840 } else if (*srcp == '(') {
852 else if (*srcp == cl)
859 while (g_ascii_isspace(*srcp)) srcp++;
860 memmove(str, srcp, strlen(srcp) + 1);
863 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
865 register gchar *srcp, *destp;
870 while ((destp = strchr(destp, op))) {
876 else if (*srcp == cl)
882 while (g_ascii_isspace(*srcp)) srcp++;
883 memmove(destp, srcp, strlen(srcp) + 1);
887 void extract_parenthesis(gchar *str, gchar op, gchar cl)
889 register gchar *srcp, *destp;
894 while ((srcp = strchr(destp, op))) {
897 memmove(destp, srcp + 1, strlen(srcp));
902 else if (*destp == cl)
914 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
917 register gchar *srcp, *destp;
919 gboolean in_quote = FALSE;
923 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
926 memmove(destp, srcp + 1, strlen(srcp));
929 if (*destp == op && !in_quote)
931 else if (*destp == cl && !in_quote)
933 else if (*destp == quote_chr)
945 void eliminate_quote(gchar *str, gchar quote_chr)
947 register gchar *srcp, *destp;
951 while ((destp = strchr(destp, quote_chr))) {
952 if ((srcp = strchr(destp + 1, quote_chr))) {
954 while (g_ascii_isspace(*srcp)) srcp++;
955 memmove(destp, srcp, strlen(srcp) + 1);
963 void extract_quote(gchar *str, gchar quote_chr)
967 if ((str = strchr(str, quote_chr))) {
969 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
970 memmove(p - 1, p, strlen(p) + 1);
975 memmove(str, str + 1, p - str);
980 void eliminate_address_comment(gchar *str)
982 register gchar *srcp, *destp;
987 while ((destp = strchr(destp, '"'))) {
988 if ((srcp = strchr(destp + 1, '"'))) {
993 while (g_ascii_isspace(*srcp)) srcp++;
994 memmove(destp, srcp, strlen(srcp) + 1);
1004 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
1010 else if (*srcp == ')')
1016 while (g_ascii_isspace(*srcp)) srcp++;
1017 memmove(destp, srcp, strlen(srcp) + 1);
1021 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1023 gboolean in_quote = FALSE;
1026 if (*str == c && !in_quote)
1027 return (gchar *)str;
1028 if (*str == quote_chr)
1036 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1038 gboolean in_quote = FALSE;
1041 p = str + strlen(str) - 1;
1043 if (*p == c && !in_quote)
1045 if (*p == quote_chr)
1053 void extract_address(gchar *str)
1055 eliminate_address_comment(str);
1056 if (strchr_with_skip_quote(str, '"', '<'))
1057 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1061 void extract_list_id_str(gchar *str)
1063 if (strchr_with_skip_quote(str, '"', '<'))
1064 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1068 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1073 if (!str) return addr_list;
1075 Xstrdup_a(work, str, return addr_list);
1078 eliminate_address_comment(work);
1081 while (workp && *workp) {
1084 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1090 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1091 extract_parenthesis_with_skip_quote
1092 (workp, '"', '<', '>');
1096 addr_list = g_slist_append(addr_list, g_strdup(workp));
1104 GSList *address_list_append(GSList *addr_list, const gchar *str)
1106 return address_list_append_real(addr_list, str, TRUE);
1109 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1111 return address_list_append_real(addr_list, str, FALSE);
1114 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1118 if (!str) return msgid_list;
1121 while (strp && *strp) {
1122 const gchar *start, *end;
1125 if ((start = strchr(strp, '<')) != NULL) {
1126 end = strchr(start + 1, '>');
1131 msgid = g_strndup(start + 1, end - start - 1);
1134 msgid_list = g_slist_prepend(msgid_list, msgid);
1144 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1148 list = references_list_prepend(NULL, str);
1149 list = g_slist_reverse(list);
1150 msgid_list = g_slist_concat(msgid_list, list);
1155 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1160 if (!str) return group_list;
1162 Xstrdup_a(work, str, return group_list);
1166 while (workp && *workp) {
1169 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1177 group_list = g_slist_append(group_list,
1186 GList *add_history(GList *list, const gchar *str)
1190 g_return_val_if_fail(str != NULL, list);
1192 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1195 list = g_list_remove(list, old->data);
1196 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1199 last = g_list_last(list);
1202 g_list_remove(list, last->data);
1206 list = g_list_prepend(list, g_strdup(str));
1211 void remove_return(gchar *str)
1213 register gchar *p = str;
1216 if (*p == '\n' || *p == '\r')
1217 memmove(p, p + 1, strlen(p));
1223 void remove_space(gchar *str)
1225 register gchar *p = str;
1230 while (g_ascii_isspace(*(p + spc)))
1233 memmove(p, p + spc, strlen(p + spc) + 1);
1239 void unfold_line(gchar *str)
1241 register gchar *p = str;
1245 if (*p == '\n' || *p == '\r') {
1248 while (g_ascii_isspace(*(p + spc)))
1251 memmove(p, p + spc, strlen(p + spc) + 1);
1257 void subst_char(gchar *str, gchar orig, gchar subst)
1259 register gchar *p = str;
1268 void subst_chars(gchar *str, gchar *orig, gchar subst)
1270 register gchar *p = str;
1273 if (strchr(orig, *p) != NULL)
1279 void subst_for_filename(gchar *str)
1284 subst_chars(str, "\t\r\n\\/*:", '_');
1286 subst_chars(str, "\t\r\n\\/*", '_');
1290 void subst_for_shellsafe_filename(gchar *str)
1294 subst_for_filename(str);
1295 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1298 gboolean is_header_line(const gchar *str)
1300 if (str[0] == ':') return FALSE;
1302 while (*str != '\0' && *str != ' ') {
1311 gboolean is_ascii_str(const gchar *str)
1313 const guchar *p = (const guchar *)str;
1315 while (*p != '\0') {
1316 if (*p != '\t' && *p != ' ' &&
1317 *p != '\r' && *p != '\n' &&
1318 (*p < 32 || *p >= 127))
1326 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1328 const gchar *first_pos;
1329 const gchar *last_pos;
1330 const gchar *p = str;
1331 gint quote_level = -1;
1333 /* speed up line processing by only searching to the last '>' */
1334 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1335 /* skip a line if it contains a '<' before the initial '>' */
1336 if (memchr(str, '<', first_pos - str) != NULL)
1338 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1342 while (p <= last_pos) {
1343 while (p < last_pos) {
1344 if (g_ascii_isspace(*p))
1350 if (strchr(quote_chars, *p))
1352 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1353 /* any characters are allowed except '-' and space */
1355 && !strchr(quote_chars, *p)
1356 && !g_ascii_isspace(*p)
1359 if (strchr(quote_chars, *p))
1371 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1373 const gchar *p = str, *q;
1374 gint cur_line = 0, len;
1376 while ((q = strchr(p, '\n')) != NULL) {
1378 if (len > max_chars) {
1388 if (len > max_chars) {
1397 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1399 gchar * position = NULL;
1400 gchar * tmp_pos = NULL;
1403 if (quote_chars == NULL)
1406 for (i = 0; i < strlen(quote_chars); i++) {
1407 tmp_pos = strchr (str, quote_chars[i]);
1409 || (tmp_pos != NULL && position >= tmp_pos) )
1415 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1417 gchar * position = NULL;
1418 gchar * tmp_pos = NULL;
1421 if (quote_chars == NULL)
1424 for (i = 0; i < strlen(quote_chars); i++) {
1425 tmp_pos = strrchr (str, quote_chars[i]);
1427 || (tmp_pos != NULL && position <= tmp_pos) )
1433 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1435 register guint haystack_len, needle_len;
1436 gboolean in_squote = FALSE, in_dquote = FALSE;
1438 haystack_len = strlen(haystack);
1439 needle_len = strlen(needle);
1441 if (haystack_len < needle_len || needle_len == 0)
1444 while (haystack_len >= needle_len) {
1445 if (!in_squote && !in_dquote &&
1446 !strncmp(haystack, needle, needle_len))
1447 return (gchar *)haystack;
1449 /* 'foo"bar"' -> foo"bar"
1450 "foo'bar'" -> foo'bar' */
1451 if (*haystack == '\'') {
1454 else if (!in_dquote)
1456 } else if (*haystack == '\"') {
1459 else if (!in_squote)
1470 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1473 gchar quote_chr = '"';
1475 gboolean in_quote = FALSE;
1479 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1483 if (*p == op && !in_quote)
1485 else if (*p == cl && !in_quote)
1487 else if (*p == quote_chr)
1500 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1503 GSList *string_list = NULL, *slist;
1505 const gchar *s_op, *s_cl;
1508 g_return_val_if_fail(str != NULL, NULL);
1511 max_tokens = G_MAXINT;
1513 s_op = strchr_with_skip_quote(str, '"', op);
1514 if (!s_op) return NULL;
1516 s_cl = strchr_parenthesis_close(str, op, cl);
1524 new_string = g_new(gchar, len + 1);
1525 strncpy(new_string, str, len);
1526 new_string[len] = 0;
1527 string_list = g_slist_prepend(string_list, new_string);
1531 while (*str && g_ascii_isspace(*str)) str++;
1533 string_list = g_slist_prepend(string_list,
1536 s_op = strchr_with_skip_quote(str, '"', op);
1537 if (!--max_tokens || !s_op) break;
1541 s_cl = strchr_parenthesis_close(str, op, cl);
1542 } while (--max_tokens && s_cl);
1545 str_array = g_new(gchar*, n);
1549 str_array[i--] = NULL;
1550 for (slist = string_list; slist; slist = slist->next)
1551 str_array[i--] = slist->data;
1553 g_slist_free(string_list);
1558 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1561 GSList *string_list = NULL, *slist;
1562 gchar **str_array, *s, *new_str;
1563 guint i, n = 1, len;
1565 g_return_val_if_fail(str != NULL, NULL);
1566 g_return_val_if_fail(delim != NULL, NULL);
1569 max_tokens = G_MAXINT;
1571 s = strstr_with_skip_quote(str, delim);
1573 guint delimiter_len = strlen(delim);
1577 new_str = g_strndup(str, len);
1579 if (new_str[0] == '\'' || new_str[0] == '\"') {
1580 if (new_str[len - 1] == new_str[0]) {
1581 new_str[len - 1] = '\0';
1582 memmove(new_str, new_str + 1, len - 1);
1585 string_list = g_slist_prepend(string_list, new_str);
1587 str = s + delimiter_len;
1588 s = strstr_with_skip_quote(str, delim);
1589 } while (--max_tokens && s);
1593 new_str = g_strdup(str);
1594 if (new_str[0] == '\'' || new_str[0] == '\"') {
1596 if (new_str[len - 1] == new_str[0]) {
1597 new_str[len - 1] = '\0';
1598 memmove(new_str, new_str + 1, len - 1);
1601 string_list = g_slist_prepend(string_list, new_str);
1605 str_array = g_new(gchar*, n);
1609 str_array[i--] = NULL;
1610 for (slist = string_list; slist; slist = slist->next)
1611 str_array[i--] = slist->data;
1613 g_slist_free(string_list);
1618 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1620 gchar *abbrev_group;
1622 const gchar *p = group;
1625 g_return_val_if_fail(group != NULL, NULL);
1627 last = group + strlen(group);
1628 abbrev_group = ap = g_malloc(strlen(group) + 1);
1633 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1635 while (*p != '.') p++;
1638 return abbrev_group;
1643 return abbrev_group;
1646 gchar *trim_string(const gchar *str, gint len)
1648 const gchar *p = str;
1653 if (!str) return NULL;
1654 if (strlen(str) <= len)
1655 return g_strdup(str);
1656 if (g_utf8_validate(str, -1, NULL) == FALSE)
1657 return g_strdup(str);
1659 while (*p != '\0') {
1660 mb_len = g_utf8_skip[*(guchar *)p];
1663 else if (new_len + mb_len > len)
1670 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1671 return g_strconcat(new_str, "...", NULL);
1674 GList *uri_list_extract_filenames(const gchar *uri_list)
1676 GList *result = NULL;
1678 gchar *escaped_utf8uri;
1684 while (g_ascii_isspace(*p)) p++;
1685 if (!strncmp(p, "file:", 5)) {
1688 while (*q && *q != '\n' && *q != '\r') q++;
1691 gchar *file, *locale_file = NULL;
1693 while (q > p && g_ascii_isspace(*q))
1695 Xalloca(escaped_utf8uri, q - p + 2,
1697 Xalloca(file, q - p + 2,
1700 strncpy(escaped_utf8uri, p, q - p + 1);
1701 escaped_utf8uri[q - p + 1] = '\0';
1702 decode_uri(file, escaped_utf8uri);
1704 * g_filename_from_uri() rejects escaped/locale encoded uri
1705 * string which come from Nautilus.
1707 if (g_utf8_validate(file, -1, NULL))
1709 = conv_codeset_strdup(
1712 conv_get_locale_charset_str());
1714 locale_file = g_strdup(file + 5);
1715 result = g_list_append(result, locale_file);
1719 p = strchr(p, '\n');
1726 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1729 static gint axtoi(const gchar *hexstr)
1731 gint hi, lo, result;
1734 if ('0' <= hi && hi <= '9') {
1737 if ('a' <= hi && hi <= 'f') {
1740 if ('A' <= hi && hi <= 'F') {
1745 if ('0' <= lo && lo <= '9') {
1748 if ('a' <= lo && lo <= 'f') {
1751 if ('A' <= lo && lo <= 'F') {
1754 result = lo + (16 * hi);
1758 gboolean is_uri_string(const gchar *str)
1760 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1761 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1762 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1763 g_ascii_strncasecmp(str, "www.", 4) == 0);
1766 gchar *get_uri_path(const gchar *uri)
1768 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1769 return (gchar *)(uri + 7);
1770 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1771 return (gchar *)(uri + 8);
1772 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1773 return (gchar *)(uri + 6);
1775 return (gchar *)uri;
1778 gint get_uri_len(const gchar *str)
1782 if (is_uri_string(str)) {
1783 for (p = str; *p != '\0'; p++) {
1784 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1793 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1794 * plusses, and escape characters are used)
1796 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1798 gchar *dec = decoded_uri;
1799 const gchar *enc = encoded_uri;
1804 if (isxdigit((guchar)enc[0]) &&
1805 isxdigit((guchar)enc[1])) {
1823 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1824 gchar **subject, gchar **body)
1829 Xstrdup_a(tmp_mailto, mailto, return -1);
1831 if (!strncmp(tmp_mailto, "mailto:", 7))
1834 p = strchr(tmp_mailto, '?');
1841 *to = g_strdup(tmp_mailto);
1844 gchar *field, *value;
1861 if (*value == '\0') continue;
1863 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1864 *cc = g_strdup(value);
1865 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1866 *bcc = g_strdup(value);
1867 } else if (subject && !*subject &&
1868 !g_ascii_strcasecmp(field, "subject")) {
1869 *subject = g_malloc(strlen(value) + 1);
1870 decode_uri(*subject, value);
1871 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1872 *body = g_malloc(strlen(value) + 1);
1873 decode_uri(*body, value);
1882 #include <windows.h>
1883 #ifndef CSIDL_APPDATA
1884 #define CSIDL_APPDATA 0x001a
1886 #ifndef CSIDL_LOCAL_APPDATA
1887 #define CSIDL_LOCAL_APPDATA 0x001c
1889 #ifndef CSIDL_FLAG_CREATE
1890 #define CSIDL_FLAG_CREATE 0x8000
1892 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1896 w32_strerror (int w32_errno)
1898 static char strerr[256];
1899 int ec = (int)GetLastError ();
1903 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1904 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1905 strerr, DIM (strerr)-1, NULL);
1909 static __inline__ void *
1910 dlopen (const char * name, int flag)
1912 void * hd = LoadLibrary (name);
1916 static __inline__ void *
1917 dlsym (void * hd, const char * sym)
1921 void * fnc = GetProcAddress (hd, sym);
1930 static __inline__ const char *
1933 return w32_strerror (0);
1937 static __inline__ int
1949 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1951 static int initialized;
1952 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1956 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1962 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1964 handle = dlopen (dllnames[i], RTLD_LAZY);
1967 func = dlsym (handle, "SHGetFolderPathA");
1978 return func (a,b,c,d,e);
1984 const gchar *get_home_dir(void)
1987 static char home_dir[MAX_PATH] = "";
1989 if (home_dir[0] == '\0')
1991 if (w32_shgetfolderpath
1992 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1993 NULL, 0, home_dir) < 0)
1994 strcpy (home_dir, "C:\\Sylpheed");
1998 return g_get_home_dir();
2002 const gchar *get_rc_dir(void)
2004 static gchar *rc_dir = NULL;
2007 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2013 const gchar *get_mail_base_dir(void)
2016 static gchar *mail_base_dir = NULL;
2019 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2022 return mail_base_dir;
2024 return get_home_dir();
2028 const gchar *get_news_cache_dir(void)
2030 static gchar *news_cache_dir = NULL;
2032 if (!news_cache_dir)
2033 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2034 NEWS_CACHE_DIR, NULL);
2036 return news_cache_dir;
2039 const gchar *get_imap_cache_dir(void)
2041 static gchar *imap_cache_dir = NULL;
2043 if (!imap_cache_dir)
2044 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2045 IMAP_CACHE_DIR, NULL);
2047 return imap_cache_dir;
2050 const gchar *get_mbox_cache_dir(void)
2052 static gchar *mbox_cache_dir = NULL;
2054 if (!mbox_cache_dir)
2055 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2056 MBOX_CACHE_DIR, NULL);
2058 return mbox_cache_dir;
2061 const gchar *get_mime_tmp_dir(void)
2063 static gchar *mime_tmp_dir = NULL;
2066 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2067 MIME_TMP_DIR, NULL);
2069 return mime_tmp_dir;
2072 const gchar *get_template_dir(void)
2074 static gchar *template_dir = NULL;
2077 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2078 TEMPLATE_DIR, NULL);
2080 return template_dir;
2083 const gchar *get_header_cache_dir(void)
2085 static gchar *header_dir = NULL;
2088 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2089 HEADER_CACHE_DIR, NULL);
2094 const gchar *get_tmp_dir(void)
2096 static gchar *tmp_dir = NULL;
2099 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2105 gchar *get_tmp_file(void)
2108 static guint32 id = 0;
2110 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2111 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2116 const gchar *get_domain_name(void)
2119 static gchar *domain_name = NULL;
2125 if (uname(&uts) < 0) {
2127 domain_name = "unknown";
2129 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2130 perror("gethostbyname");
2131 domain_name = g_strdup(uts.nodename);
2133 domain_name = g_strdup(hp->h_name);
2137 debug_print("domain name = %s\n", domain_name);
2146 off_t get_file_size(const gchar *file)
2150 if (g_stat(file, &s) < 0) {
2151 FILE_OP_ERROR(file, "stat");
2158 off_t get_file_size_as_crlf(const gchar *file)
2162 gchar buf[BUFFSIZE];
2164 if ((fp = g_fopen(file, "rb")) == NULL) {
2165 FILE_OP_ERROR(file, "fopen");
2169 while (fgets(buf, sizeof(buf), fp) != NULL) {
2171 size += strlen(buf) + 2;
2175 FILE_OP_ERROR(file, "fgets");
2184 off_t get_left_file_size(FILE *fp)
2190 if ((pos = ftell(fp)) < 0) {
2194 if (fseek(fp, 0L, SEEK_END) < 0) {
2198 if ((end = ftell(fp)) < 0) {
2203 if (fseek(fp, pos, SEEK_SET) < 0) {
2211 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2218 if (g_stat(file, &s) < 0) {
2219 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2223 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2229 gboolean is_dir_exist(const gchar *dir)
2234 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2237 gboolean is_file_entry_exist(const gchar *file)
2242 return g_file_test(file, G_FILE_TEST_EXISTS);
2245 gboolean dirent_is_regular_file(struct dirent *d)
2247 #ifdef HAVE_DIRENT_D_TYPE
2248 if (d->d_type == DT_REG)
2250 else if (d->d_type != DT_UNKNOWN)
2254 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2257 gboolean dirent_is_directory(struct dirent *d)
2259 #ifdef HAVE_DIRENT_D_TYPE
2260 if (d->d_type == DT_DIR)
2262 else if (d->d_type != DT_UNKNOWN)
2266 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2269 gint change_dir(const gchar *dir)
2271 gchar *prevdir = NULL;
2274 prevdir = g_get_current_dir();
2276 if (g_chdir(dir) < 0) {
2277 FILE_OP_ERROR(dir, "chdir");
2278 if (debug_mode) g_free(prevdir);
2280 } else if (debug_mode) {
2283 cwd = g_get_current_dir();
2284 if (strcmp(prevdir, cwd) != 0)
2285 g_print("current dir: %s\n", cwd);
2293 gint make_dir(const gchar *dir)
2295 if (g_mkdir(dir, S_IRWXU) < 0) {
2296 FILE_OP_ERROR(dir, "mkdir");
2299 if (g_chmod(dir, S_IRWXU) < 0)
2300 FILE_OP_ERROR(dir, "chmod");
2305 gint make_dir_hier(const gchar *dir)
2310 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2311 parent_dir = g_strndup(dir, p - dir);
2312 if (*parent_dir != '\0') {
2313 if (!is_dir_exist(parent_dir)) {
2314 if (make_dir(parent_dir) < 0) {
2323 if (!is_dir_exist(dir)) {
2324 if (make_dir(dir) < 0)
2331 gint remove_all_files(const gchar *dir)
2334 const gchar *dir_name;
2337 prev_dir = g_get_current_dir();
2339 if (g_chdir(dir) < 0) {
2340 FILE_OP_ERROR(dir, "chdir");
2345 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2346 g_warning("failed to open directory: %s\n", dir);
2351 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2352 if (g_unlink(dir_name) < 0)
2353 FILE_OP_ERROR(dir_name, "unlink");
2358 if (g_chdir(prev_dir) < 0) {
2359 FILE_OP_ERROR(prev_dir, "chdir");
2369 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2372 const gchar *dir_name;
2376 prev_dir = g_get_current_dir();
2378 if (g_chdir(dir) < 0) {
2379 FILE_OP_ERROR(dir, "chdir");
2384 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2385 g_warning("failed to open directory: %s\n", dir);
2390 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2391 file_no = to_number(dir_name);
2392 if (file_no > 0 && first <= file_no && file_no <= last) {
2393 if (is_dir_exist(dir_name))
2395 if (g_unlink(dir_name) < 0)
2396 FILE_OP_ERROR(dir_name, "unlink");
2402 if (g_chdir(prev_dir) < 0) {
2403 FILE_OP_ERROR(prev_dir, "chdir");
2413 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2416 const gchar *dir_name;
2420 prev_dir = g_get_current_dir();
2422 if (g_chdir(dir) < 0) {
2423 FILE_OP_ERROR(dir, "chdir");
2428 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2429 FILE_OP_ERROR(dir, "opendir");
2434 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2435 file_no = to_number(dir_name);
2436 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2437 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2438 if (is_dir_exist(dir_name))
2440 if (g_unlink(dir_name) < 0)
2441 FILE_OP_ERROR(dir_name, "unlink");
2447 if (g_chdir(prev_dir) < 0) {
2448 FILE_OP_ERROR(prev_dir, "chdir");
2458 gint remove_all_numbered_files(const gchar *dir)
2460 return remove_numbered_files(dir, 0, UINT_MAX);
2463 gint remove_expired_files(const gchar *dir, guint hours)
2466 const gchar *dir_name;
2470 time_t mtime, now, expire_time;
2472 prev_dir = g_get_current_dir();
2474 if (g_chdir(dir) < 0) {
2475 FILE_OP_ERROR(dir, "chdir");
2480 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2481 g_warning("failed to open directory: %s\n", dir);
2487 expire_time = hours * 60 * 60;
2489 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2490 file_no = to_number(dir_name);
2492 if (g_stat(dir_name, &s) < 0) {
2493 FILE_OP_ERROR(dir_name, "stat");
2496 if (S_ISDIR(s.st_mode))
2498 mtime = MAX(s.st_mtime, s.st_atime);
2499 if (now - mtime > expire_time) {
2500 if (g_unlink(dir_name) < 0)
2501 FILE_OP_ERROR(dir_name, "unlink");
2508 if (g_chdir(prev_dir) < 0) {
2509 FILE_OP_ERROR(prev_dir, "chdir");
2519 gint remove_dir_recursive(const gchar *dir)
2523 const gchar *dir_name;
2526 if (g_stat(dir, &s) < 0) {
2527 FILE_OP_ERROR(dir, "stat");
2528 if (ENOENT == errno) return 0;
2532 if (!S_ISDIR(s.st_mode)) {
2533 if (g_unlink(dir) < 0) {
2534 FILE_OP_ERROR(dir, "unlink");
2541 prev_dir = g_get_current_dir();
2542 /* g_print("prev_dir = %s\n", prev_dir); */
2544 if (!path_cmp(prev_dir, dir)) {
2546 if (g_chdir("..") < 0) {
2547 FILE_OP_ERROR(dir, "chdir");
2550 prev_dir = g_get_current_dir();
2553 if (g_chdir(dir) < 0) {
2554 FILE_OP_ERROR(dir, "chdir");
2559 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2560 g_warning("failed to open directory: %s\n", dir);
2566 /* remove all files in the directory */
2567 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2568 /* g_print("removing %s\n", dir_name); */
2570 if (is_dir_exist(dir_name)) {
2571 if (remove_dir_recursive(dir_name) < 0) {
2572 g_warning("can't remove directory\n");
2576 if (g_unlink(dir_name) < 0)
2577 FILE_OP_ERROR(dir_name, "unlink");
2583 if (g_chdir(prev_dir) < 0) {
2584 FILE_OP_ERROR(prev_dir, "chdir");
2591 if (g_rmdir(dir) < 0) {
2592 FILE_OP_ERROR(dir, "rmdir");
2599 gint rename_force(const gchar *oldpath, const gchar *newpath)
2602 if (!is_file_entry_exist(oldpath)) {
2606 if (is_file_exist(newpath)) {
2607 if (g_unlink(newpath) < 0)
2608 FILE_OP_ERROR(newpath, "unlink");
2611 return g_rename(oldpath, newpath);
2615 /* this seems to be slower than the stdio version... */
2616 gint copy_file(const gchar *src, const gchar *dest)
2618 gint src_fd, dest_fd;
2622 gchar *dest_bak = NULL;
2624 if ((src_fd = open(src, O_RDONLY)) < 0) {
2625 FILE_OP_ERROR(src, "open");
2629 if (is_file_exist(dest)) {
2630 dest_bak = g_strconcat(dest, ".bak", NULL);
2631 if (rename_force(dest, dest_bak) < 0) {
2632 FILE_OP_ERROR(dest, "rename");
2639 if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
2640 FILE_OP_ERROR(dest, "open");
2643 if (rename(dest_bak, dest) < 0)
2644 FILE_OP_ERROR(dest_bak, "rename");
2650 while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
2655 n_write = write(dest_fd, bufp, len);
2657 g_warning("writing to %s failed.\n", dest);
2662 if (rename(dest_bak, dest) < 0)
2663 FILE_OP_ERROR(dest_bak, "rename");
2676 if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
2677 g_warning("File copy from %s to %s failed.\n", src, dest);
2680 if (rename(dest_bak, dest) < 0)
2681 FILE_OP_ERROR(dest_bak, "rename");
2694 * Append src file body to the tail of dest file.
2695 * Now keep_backup has no effects.
2697 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2699 FILE *src_fp, *dest_fp;
2703 gboolean err = FALSE;
2705 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2706 FILE_OP_ERROR(src, "fopen");
2710 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2711 FILE_OP_ERROR(dest, "fopen");
2716 if (change_file_mode_rw(dest_fp, dest) < 0) {
2717 FILE_OP_ERROR(dest, "chmod");
2718 g_warning("can't change file mode\n");
2721 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2722 if (n_read < sizeof(buf) && ferror(src_fp))
2724 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2725 g_warning("writing to %s failed.\n", dest);
2733 if (ferror(src_fp)) {
2734 FILE_OP_ERROR(src, "fread");
2738 if (fclose(dest_fp) == EOF) {
2739 FILE_OP_ERROR(dest, "fclose");
2751 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2753 FILE *src_fp, *dest_fp;
2756 gchar *dest_bak = NULL;
2757 gboolean err = FALSE;
2759 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2760 FILE_OP_ERROR(src, "fopen");
2763 if (is_file_exist(dest)) {
2764 dest_bak = g_strconcat(dest, ".bak", NULL);
2765 if (rename_force(dest, dest_bak) < 0) {
2766 FILE_OP_ERROR(dest, "rename");
2773 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2774 FILE_OP_ERROR(dest, "fopen");
2777 if (rename_force(dest_bak, dest) < 0)
2778 FILE_OP_ERROR(dest_bak, "rename");
2784 if (change_file_mode_rw(dest_fp, dest) < 0) {
2785 FILE_OP_ERROR(dest, "chmod");
2786 g_warning("can't change file mode\n");
2789 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2790 if (n_read < sizeof(buf) && ferror(src_fp))
2792 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2793 g_warning("writing to %s failed.\n", dest);
2798 if (rename_force(dest_bak, dest) < 0)
2799 FILE_OP_ERROR(dest_bak, "rename");
2806 if (ferror(src_fp)) {
2807 FILE_OP_ERROR(src, "fread");
2811 if (fclose(dest_fp) == EOF) {
2812 FILE_OP_ERROR(dest, "fclose");
2819 if (rename_force(dest_bak, dest) < 0)
2820 FILE_OP_ERROR(dest_bak, "rename");
2826 if (keep_backup == FALSE && dest_bak)
2834 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2836 if (overwrite == FALSE && is_file_exist(dest)) {
2837 g_warning("move_file(): file %s already exists.", dest);
2841 if (rename_force(src, dest) == 0) return 0;
2843 if (EXDEV != errno) {
2844 FILE_OP_ERROR(src, "rename");
2848 if (copy_file(src, dest, FALSE) < 0) return -1;
2855 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2858 gint bytes_left, to_read;
2861 if (fseek(fp, offset, SEEK_SET) < 0) {
2866 bytes_left = length;
2867 to_read = MIN(bytes_left, sizeof(buf));
2869 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2870 if (n_read < to_read && ferror(fp))
2872 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2875 bytes_left -= n_read;
2876 if (bytes_left == 0)
2878 to_read = MIN(bytes_left, sizeof(buf));
2889 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2892 gboolean err = FALSE;
2894 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2895 FILE_OP_ERROR(dest, "fopen");
2899 if (change_file_mode_rw(dest_fp, dest) < 0) {
2900 FILE_OP_ERROR(dest, "chmod");
2901 g_warning("can't change file mode\n");
2904 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2907 if (!err && fclose(dest_fp) == EOF) {
2908 FILE_OP_ERROR(dest, "fclose");
2913 g_warning("writing to %s failed.\n", dest);
2921 /* convert line endings into CRLF. If the last line doesn't end with
2922 * linebreak, add it.
2924 gchar *canonicalize_str(const gchar *str)
2930 for (p = str; *p != '\0'; ++p) {
2937 if (p == str || *(p - 1) != '\n')
2940 out = outp = g_malloc(new_len + 1);
2941 for (p = str; *p != '\0'; ++p) {
2948 if (p == str || *(p - 1) != '\n') {
2957 gint canonicalize_file(const gchar *src, const gchar *dest)
2959 FILE *src_fp, *dest_fp;
2960 gchar buf[BUFFSIZE];
2962 gboolean err = FALSE;
2963 gboolean last_linebreak = FALSE;
2965 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2966 FILE_OP_ERROR(src, "fopen");
2970 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2971 FILE_OP_ERROR(dest, "fopen");
2976 if (change_file_mode_rw(dest_fp, dest) < 0) {
2977 FILE_OP_ERROR(dest, "chmod");
2978 g_warning("can't change file mode\n");
2981 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2985 if (len == 0) break;
2986 last_linebreak = FALSE;
2988 if (buf[len - 1] != '\n') {
2989 last_linebreak = TRUE;
2990 r = fputs(buf, dest_fp);
2991 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2992 r = fputs(buf, dest_fp);
2995 r = fwrite(buf, 1, len - 1, dest_fp);
3000 r = fputs("\r\n", dest_fp);
3004 g_warning("writing to %s failed.\n", dest);
3012 if (last_linebreak == TRUE) {
3013 if (fputs("\r\n", dest_fp) == EOF)
3017 if (ferror(src_fp)) {
3018 FILE_OP_ERROR(src, "fgets");
3022 if (fclose(dest_fp) == EOF) {
3023 FILE_OP_ERROR(dest, "fclose");
3035 gint canonicalize_file_replace(const gchar *file)
3039 tmp_file = get_tmp_file();
3041 if (canonicalize_file(file, tmp_file) < 0) {
3046 if (move_file(tmp_file, file, TRUE) < 0) {
3047 g_warning("can't replace %s .\n", file);
3057 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3059 FILE *src_fp, *dest_fp;
3060 gchar buf[BUFFSIZE];
3061 gboolean err = FALSE;
3063 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3064 FILE_OP_ERROR(src, "fopen");
3068 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3069 FILE_OP_ERROR(dest, "fopen");
3074 if (change_file_mode_rw(dest_fp, dest) < 0) {
3075 FILE_OP_ERROR(dest, "chmod");
3076 g_warning("can't change file mode\n");
3079 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3081 if (fputs(buf, dest_fp) == EOF) {
3082 g_warning("writing to %s failed.\n", dest);
3090 if (ferror(src_fp)) {
3091 FILE_OP_ERROR(src, "fgets");
3095 if (fclose(dest_fp) == EOF) {
3096 FILE_OP_ERROR(dest, "fclose");
3108 gint uncanonicalize_file_replace(const gchar *file)
3112 tmp_file = get_tmp_file();
3114 if (uncanonicalize_file(file, tmp_file) < 0) {
3119 if (move_file(tmp_file, file, TRUE) < 0) {
3120 g_warning("can't replace %s .\n", file);
3130 gchar *normalize_newlines(const gchar *str)
3132 const gchar *p = str;
3135 out = outp = g_malloc(strlen(str) + 1);
3136 for (p = str; *p != '\0'; ++p) {
3138 if (*(p + 1) != '\n')
3149 gchar *get_outgoing_rfc2822_str(FILE *fp)
3151 gchar buf[BUFFSIZE];
3155 str = g_string_new(NULL);
3157 /* output header part */
3158 while (fgets(buf, sizeof(buf), fp) != NULL) {
3160 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3167 else if (next != ' ' && next != '\t') {
3171 if (fgets(buf, sizeof(buf), fp) == NULL)
3175 g_string_append(str, buf);
3176 g_string_append(str, "\r\n");
3182 /* output body part */
3183 while (fgets(buf, sizeof(buf), fp) != NULL) {
3186 g_string_append_c(str, '.');
3187 g_string_append(str, buf);
3188 g_string_append(str, "\r\n");
3192 g_string_free(str, FALSE);
3198 * Create a new boundary in a way that it is very unlikely that this
3199 * will occur in the following text. It would be easy to ensure
3200 * uniqueness if everything is either quoted-printable or base64
3201 * encoded (note that conversion is allowed), but because MIME bodies
3202 * may be nested, it may happen that the same boundary has already
3205 * boundary := 0*69<bchars> bcharsnospace
3206 * bchars := bcharsnospace / " "
3207 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3208 * "+" / "_" / "," / "-" / "." /
3209 * "/" / ":" / "=" / "?"
3211 * some special characters removed because of buggy MTAs
3214 gchar *generate_mime_boundary(const gchar *prefix)
3216 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3217 "abcdefghijklmnopqrstuvwxyz"
3222 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3223 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3226 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3230 gint change_file_mode_rw(FILE *fp, const gchar *file)
3233 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3235 return g_chmod(file, S_IRUSR|S_IWUSR);
3239 FILE *my_tmpfile(void)
3241 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3242 const gchar suffix[] = ".XXXXXX";
3243 const gchar *tmpdir;
3245 const gchar *progname;
3251 tmpdir = get_tmp_dir();
3252 tmplen = strlen(tmpdir);
3253 progname = g_get_prgname();
3254 if (progname == NULL)
3255 progname = "sylpheed-claws";
3256 proglen = strlen(progname);
3257 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3260 memcpy(fname, tmpdir, tmplen);
3261 fname[tmplen] = G_DIR_SEPARATOR;
3262 memcpy(fname + tmplen + 1, progname, proglen);
3263 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3265 fd = mkstemp(fname);
3273 fp = fdopen(fd, "w+b");
3278 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3283 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3287 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3288 dir, G_DIR_SEPARATOR);
3289 fd = mkstemp_name(template, filename);
3292 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3293 fd = mkstemp(*filename);
3295 return fdopen(fd, "w+");
3298 FILE *str_open_as_stream(const gchar *str)
3303 g_return_val_if_fail(str != NULL, NULL);
3307 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3312 if (len == 0) return fp;
3314 if (fwrite(str, 1, len, fp) != len) {
3315 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3324 gint str_write_to_file(const gchar *str, const gchar *file)
3329 g_return_val_if_fail(str != NULL, -1);
3330 g_return_val_if_fail(file != NULL, -1);
3332 if ((fp = g_fopen(file, "wb")) == NULL) {
3333 FILE_OP_ERROR(file, "fopen");
3343 if (fwrite(str, 1, len, fp) != len) {
3344 FILE_OP_ERROR(file, "fwrite");
3350 if (fclose(fp) == EOF) {
3351 FILE_OP_ERROR(file, "fclose");
3359 gchar *file_read_to_str(const gchar *file)
3364 g_return_val_if_fail(file != NULL, NULL);
3366 if ((fp = g_fopen(file, "rb")) == NULL) {
3367 FILE_OP_ERROR(file, "fopen");
3371 str = file_read_stream_to_str(fp);
3378 gchar *file_read_stream_to_str(FILE *fp)
3385 g_return_val_if_fail(fp != NULL, NULL);
3387 array = g_byte_array_new();
3389 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3390 if (n_read < sizeof(buf) && ferror(fp))
3392 g_byte_array_append(array, buf, n_read);
3396 FILE_OP_ERROR("file stream", "fread");
3397 g_byte_array_free(array, TRUE);
3402 g_byte_array_append(array, buf, 1);
3403 str = (gchar *)array->data;
3404 g_byte_array_free(array, FALSE);
3406 if (!g_utf8_validate(str, -1, NULL)) {
3407 const gchar *src_codeset, *dest_codeset;
3409 src_codeset = conv_get_locale_charset_str();
3410 dest_codeset = CS_UTF_8;
3411 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3419 gint execute_async(gchar *const argv[])
3421 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3423 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3424 NULL, NULL, NULL, FALSE) == FALSE) {
3425 g_warning("Can't execute command: %s\n", argv[0]);
3432 gint execute_sync(gchar *const argv[])
3436 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3438 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3439 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3440 g_warning("Can't execute command: %s\n", argv[0]);
3445 if (WIFEXITED(status))
3446 return WEXITSTATUS(status);
3454 gint execute_command_line(const gchar *cmdline, gboolean async)
3459 debug_print("execute_command_line(): executing: %s\n", cmdline);
3461 argv = strsplit_with_quote(cmdline, " ", 0);
3464 ret = execute_async(argv);
3466 ret = execute_sync(argv);
3473 gchar *get_command_output(const gchar *cmdline)
3475 gchar *child_stdout;
3478 g_return_val_if_fail(cmdline != NULL, NULL);
3480 debug_print("get_command_output(): executing: %s\n", cmdline);
3482 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3484 g_warning("Can't execute command: %s\n", cmdline);
3488 return child_stdout;
3491 static gint is_unchanged_uri_char(char c)
3503 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3509 for(i = 0; i < strlen(uri) ; i++) {
3510 if (is_unchanged_uri_char(uri[i])) {
3511 if (k + 2 >= bufsize)
3513 encoded_uri[k++] = uri[i];
3516 char * hexa = "0123456789ABCDEF";
3518 if (k + 4 >= bufsize)
3520 encoded_uri[k++] = '%';
3521 encoded_uri[k++] = hexa[uri[i] / 16];
3522 encoded_uri[k++] = hexa[uri[i] % 16];
3528 gint open_uri(const gchar *uri, const gchar *cmdline)
3530 gchar buf[BUFFSIZE];
3532 gchar encoded_uri[BUFFSIZE];
3534 g_return_val_if_fail(uri != NULL, -1);
3536 /* an option to choose whether to use encode_uri or not ? */
3537 encode_uri(encoded_uri, BUFFSIZE, uri);
3540 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3541 !strchr(p + 2, '%'))
3542 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3545 g_warning("Open URI command line is invalid "
3546 "(there must be only one '%%s'): %s",
3548 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3551 execute_command_line(buf, TRUE);
3556 time_t remote_tzoffset_sec(const gchar *zone)
3558 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3564 time_t remoteoffset;
3566 strncpy(zone3, zone, 3);
3570 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3571 (c == '+' || c == '-')) {
3572 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3574 remoteoffset = -remoteoffset;
3575 } else if (!strncmp(zone, "UT" , 2) ||
3576 !strncmp(zone, "GMT", 2)) {
3578 } else if (strlen(zone3) == 3) {
3579 for (p = ustzstr; *p != '\0'; p += 3) {
3580 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3581 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3582 remoteoffset = iustz * 3600;
3588 } else if (strlen(zone3) == 1) {
3590 case 'Z': remoteoffset = 0; break;
3591 case 'A': remoteoffset = -1; break;
3592 case 'B': remoteoffset = -2; break;
3593 case 'C': remoteoffset = -3; break;
3594 case 'D': remoteoffset = -4; break;
3595 case 'E': remoteoffset = -5; break;
3596 case 'F': remoteoffset = -6; break;
3597 case 'G': remoteoffset = -7; break;
3598 case 'H': remoteoffset = -8; break;
3599 case 'I': remoteoffset = -9; break;
3600 case 'K': remoteoffset = -10; break; /* J is not used */
3601 case 'L': remoteoffset = -11; break;
3602 case 'M': remoteoffset = -12; break;
3603 case 'N': remoteoffset = 1; break;
3604 case 'O': remoteoffset = 2; break;
3605 case 'P': remoteoffset = 3; break;
3606 case 'Q': remoteoffset = 4; break;
3607 case 'R': remoteoffset = 5; break;
3608 case 'S': remoteoffset = 6; break;
3609 case 'T': remoteoffset = 7; break;
3610 case 'U': remoteoffset = 8; break;
3611 case 'V': remoteoffset = 9; break;
3612 case 'W': remoteoffset = 10; break;
3613 case 'X': remoteoffset = 11; break;
3614 case 'Y': remoteoffset = 12; break;
3615 default: remoteoffset = 0; break;
3617 remoteoffset = remoteoffset * 3600;
3621 return remoteoffset;
3624 time_t tzoffset_sec(time_t *now)
3630 lt = localtime(now);
3632 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3634 if (lt->tm_year < gmt.tm_year)
3636 else if (lt->tm_year > gmt.tm_year)
3638 else if (lt->tm_yday < gmt.tm_yday)
3640 else if (lt->tm_yday > gmt.tm_yday)
3643 if (off >= 24 * 60) /* should be impossible */
3644 off = 23 * 60 + 59; /* if not, insert silly value */
3645 if (off <= -24 * 60)
3646 off = -(23 * 60 + 59);
3651 /* calculate timezone offset */
3652 gchar *tzoffset(time_t *now)
3654 static gchar offset_string[6];
3660 lt = localtime(now);
3662 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3664 if (lt->tm_year < gmt.tm_year)
3666 else if (lt->tm_year > gmt.tm_year)
3668 else if (lt->tm_yday < gmt.tm_yday)
3670 else if (lt->tm_yday > gmt.tm_yday)
3678 if (off >= 24 * 60) /* should be impossible */
3679 off = 23 * 60 + 59; /* if not, insert silly value */
3681 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3683 return offset_string;
3686 void get_rfc822_date(gchar *buf, gint len)
3690 gchar day[4], mon[4];
3691 gint dd, hh, mm, ss, yyyy;
3696 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3697 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3698 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3699 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3702 /* just a wrapper to suppress the warning of gcc about %c */
3703 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3704 const struct tm *tm)
3706 return strftime(s, max, format, tm);
3709 void debug_set_mode(gboolean mode)
3714 gboolean debug_get_mode(void)
3719 void debug_print_real(const gchar *format, ...)
3722 gchar buf[BUFFSIZE];
3724 if (!debug_mode) return;
3726 va_start(args, format);
3727 g_vsnprintf(buf, sizeof(buf), format, args);
3734 const char * debug_srcname(const char *file)
3736 const char *s = strrchr (file, '/');
3741 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3743 if (subject == NULL)
3746 subject += subject_get_prefix_length(subject);
3748 return g_hash_table_lookup(subject_table, subject);
3751 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3754 if (subject == NULL || *subject == 0)
3756 subject += subject_get_prefix_length(subject);
3757 g_hash_table_insert(subject_table, subject, data);
3760 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3762 if (subject == NULL)
3765 subject += subject_get_prefix_length(subject);
3766 g_hash_table_remove(subject_table, subject);
3770 *\brief Check if a string is prefixed with known (combinations)
3771 * of prefixes. The function assumes that each prefix
3772 * is terminated by zero or exactly _one_ space.
3774 *\param str String to check for a prefixes
3776 *\return int Number of chars in the prefix that should be skipped
3777 * for a "clean" subject line. If no prefix was found, 0
3780 int subject_get_prefix_length(const gchar *subject)
3782 /*!< Array with allowable reply prefixes regexps. */
3783 static const gchar * const prefixes[] = {
3784 "Re\\:", /* "Re:" */
3785 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3786 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3787 "Aw\\:", /* "Aw:" (German) */
3788 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3789 "Res\\:", /* "Res:" (Brazilian Outlook) */
3790 "Fw\\:", /* "Fw:" Forward */
3791 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3792 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3793 "Rif\\:" /* "Rif:" (Italian Outlook) */
3796 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3799 static regex_t regex;
3800 static gboolean init_;
3802 if (!subject) return 0;
3803 if (!*subject) return 0;
3806 GString *s = g_string_new("");
3808 for (n = 0; n < PREFIXES; n++)
3809 /* Terminate each prefix regexpression by a
3810 * "\ ?" (zero or ONE space), and OR them */
3811 g_string_append_printf(s, "(%s\\ ?)%s",
3816 g_string_prepend(s, "(");
3817 g_string_append(s, ")+"); /* match at least once */
3818 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3821 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3822 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3823 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3824 debug_print("Error compiling regexp %s\n", s->str);
3825 g_string_free(s, TRUE);
3829 g_string_free(s, TRUE);
3833 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3839 guint g_stricase_hash(gconstpointer gptr)
3841 guint hash_result = 0;
3844 for (str = gptr; str && *str; str++) {
3845 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3846 else hash_result += *str;
3852 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3854 const char *str1 = gptr1;
3855 const char *str2 = gptr2;
3857 return !g_utf8_collate(str1, str2);
3860 gint g_int_compare(gconstpointer a, gconstpointer b)
3862 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3865 gchar *generate_msgid(gchar *buf, gint len)
3874 addr = g_strconcat("@", get_domain_name(), NULL);
3876 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3877 lt->tm_year + 1900, lt->tm_mon + 1,
3878 lt->tm_mday, lt->tm_hour,
3879 lt->tm_min, lt->tm_sec,
3880 (guint) rand(), addr);
3887 quote_cmd_argument()
3889 return a quoted string safely usable in argument of a command.
3891 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3894 gint quote_cmd_argument(gchar * result, guint size,
3904 for(p = path ; * p != '\0' ; p ++) {
3906 if (isalnum((guchar)*p) || (* p == '/')) {
3907 if (remaining > 0) {
3913 result[size - 1] = '\0';
3918 if (remaining >= 2) {
3926 result[size - 1] = '\0';
3931 if (remaining > 0) {
3935 result[size - 1] = '\0';
3949 static void g_node_map_recursive(GNode *node, gpointer data)
3951 GNodeMapData *mapdata = (GNodeMapData *) data;
3953 GNodeMapData newmapdata;
3956 newdata = mapdata->func(node->data, mapdata->data);
3957 if (newdata != NULL) {
3958 newnode = g_node_new(newdata);
3959 g_node_append(mapdata->parent, newnode);
3961 newmapdata.parent = newnode;
3962 newmapdata.func = mapdata->func;
3963 newmapdata.data = mapdata->data;
3965 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3969 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3972 GNodeMapData mapdata;
3974 g_return_val_if_fail(node != NULL, NULL);
3975 g_return_val_if_fail(func != NULL, NULL);
3977 root = g_node_new(func(node->data, data));
3979 mapdata.parent = root;
3980 mapdata.func = func;
3981 mapdata.data = data;
3983 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3988 #define HEX_TO_INT(val, hex) \
3992 if ('0' <= c && c <= '9') { \
3994 } else if ('a' <= c && c <= 'f') { \
3995 val = c - 'a' + 10; \
3996 } else if ('A' <= c && c <= 'F') { \
3997 val = c - 'A' + 10; \
4003 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4010 if (hi == -1 || lo == -1)
4013 *out = (hi << 4) + lo;
4017 #define INT_TO_HEX(hex, val) \
4020 hex = '0' + (val); \
4022 hex = 'A' + (val) - 10; \
4025 void get_hex_str(gchar *out, guchar ch)
4029 INT_TO_HEX(hex, ch >> 4);
4031 INT_TO_HEX(hex, ch & 0x0f);
4037 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4039 #define G_PRINT_REF g_print
4043 *\brief Register ref counted pointer. It is based on GBoxed, so should
4044 * work with anything that uses the GType system. The semantics
4045 * are similar to a C++ auto pointer, with the exception that
4046 * C doesn't have automatic closure (calling destructors) when
4047 * exiting a block scope.
4048 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4049 * function directly.
4051 *\return GType A GType type.
4053 GType g_auto_pointer_register(void)
4055 static GType auto_pointer_type;
4056 if (!auto_pointer_type)
4058 g_boxed_type_register_static
4059 ("G_TYPE_AUTO_POINTER",
4060 (GBoxedCopyFunc) g_auto_pointer_copy,
4061 (GBoxedFreeFunc) g_auto_pointer_free);
4062 return auto_pointer_type;
4066 *\brief Structure with g_new() allocated pointer guarded by the
4069 typedef struct AutoPointerRef {
4070 void (*free) (gpointer);
4076 *\brief The auto pointer opaque structure that references the
4077 * pointer guard block.
4079 typedef struct AutoPointer {
4080 AutoPointerRef *ref;
4081 gpointer ptr; /*!< access to protected pointer */
4085 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4089 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4090 * ... when assigning, copying and freeing storage elements
4092 * gtk_list_store_new(N_S_COLUMNS,
4093 * G_TYPE_AUTO_POINTER,
4097 * Template *precious_data = g_new0(Template, 1);
4098 * g_pointer protect = g_auto_pointer_new(precious_data);
4100 * gtk_list_store_set(container, &iter,
4104 * ... the gtk_list_store has copied the pointer and
4105 * ... incremented its reference count, we should free
4106 * ... the auto pointer (in C++ a destructor would do
4107 * ... this for us when leaving block scope)
4109 * g_auto_pointer_free(protect);
4111 * ... gtk_list_store_set() now manages the data. When
4112 * ... *explicitly* requesting a pointer from the list
4113 * ... store, don't forget you get a copy that should be
4114 * ... freed with g_auto_pointer_free() eventually.
4118 *\param pointer Pointer to be guarded.
4120 *\return GAuto * Pointer that should be used in containers with
4123 GAuto *g_auto_pointer_new(gpointer p)
4125 AutoPointerRef *ref;
4131 ref = g_new0(AutoPointerRef, 1);
4132 ptr = g_new0(AutoPointer, 1);
4142 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4148 *\brief Allocate an autopointer using the passed \a free function to
4149 * free the guarded pointer
4151 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4158 aptr = g_auto_pointer_new(p);
4159 aptr->ref->free = free_;
4163 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4165 if (auto_ptr == NULL)
4167 return ((AutoPointer *) auto_ptr)->ptr;
4171 *\brief Copies an auto pointer by. It's mostly not necessary
4172 * to call this function directly, unless you copy/assign
4173 * the guarded pointer.
4175 *\param auto_ptr Auto pointer returned by previous call to
4176 * g_auto_pointer_new_XXX()
4178 *\return gpointer An auto pointer
4180 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4183 AutoPointerRef *ref;
4186 if (auto_ptr == NULL)
4191 newp = g_new0(AutoPointer, 1);
4194 newp->ptr = ref->pointer;
4198 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4204 *\brief Free an auto pointer
4206 void g_auto_pointer_free(GAuto *auto_ptr)
4209 AutoPointerRef *ref;
4211 if (auto_ptr == NULL)
4217 if (--(ref->cnt) == 0) {
4219 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4221 ref->free(ref->pointer);
4226 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4231 void replace_returns(gchar *str)
4236 while (strstr(str, "\n")) {
4237 *strstr(str, "\n") = ' ';
4239 while (strstr(str, "\r")) {
4240 *strstr(str, "\r") = ' ';
4244 /* get_uri_part() - retrieves a URI starting from scanpos.
4245 Returns TRUE if succesful */
4246 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4247 const gchar **bp, const gchar **ep, gboolean hdr)
4251 g_return_val_if_fail(start != NULL, FALSE);
4252 g_return_val_if_fail(scanpos != NULL, FALSE);
4253 g_return_val_if_fail(bp != NULL, FALSE);
4254 g_return_val_if_fail(ep != NULL, FALSE);
4258 /* find end point of URI */
4259 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4260 if (!isgraph(*(const guchar *)ep_) ||
4261 !IS_ASCII(*(const guchar *)ep_) ||
4262 strchr("[]{}()<>\"", *ep_))
4266 /* no punctuation at end of string */
4268 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4269 * should pass some URI type to this function and decide on that whether
4270 * to perform punctuation stripping */
4272 #define IS_REAL_PUNCT(ch) (ispunct(ch) && ((ch) != '/'))
4274 for (; ep_ - 1 > scanpos + 1 &&
4275 IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
4279 #undef IS_REAL_PUNCT
4286 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4288 return g_strndup(bp, ep - bp);
4291 /* valid mail address characters */
4292 #define IS_RFC822_CHAR(ch) \
4296 !g_ascii_isspace(ch) && \
4297 !strchr("(),;<>\"", (ch)))
4299 /* alphabet and number within 7bit ASCII */
4300 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4301 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4303 static GHashTable *create_domain_tab(void)
4305 static const gchar *toplvl_domains [] = {
4307 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4308 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4309 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4310 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4311 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4312 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4313 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4314 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4315 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4316 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4317 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4318 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4319 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4320 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4321 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4322 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4323 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4324 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4325 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4326 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4327 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4328 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4329 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4330 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4331 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4332 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4335 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4337 g_return_val_if_fail(htab, NULL);
4338 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4339 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4343 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4345 const gint MAX_LVL_DOM_NAME_LEN = 6;
4346 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4347 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4350 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4353 for (p = buf; p < m && first < last; *p++ = *first++)
4357 return g_hash_table_lookup(tab, buf) != NULL;
4360 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4361 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4362 const gchar **bp, const gchar **ep, gboolean hdr)
4364 /* more complex than the uri part because we need to scan back and forward starting from
4365 * the scan position. */
4366 gboolean result = FALSE;
4367 const gchar *bp_ = NULL;
4368 const gchar *ep_ = NULL;
4369 static GHashTable *dom_tab;
4370 const gchar *last_dot = NULL;
4371 const gchar *prelast_dot = NULL;
4372 const gchar *last_tld_char = NULL;
4374 /* the informative part of the email address (describing the name
4375 * of the email address owner) may contain quoted parts. the
4376 * closure stack stores the last encountered quotes. */
4377 gchar closure_stack[128];
4378 gchar *ptr = closure_stack;
4380 g_return_val_if_fail(start != NULL, FALSE);
4381 g_return_val_if_fail(scanpos != NULL, FALSE);
4382 g_return_val_if_fail(bp != NULL, FALSE);
4383 g_return_val_if_fail(ep != NULL, FALSE);
4387 /* go to the real start */
4388 if (start[0] == ',')
4390 if (start[0] == ';')
4392 while (start[0] == ' ' || start[0] == '\t')
4397 /* find end (either , or ; or end of line) */
4398 if (strstr(start, ",") && strstr(start, ";"))
4399 *ep = strstr(start,",") < strstr(start, ";")
4400 ? strstr(start, ",") : strstr(start, ";");
4401 else if (strstr(start, ","))
4402 *ep = strstr(start, ",");
4403 else if (strstr(start, ";"))
4404 *ep = strstr(start, ";");
4406 *ep = start+strlen(start);
4408 /* check there's still an @ in that, or search
4409 * further if possible */
4410 if (strstr(start, "@") && strstr(start, "@") < *ep)
4412 else if (*ep < start+strlen(start)) {
4420 dom_tab = create_domain_tab();
4421 g_return_val_if_fail(dom_tab, FALSE);
4423 /* scan start of address */
4424 for (bp_ = scanpos - 1;
4425 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4428 /* TODO: should start with an alnum? */
4430 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4433 if (bp_ != scanpos) {
4434 /* scan end of address */
4435 for (ep_ = scanpos + 1;
4436 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4438 prelast_dot = last_dot;
4440 if (*(last_dot + 1) == '.') {
4441 if (prelast_dot == NULL)
4443 last_dot = prelast_dot;
4448 /* TODO: really should terminate with an alnum? */
4449 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4454 if (last_dot == NULL)
4456 if (last_dot >= ep_)
4457 last_dot = prelast_dot;
4458 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4462 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4463 if (*last_tld_char == '?')
4466 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4473 if (!result) return FALSE;
4475 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4476 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4477 && IS_RFC822_CHAR(*(ep_ + 3))) {
4478 /* this informative part with an @ in it is
4479 * followed by the email address */
4482 /* go to matching '>' (or next non-rfc822 char, like \n) */
4483 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4486 /* include the bracket */
4487 if (*ep_ == '>') ep_++;
4489 /* include the leading quote */
4497 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4498 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4501 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4502 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4505 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4506 #define IN_STACK() (ptr > closure_stack)
4507 /* has underrun check */
4508 #define POP_STACK() if(IN_STACK()) --ptr
4509 /* has overrun check */
4510 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4511 /* has underrun check */
4512 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4516 /* scan for the informative part. */
4517 for (bp_ -= 2; bp_ >= start; bp_--) {
4518 /* if closure on the stack keep scanning */
4519 if (PEEK_STACK() == *bp_) {
4523 if (*bp_ == '\'' || *bp_ == '"') {
4528 /* if nothing in the closure stack, do the special conditions
4529 * the following if..else expression simply checks whether
4530 * a token is acceptable. if not acceptable, the clause
4531 * should terminate the loop with a 'break' */
4532 if (!PEEK_STACK()) {
4534 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4535 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4536 /* hyphens are allowed, but only in
4538 } else if (strchr(" \"'", *bp_)) {
4539 /* but anything not being a punctiation
4542 break; /* anything else is rejected */
4549 /* scan forward (should start with an alnum) */
4550 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4566 #undef IS_ASCII_ALNUM
4567 #undef IS_RFC822_CHAR
4569 gchar *make_email_string(const gchar *bp, const gchar *ep)
4571 /* returns a mailto: URI; mailto: is also used to detect the
4572 * uri type later on in the button_pressed signal handler */
4576 tmp = g_strndup(bp, ep - bp);
4577 result = g_strconcat("mailto:", tmp, NULL);
4583 gchar *make_http_string(const gchar *bp, const gchar *ep)
4585 /* returns an http: URI; */
4589 tmp = g_strndup(bp, ep - bp);
4590 result = g_strconcat("http://", tmp, NULL);
4596 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4598 FILE *fp = fopen(path, "rb");
4599 gchar buf[BUFFSIZE];
4600 gchar *result = NULL;
4603 while (fgets(buf, sizeof (buf), fp) != NULL) {
4604 gchar **parts = g_strsplit(buf, ";", -1);
4605 gchar *trimmed = parts[0];
4606 while (trimmed[0] == ' ')
4608 while (trimmed[strlen(trimmed)-1] == ' ')
4609 trimmed[strlen(trimmed)-1] = '\0';
4611 if (!strcmp(trimmed, type)) {
4613 while (trimmed[0] == ' ')
4615 while (trimmed[strlen(trimmed)-1] == ' ')
4616 trimmed[strlen(trimmed)-1] = '\0';
4617 while (trimmed[strlen(trimmed)-1] == '\n')
4618 trimmed[strlen(trimmed)-1] = '\0';
4619 while (trimmed[strlen(trimmed)-1] == '\r')
4620 trimmed[strlen(trimmed)-1] = '\0';
4621 result = g_strdup(trimmed);
4624 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4625 gchar *start = g_strdup(result);
4626 gchar *end = g_strdup(strstr(result, "%s")+2);
4628 *strstr(start, "%s") = '\0';
4629 tmp = g_strconcat(start,"'%s'",end, NULL);
4642 gchar *mailcap_get_command_for_type(const gchar *type)
4644 gchar *result = NULL;
4646 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4647 result = mailcap_get_command_in_file(path, type);
4651 result = mailcap_get_command_in_file("/etc/mailcap", type);
4655 gint copy_dir(const gchar *src, const gchar *dst)
4660 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4661 g_warning("failed to open directory: %s\n", src);
4665 if (make_dir(dst) < 0)
4668 while ((name = g_dir_read_name(dir)) != NULL) {
4669 gchar *old_file, *new_file;
4670 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4671 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4672 debug_print("copying: %s -> %s\n", old_file, new_file);
4673 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4674 gint r = copy_file(old_file, new_file, TRUE);
4677 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4678 gint r = copy_dir(old_file, new_file);