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.
27 #include <glib/gi18n.h>
33 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
41 #include <sys/types.h>
43 # include <sys/wait.h>
48 #include <sys/utsname.h>
57 #include "../codeconv.h"
61 static gboolean debug_mode = FALSE;
64 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
65 gint g_chdir(const gchar *path)
68 if (G_WIN32_HAVE_WIDECHAR_API()) {
73 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
79 retval = _wchdir(wpath);
91 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
92 if (cp_path == NULL) {
97 retval = chdir(cp_path);
110 gint g_chmod(const gchar *path, gint mode)
113 if (G_WIN32_HAVE_WIDECHAR_API()) {
118 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
124 retval = _wchmod(wpath, mode);
136 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
137 if (cp_path == NULL) {
142 retval = chmod(cp_path, mode);
151 return chmod(path, mode);
154 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
156 void list_free_strings(GList *list)
158 list = g_list_first(list);
160 while (list != NULL) {
166 void slist_free_strings(GSList *list)
168 while (list != NULL) {
174 GSList *slist_concat_unique (GSList *first, GSList *second)
182 } else if (second == NULL)
185 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
186 if (g_slist_find(ret, tmp->data) == NULL)
187 ret = g_slist_prepend(ret, tmp->data);
192 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
197 void hash_free_strings(GHashTable *table)
199 g_hash_table_foreach(table, hash_free_strings_func, NULL);
202 static void hash_free_value_mem_func(gpointer key, gpointer value,
208 void hash_free_value_mem(GHashTable *table)
210 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
213 gint str_case_equal(gconstpointer v, gconstpointer v2)
215 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
218 guint str_case_hash(gconstpointer key)
220 const gchar *p = key;
224 h = g_ascii_tolower(h);
225 for (p += 1; *p != '\0'; p++)
226 h = (h << 5) - h + g_ascii_tolower(*p);
232 void ptr_array_free_strings(GPtrArray *array)
237 g_return_if_fail(array != NULL);
239 for (i = 0; i < array->len; i++) {
240 str = g_ptr_array_index(array, i);
245 gboolean str_find(const gchar *haystack, const gchar *needle)
247 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
250 gboolean str_case_find(const gchar *haystack, const gchar *needle)
252 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
255 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
257 return strcmp(haystack, needle) == 0;
260 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
262 return g_ascii_strcasecmp(haystack, needle) == 0;
265 gint to_number(const gchar *nstr)
267 register const gchar *p;
269 if (*nstr == '\0') return -1;
271 for (p = nstr; *p != '\0'; p++)
272 if (!g_ascii_isdigit(*p)) return -1;
277 /* convert integer into string,
278 nstr must be not lower than 11 characters length */
279 gchar *itos_buf(gchar *nstr, gint n)
281 g_snprintf(nstr, 11, "%d", n);
285 /* convert integer into string */
288 static gchar nstr[11];
290 return itos_buf(nstr, n);
293 gchar *to_human_readable(off_t size)
295 static gchar str[10];
298 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
299 else if (size >> 10 < 1024)
300 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
301 else if (size >> 20 < 1024)
302 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
304 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
309 /* strcmp with NULL-checking */
310 gint strcmp2(const gchar *s1, const gchar *s2)
312 if (s1 == NULL || s2 == NULL)
315 return strcmp(s1, s2);
317 /* strstr with NULL-checking */
318 gchar *strstr2(const gchar *s1, const gchar *s2)
320 if (s1 == NULL || s2 == NULL)
323 return strstr(s1, s2);
326 gint path_cmp(const gchar *s1, const gchar *s2)
330 if (s1 == NULL || s2 == NULL) return -1;
331 if (*s1 == '\0' || *s2 == '\0') return -1;
336 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
337 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
339 return strncmp(s1, s2, MAX(len1, len2));
342 /* remove trailing return code */
343 gchar *strretchomp(gchar *str)
347 if (!*str) return str;
349 for (s = str + strlen(str) - 1;
350 s >= str && (*s == '\n' || *s == '\r');
357 /* remove trailing character */
358 gchar *strtailchomp(gchar *str, gchar tail_char)
362 if (!*str) return str;
363 if (tail_char == '\0') return str;
365 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
371 /* remove CR (carriage return) */
372 gchar *strcrchomp(gchar *str)
376 if (!*str) return str;
378 s = str + strlen(str) - 1;
379 if (*s == '\n' && s > str && *(s - 1) == '\r') {
387 void file_strip_crs(const gchar *file)
389 FILE *fp = NULL, *outfp = NULL;
391 gchar *out = get_tmp_file();
395 fp = fopen(file, "rb");
399 outfp = fopen(out, "wb");
405 while (fgets(buf, sizeof (buf), fp) != NULL) {
412 rename_force(out, file);
417 /* Similar to `strstr' but this function ignores the case of both strings. */
418 gchar *strcasestr(const gchar *haystack, const gchar *needle)
420 register size_t haystack_len, needle_len;
422 haystack_len = strlen(haystack);
423 needle_len = strlen(needle);
425 if (haystack_len < needle_len || needle_len == 0)
428 while (haystack_len >= needle_len) {
429 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
430 return (gchar *)haystack;
440 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
441 gconstpointer needle, size_t needlelen)
443 const gchar *haystack_ = (const gchar *)haystack;
444 const gchar *needle_ = (const gchar *)needle;
445 const gchar *haystack_cur = (const gchar *)haystack;
448 return memchr(haystack_, *needle_, haystacklen);
450 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
452 if (haystacklen - (haystack_cur - haystack_) < needlelen)
454 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
455 return (gpointer)haystack_cur;
463 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
464 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
466 register const gchar *s = src;
467 register gchar *d = dest;
477 int iswalnum(wint_t wc)
479 return g_ascii_isalnum((int)wc);
484 int iswspace(wint_t wc)
486 return g_ascii_isspace((int)wc);
491 wint_t towlower(wint_t wc)
493 if (wc >= L'A' && wc <= L'Z')
494 return wc + L'a' - L'A';
501 size_t wcslen(const wchar_t *s)
513 /* Copy SRC to DEST. */
514 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
522 } while (c != L'\0');
529 /* Copy no more than N wide-characters of SRC to DEST. */
530 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
540 } while (c != L'\0');
551 /* Duplicate S, returning an identical malloc'd string. */
552 wchar_t *wcsdup(const wchar_t *s)
557 new_str = g_new(wchar_t, wcslen(s) + 1);
565 /* Duplicate no more than N wide-characters of S,
566 returning an identical malloc'd string. */
567 wchar_t *wcsndup(const wchar_t *s, size_t n)
572 new_str = g_new(wchar_t, n + 1);
573 wcsncpy(new_str, s, n);
574 new_str[n] = (wchar_t)0;
581 wchar_t *strdup_mbstowcs(const gchar *s)
586 new_str = g_new(wchar_t, strlen(s) + 1);
587 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
591 new_str = g_realloc(new_str,
592 sizeof(wchar_t) * (wcslen(new_str) + 1));
599 gchar *strdup_wcstombs(const wchar_t *s)
605 len = wcslen(s) * MB_CUR_MAX + 1;
606 new_str = g_new(gchar, len);
607 if (wcstombs(new_str, s, len) < 0) {
611 new_str = g_realloc(new_str, strlen(new_str) + 1);
618 /* Compare S1 and S2, ignoring case. */
619 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
625 c1 = towlower(*s1++);
626 c2 = towlower(*s2++);
629 else if (c1 == 0 && c2 == 0)
636 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
637 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
639 register size_t haystack_len, needle_len;
641 haystack_len = wcslen(haystack);
642 needle_len = wcslen(needle);
644 if (haystack_len < needle_len || needle_len == 0)
647 while (haystack_len >= needle_len) {
648 if (!wcsncasecmp(haystack, needle, needle_len))
649 return (wchar_t *)haystack;
659 gint get_mbs_len(const gchar *s)
669 mb_len = g_utf8_skip[*(guchar *)p];
681 /* Examine if next block is non-ASCII string */
682 gboolean is_next_nonascii(const gchar *s)
686 /* skip head space */
687 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
689 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
690 if (*(guchar *)p > 127 || *(guchar *)p < 32)
697 gint get_next_word_len(const gchar *s)
701 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
707 /* compare subjects */
708 gint subject_compare(const gchar *s1, const gchar *s2)
712 if (!s1 || !s2) return -1;
713 if (!*s1 || !*s2) return -1;
715 Xstrdup_a(str1, s1, return -1);
716 Xstrdup_a(str2, s2, return -1);
718 trim_subject_for_compare(str1);
719 trim_subject_for_compare(str2);
721 if (!*str1 || !*str2) return -1;
723 return strcmp(str1, str2);
726 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
730 if (!s1 || !s2) return -1;
732 Xstrdup_a(str1, s1, return -1);
733 Xstrdup_a(str2, s2, return -1);
735 trim_subject_for_sort(str1);
736 trim_subject_for_sort(str2);
738 return g_utf8_collate(str1, str2);
741 void trim_subject_for_compare(gchar *str)
745 eliminate_parenthesis(str, '[', ']');
746 eliminate_parenthesis(str, '(', ')');
749 srcp = str + subject_get_prefix_length(str);
751 memmove(str, srcp, strlen(srcp) + 1);
754 void trim_subject_for_sort(gchar *str)
760 srcp = str + subject_get_prefix_length(str);
762 memmove(str, srcp, strlen(srcp) + 1);
765 void trim_subject(gchar *str)
767 register gchar *srcp;
773 srcp = str + subject_get_prefix_length(str);
778 } else if (*srcp == '(') {
790 else if (*srcp == cl)
797 while (g_ascii_isspace(*srcp)) srcp++;
798 memmove(str, srcp, strlen(srcp) + 1);
801 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
803 register gchar *srcp, *destp;
808 while ((destp = strchr(destp, op))) {
814 else if (*srcp == cl)
820 while (g_ascii_isspace(*srcp)) srcp++;
821 memmove(destp, srcp, strlen(srcp) + 1);
825 void extract_parenthesis(gchar *str, gchar op, gchar cl)
827 register gchar *srcp, *destp;
832 while ((srcp = strchr(destp, op))) {
835 memmove(destp, srcp + 1, strlen(srcp));
840 else if (*destp == cl)
852 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
855 register gchar *srcp, *destp;
857 gboolean in_quote = FALSE;
861 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
864 memmove(destp, srcp + 1, strlen(srcp));
867 if (*destp == op && !in_quote)
869 else if (*destp == cl && !in_quote)
871 else if (*destp == quote_chr)
883 void eliminate_quote(gchar *str, gchar quote_chr)
885 register gchar *srcp, *destp;
889 while ((destp = strchr(destp, quote_chr))) {
890 if ((srcp = strchr(destp + 1, quote_chr))) {
892 while (g_ascii_isspace(*srcp)) srcp++;
893 memmove(destp, srcp, strlen(srcp) + 1);
901 void extract_quote(gchar *str, gchar quote_chr)
905 if ((str = strchr(str, quote_chr))) {
907 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
908 memmove(p - 1, p, strlen(p) + 1);
913 memmove(str, str + 1, p - str);
918 void eliminate_address_comment(gchar *str)
920 register gchar *srcp, *destp;
925 while ((destp = strchr(destp, '"'))) {
926 if ((srcp = strchr(destp + 1, '"'))) {
931 while (g_ascii_isspace(*srcp)) srcp++;
932 memmove(destp, srcp, strlen(srcp) + 1);
942 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
948 else if (*srcp == ')')
954 while (g_ascii_isspace(*srcp)) srcp++;
955 memmove(destp, srcp, strlen(srcp) + 1);
959 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
961 gboolean in_quote = FALSE;
964 if (*str == c && !in_quote)
966 if (*str == quote_chr)
974 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
976 gboolean in_quote = FALSE;
979 p = str + strlen(str) - 1;
981 if (*p == c && !in_quote)
991 void extract_address(gchar *str)
993 eliminate_address_comment(str);
994 if (strchr_with_skip_quote(str, '"', '<'))
995 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
999 void extract_list_id_str(gchar *str)
1001 if (strchr_with_skip_quote(str, '"', '<'))
1002 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1006 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1011 if (!str) return addr_list;
1013 Xstrdup_a(work, str, return addr_list);
1016 eliminate_address_comment(work);
1019 while (workp && *workp) {
1022 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1028 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1029 extract_parenthesis_with_skip_quote
1030 (workp, '"', '<', '>');
1034 addr_list = g_slist_append(addr_list, g_strdup(workp));
1042 GSList *address_list_append(GSList *addr_list, const gchar *str)
1044 return address_list_append_real(addr_list, str, TRUE);
1047 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1049 return address_list_append_real(addr_list, str, FALSE);
1052 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1056 if (!str) return msgid_list;
1059 while (strp && *strp) {
1060 const gchar *start, *end;
1063 if ((start = strchr(strp, '<')) != NULL) {
1064 end = strchr(start + 1, '>');
1069 msgid = g_strndup(start + 1, end - start - 1);
1072 msgid_list = g_slist_prepend(msgid_list, msgid);
1082 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1086 list = references_list_prepend(NULL, str);
1087 list = g_slist_reverse(list);
1088 msgid_list = g_slist_concat(msgid_list, list);
1093 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1098 if (!str) return group_list;
1100 Xstrdup_a(work, str, return group_list);
1104 while (workp && *workp) {
1107 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1115 group_list = g_slist_append(group_list,
1124 GList *add_history(GList *list, const gchar *str)
1128 g_return_val_if_fail(str != NULL, list);
1130 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1133 list = g_list_remove(list, old->data);
1134 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1137 last = g_list_last(list);
1140 g_list_remove(list, last->data);
1144 list = g_list_prepend(list, g_strdup(str));
1149 void remove_return(gchar *str)
1151 register gchar *p = str;
1154 if (*p == '\n' || *p == '\r')
1155 memmove(p, p + 1, strlen(p));
1161 void remove_space(gchar *str)
1163 register gchar *p = str;
1168 while (g_ascii_isspace(*(p + spc)))
1171 memmove(p, p + spc, strlen(p + spc) + 1);
1177 void unfold_line(gchar *str)
1179 register gchar *p = str;
1183 if (*p == '\n' || *p == '\r') {
1186 while (g_ascii_isspace(*(p + spc)))
1189 memmove(p, p + spc, strlen(p + spc) + 1);
1195 void subst_char(gchar *str, gchar orig, gchar subst)
1197 register gchar *p = str;
1206 void subst_chars(gchar *str, gchar *orig, gchar subst)
1208 register gchar *p = str;
1211 if (strchr(orig, *p) != NULL)
1217 void subst_for_filename(gchar *str)
1221 subst_chars(str, "\t\r\n\\/*", '_');
1224 void subst_for_shellsafe_filename(gchar *str)
1228 subst_for_filename(str);
1229 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1232 gboolean is_header_line(const gchar *str)
1234 if (str[0] == ':') return FALSE;
1236 while (*str != '\0' && *str != ' ') {
1245 gboolean is_ascii_str(const gchar *str)
1247 const guchar *p = (const guchar *)str;
1249 while (*p != '\0') {
1250 if (*p != '\t' && *p != ' ' &&
1251 *p != '\r' && *p != '\n' &&
1252 (*p < 32 || *p >= 127))
1260 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1262 const gchar *first_pos;
1263 const gchar *last_pos;
1264 const gchar *p = str;
1265 gint quote_level = -1;
1267 /* speed up line processing by only searching to the last '>' */
1268 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1269 /* skip a line if it contains a '<' before the initial '>' */
1270 if (memchr(str, '<', first_pos - str) != NULL)
1272 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1276 while (p <= last_pos) {
1277 while (p < last_pos) {
1278 if (g_ascii_isspace(*p))
1284 if (strchr(quote_chars, *p))
1286 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1287 /* any characters are allowed except '-' and space */
1289 && !strchr(quote_chars, *p)
1290 && !g_ascii_isspace(*p)
1293 if (strchr(quote_chars, *p))
1305 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1307 const gchar *p = str, *q;
1308 gint cur_line = 0, len;
1310 while ((q = strchr(p, '\n')) != NULL) {
1312 if (len > max_chars) {
1322 if (len > max_chars) {
1331 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1333 gchar * position = NULL;
1334 gchar * tmp_pos = NULL;
1337 if (quote_chars == NULL)
1340 for (i = 0; i < strlen(quote_chars); i++) {
1341 tmp_pos = strchr (str, quote_chars[i]);
1343 || (tmp_pos != NULL && position >= tmp_pos) )
1349 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1351 gchar * position = NULL;
1352 gchar * tmp_pos = NULL;
1355 if (quote_chars == NULL)
1358 for (i = 0; i < strlen(quote_chars); i++) {
1359 tmp_pos = strrchr (str, quote_chars[i]);
1361 || (tmp_pos != NULL && position <= tmp_pos) )
1367 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1369 register guint haystack_len, needle_len;
1370 gboolean in_squote = FALSE, in_dquote = FALSE;
1372 haystack_len = strlen(haystack);
1373 needle_len = strlen(needle);
1375 if (haystack_len < needle_len || needle_len == 0)
1378 while (haystack_len >= needle_len) {
1379 if (!in_squote && !in_dquote &&
1380 !strncmp(haystack, needle, needle_len))
1381 return (gchar *)haystack;
1383 /* 'foo"bar"' -> foo"bar"
1384 "foo'bar'" -> foo'bar' */
1385 if (*haystack == '\'') {
1388 else if (!in_dquote)
1390 } else if (*haystack == '\"') {
1393 else if (!in_squote)
1404 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1407 gchar quote_chr = '"';
1409 gboolean in_quote = FALSE;
1413 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1417 if (*p == op && !in_quote)
1419 else if (*p == cl && !in_quote)
1421 else if (*p == quote_chr)
1434 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1437 GSList *string_list = NULL, *slist;
1439 const gchar *s_op, *s_cl;
1442 g_return_val_if_fail(str != NULL, NULL);
1445 max_tokens = G_MAXINT;
1447 s_op = strchr_with_skip_quote(str, '"', op);
1448 if (!s_op) return NULL;
1450 s_cl = strchr_parenthesis_close(str, op, cl);
1458 new_string = g_new(gchar, len + 1);
1459 strncpy(new_string, str, len);
1460 new_string[len] = 0;
1461 string_list = g_slist_prepend(string_list, new_string);
1465 while (*str && g_ascii_isspace(*str)) str++;
1467 string_list = g_slist_prepend(string_list,
1470 s_op = strchr_with_skip_quote(str, '"', op);
1471 if (!--max_tokens || !s_op) break;
1475 s_cl = strchr_parenthesis_close(str, op, cl);
1476 } while (--max_tokens && s_cl);
1479 str_array = g_new(gchar*, n);
1483 str_array[i--] = NULL;
1484 for (slist = string_list; slist; slist = slist->next)
1485 str_array[i--] = slist->data;
1487 g_slist_free(string_list);
1492 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1495 GSList *string_list = NULL, *slist;
1496 gchar **str_array, *s, *new_str;
1497 guint i, n = 1, len;
1499 g_return_val_if_fail(str != NULL, NULL);
1500 g_return_val_if_fail(delim != NULL, NULL);
1503 max_tokens = G_MAXINT;
1505 s = strstr_with_skip_quote(str, delim);
1507 guint delimiter_len = strlen(delim);
1511 new_str = g_strndup(str, len);
1513 if (new_str[0] == '\'' || new_str[0] == '\"') {
1514 if (new_str[len - 1] == new_str[0]) {
1515 new_str[len - 1] = '\0';
1516 memmove(new_str, new_str + 1, len - 1);
1519 string_list = g_slist_prepend(string_list, new_str);
1521 str = s + delimiter_len;
1522 s = strstr_with_skip_quote(str, delim);
1523 } while (--max_tokens && s);
1527 new_str = g_strdup(str);
1528 if (new_str[0] == '\'' || new_str[0] == '\"') {
1530 if (new_str[len - 1] == new_str[0]) {
1531 new_str[len - 1] = '\0';
1532 memmove(new_str, new_str + 1, len - 1);
1535 string_list = g_slist_prepend(string_list, new_str);
1539 str_array = g_new(gchar*, n);
1543 str_array[i--] = NULL;
1544 for (slist = string_list; slist; slist = slist->next)
1545 str_array[i--] = slist->data;
1547 g_slist_free(string_list);
1552 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1554 gchar *abbrev_group;
1556 const gchar *p = group;
1559 g_return_val_if_fail(group != NULL, NULL);
1561 last = group + strlen(group);
1562 abbrev_group = ap = g_malloc(strlen(group) + 1);
1567 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1569 while (*p != '.') p++;
1572 return abbrev_group;
1577 return abbrev_group;
1580 gchar *trim_string(const gchar *str, gint len)
1582 const gchar *p = str;
1587 if (!str) return NULL;
1588 if (strlen(str) <= len)
1589 return g_strdup(str);
1590 if (g_utf8_validate(str, -1, NULL) == FALSE)
1591 return g_strdup(str);
1593 while (*p != '\0') {
1594 mb_len = g_utf8_skip[*(guchar *)p];
1597 else if (new_len + mb_len > len)
1604 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1605 return g_strconcat(new_str, "...", NULL);
1608 GList *uri_list_extract_filenames(const gchar *uri_list)
1610 GList *result = NULL;
1612 gchar *escaped_utf8uri;
1618 while (g_ascii_isspace(*p)) p++;
1619 if (!strncmp(p, "file:", 5)) {
1622 while (*q && *q != '\n' && *q != '\r') q++;
1625 gchar *file, *locale_file = NULL;
1627 while (q > p && g_ascii_isspace(*q))
1629 Xalloca(escaped_utf8uri, q - p + 2,
1631 Xalloca(file, q - p + 2,
1634 strncpy(escaped_utf8uri, p, q - p + 1);
1635 escaped_utf8uri[q - p + 1] = '\0';
1636 decode_uri(file, escaped_utf8uri);
1638 * g_filename_from_uri() rejects escaped/locale encoded uri
1639 * string which come from Nautilus.
1641 if (g_utf8_validate(file, -1, NULL))
1643 = conv_codeset_strdup(
1646 conv_get_locale_charset_str());
1648 locale_file = g_strdup(file + 5);
1649 result = g_list_append(result, locale_file);
1653 p = strchr(p, '\n');
1660 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1663 static gint axtoi(const gchar *hexstr)
1665 gint hi, lo, result;
1668 if ('0' <= hi && hi <= '9') {
1671 if ('a' <= hi && hi <= 'f') {
1674 if ('A' <= hi && hi <= 'F') {
1679 if ('0' <= lo && lo <= '9') {
1682 if ('a' <= lo && lo <= 'f') {
1685 if ('A' <= lo && lo <= 'F') {
1688 result = lo + (16 * hi);
1692 gboolean is_uri_string(const gchar *str)
1694 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1695 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1696 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1697 g_ascii_strncasecmp(str, "www.", 4) == 0);
1700 gchar *get_uri_path(const gchar *uri)
1702 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1703 return (gchar *)(uri + 7);
1704 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1705 return (gchar *)(uri + 8);
1706 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1707 return (gchar *)(uri + 6);
1709 return (gchar *)uri;
1712 gint get_uri_len(const gchar *str)
1716 if (is_uri_string(str)) {
1717 for (p = str; *p != '\0'; p++) {
1718 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1727 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1728 * plusses, and escape characters are used)
1730 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1732 gchar *dec = decoded_uri;
1733 const gchar *enc = encoded_uri;
1738 if (isxdigit((guchar)enc[0]) &&
1739 isxdigit((guchar)enc[1])) {
1757 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1758 gchar **subject, gchar **body)
1763 Xstrdup_a(tmp_mailto, mailto, return -1);
1765 if (!strncmp(tmp_mailto, "mailto:", 7))
1768 p = strchr(tmp_mailto, '?');
1775 *to = g_strdup(tmp_mailto);
1778 gchar *field, *value;
1795 if (*value == '\0') continue;
1797 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1798 *cc = g_strdup(value);
1799 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1800 *bcc = g_strdup(value);
1801 } else if (subject && !*subject &&
1802 !g_ascii_strcasecmp(field, "subject")) {
1803 *subject = g_malloc(strlen(value) + 1);
1804 decode_uri(*subject, value);
1805 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1806 *body = g_malloc(strlen(value) + 1);
1807 decode_uri(*body, value);
1814 const gchar *get_home_dir(void)
1817 static const gchar *home_dir = NULL;
1820 home_dir = g_get_home_dir();
1822 home_dir = "C:\\Sylpheed";
1827 return g_get_home_dir();
1831 const gchar *get_rc_dir(void)
1833 static gchar *rc_dir = NULL;
1837 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1838 "Application Data", G_DIR_SEPARATOR_S,
1841 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1848 const gchar *get_mail_base_dir(void)
1851 static gchar *mail_base_dir = NULL;
1854 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1857 return mail_base_dir;
1859 return get_home_dir();
1863 const gchar *get_news_cache_dir(void)
1865 static gchar *news_cache_dir = NULL;
1867 if (!news_cache_dir)
1868 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1869 NEWS_CACHE_DIR, NULL);
1871 return news_cache_dir;
1874 const gchar *get_imap_cache_dir(void)
1876 static gchar *imap_cache_dir = NULL;
1878 if (!imap_cache_dir)
1879 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1880 IMAP_CACHE_DIR, NULL);
1882 return imap_cache_dir;
1885 const gchar *get_mbox_cache_dir(void)
1887 static gchar *mbox_cache_dir = NULL;
1889 if (!mbox_cache_dir)
1890 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1891 MBOX_CACHE_DIR, NULL);
1893 return mbox_cache_dir;
1896 const gchar *get_mime_tmp_dir(void)
1898 static gchar *mime_tmp_dir = NULL;
1901 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1902 MIME_TMP_DIR, NULL);
1904 return mime_tmp_dir;
1907 const gchar *get_template_dir(void)
1909 static gchar *template_dir = NULL;
1912 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1913 TEMPLATE_DIR, NULL);
1915 return template_dir;
1918 const gchar *get_header_cache_dir(void)
1920 static gchar *header_dir = NULL;
1923 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1924 HEADER_CACHE_DIR, NULL);
1929 const gchar *get_tmp_dir(void)
1931 static gchar *tmp_dir = NULL;
1934 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1940 gchar *get_tmp_file(void)
1943 static guint32 id = 0;
1945 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1946 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1951 const gchar *get_domain_name(void)
1954 static gchar *domain_name = NULL;
1960 if (uname(&uts) < 0) {
1962 domain_name = "unknown";
1964 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
1965 perror("gethostbyname");
1966 domain_name = g_strdup(uts.nodename);
1968 domain_name = g_strdup(hp->h_name);
1972 debug_print("domain name = %s\n", domain_name);
1981 off_t get_file_size(const gchar *file)
1985 if (g_stat(file, &s) < 0) {
1986 FILE_OP_ERROR(file, "stat");
1993 off_t get_file_size_as_crlf(const gchar *file)
1997 gchar buf[BUFFSIZE];
1999 if ((fp = g_fopen(file, "rb")) == NULL) {
2000 FILE_OP_ERROR(file, "fopen");
2004 while (fgets(buf, sizeof(buf), fp) != NULL) {
2006 size += strlen(buf) + 2;
2010 FILE_OP_ERROR(file, "fgets");
2019 off_t get_left_file_size(FILE *fp)
2025 if ((pos = ftell(fp)) < 0) {
2029 if (fseek(fp, 0L, SEEK_END) < 0) {
2033 if ((end = ftell(fp)) < 0) {
2038 if (fseek(fp, pos, SEEK_SET) < 0) {
2046 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2053 if (g_stat(file, &s) < 0) {
2054 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2058 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2064 gboolean is_dir_exist(const gchar *dir)
2069 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2072 gboolean is_file_entry_exist(const gchar *file)
2077 return g_file_test(file, G_FILE_TEST_EXISTS);
2080 gboolean dirent_is_regular_file(struct dirent *d)
2082 #ifdef HAVE_DIRENT_D_TYPE
2083 if (d->d_type == DT_REG)
2085 else if (d->d_type != DT_UNKNOWN)
2089 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2092 gboolean dirent_is_directory(struct dirent *d)
2094 #ifdef HAVE_DIRENT_D_TYPE
2095 if (d->d_type == DT_DIR)
2097 else if (d->d_type != DT_UNKNOWN)
2101 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2104 gint change_dir(const gchar *dir)
2106 gchar *prevdir = NULL;
2109 prevdir = g_get_current_dir();
2111 if (g_chdir(dir) < 0) {
2112 FILE_OP_ERROR(dir, "chdir");
2113 if (debug_mode) g_free(prevdir);
2115 } else if (debug_mode) {
2118 cwd = g_get_current_dir();
2119 if (strcmp(prevdir, cwd) != 0)
2120 g_print("current dir: %s\n", cwd);
2128 gint make_dir(const gchar *dir)
2130 if (g_mkdir(dir, S_IRWXU) < 0) {
2131 FILE_OP_ERROR(dir, "mkdir");
2134 if (g_chmod(dir, S_IRWXU) < 0)
2135 FILE_OP_ERROR(dir, "chmod");
2140 gint make_dir_hier(const gchar *dir)
2145 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2146 parent_dir = g_strndup(dir, p - dir);
2147 if (*parent_dir != '\0') {
2148 if (!is_dir_exist(parent_dir)) {
2149 if (make_dir(parent_dir) < 0) {
2158 if (!is_dir_exist(dir)) {
2159 if (make_dir(dir) < 0)
2166 gint remove_all_files(const gchar *dir)
2169 const gchar *dir_name;
2172 prev_dir = g_get_current_dir();
2174 if (g_chdir(dir) < 0) {
2175 FILE_OP_ERROR(dir, "chdir");
2180 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2181 g_warning("failed to open directory: %s\n", dir);
2186 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2187 if (g_unlink(dir_name) < 0)
2188 FILE_OP_ERROR(dir_name, "unlink");
2193 if (g_chdir(prev_dir) < 0) {
2194 FILE_OP_ERROR(prev_dir, "chdir");
2204 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2207 const gchar *dir_name;
2211 prev_dir = g_get_current_dir();
2213 if (g_chdir(dir) < 0) {
2214 FILE_OP_ERROR(dir, "chdir");
2219 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2220 g_warning("failed to open directory: %s\n", dir);
2225 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2226 file_no = to_number(dir_name);
2227 if (file_no > 0 && first <= file_no && file_no <= last) {
2228 if (is_dir_exist(dir_name))
2230 if (g_unlink(dir_name) < 0)
2231 FILE_OP_ERROR(dir_name, "unlink");
2237 if (g_chdir(prev_dir) < 0) {
2238 FILE_OP_ERROR(prev_dir, "chdir");
2248 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2251 const gchar *dir_name;
2255 prev_dir = g_get_current_dir();
2257 if (g_chdir(dir) < 0) {
2258 FILE_OP_ERROR(dir, "chdir");
2263 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2264 FILE_OP_ERROR(dir, "opendir");
2269 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2270 file_no = to_number(dir_name);
2271 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2272 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2273 if (is_dir_exist(dir_name))
2275 if (g_unlink(dir_name) < 0)
2276 FILE_OP_ERROR(dir_name, "unlink");
2282 if (g_chdir(prev_dir) < 0) {
2283 FILE_OP_ERROR(prev_dir, "chdir");
2293 gint remove_all_numbered_files(const gchar *dir)
2295 return remove_numbered_files(dir, 0, UINT_MAX);
2298 gint remove_expired_files(const gchar *dir, guint hours)
2301 const gchar *dir_name;
2305 time_t mtime, now, expire_time;
2307 prev_dir = g_get_current_dir();
2309 if (g_chdir(dir) < 0) {
2310 FILE_OP_ERROR(dir, "chdir");
2315 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2316 g_warning("failed to open directory: %s\n", dir);
2322 expire_time = hours * 60 * 60;
2324 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2325 file_no = to_number(dir_name);
2327 if (g_stat(dir_name, &s) < 0) {
2328 FILE_OP_ERROR(dir_name, "stat");
2331 if (S_ISDIR(s.st_mode))
2333 mtime = MAX(s.st_mtime, s.st_atime);
2334 if (now - mtime > expire_time) {
2335 if (g_unlink(dir_name) < 0)
2336 FILE_OP_ERROR(dir_name, "unlink");
2343 if (g_chdir(prev_dir) < 0) {
2344 FILE_OP_ERROR(prev_dir, "chdir");
2354 gint remove_dir_recursive(const gchar *dir)
2358 const gchar *dir_name;
2361 if (g_stat(dir, &s) < 0) {
2362 FILE_OP_ERROR(dir, "stat");
2363 if (ENOENT == errno) return 0;
2367 if (!S_ISDIR(s.st_mode)) {
2368 if (g_unlink(dir) < 0) {
2369 FILE_OP_ERROR(dir, "unlink");
2376 prev_dir = g_get_current_dir();
2377 /* g_print("prev_dir = %s\n", prev_dir); */
2379 if (!path_cmp(prev_dir, dir)) {
2381 if (g_chdir("..") < 0) {
2382 FILE_OP_ERROR(dir, "chdir");
2385 prev_dir = g_get_current_dir();
2388 if (g_chdir(dir) < 0) {
2389 FILE_OP_ERROR(dir, "chdir");
2394 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2395 g_warning("failed to open directory: %s\n", dir);
2401 /* remove all files in the directory */
2402 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2403 /* g_print("removing %s\n", dir_name); */
2405 if (is_dir_exist(dir_name)) {
2406 if (remove_dir_recursive(dir_name) < 0) {
2407 g_warning("can't remove directory\n");
2411 if (g_unlink(dir_name) < 0)
2412 FILE_OP_ERROR(dir_name, "unlink");
2418 if (g_chdir(prev_dir) < 0) {
2419 FILE_OP_ERROR(prev_dir, "chdir");
2426 if (g_rmdir(dir) < 0) {
2427 FILE_OP_ERROR(dir, "rmdir");
2434 gint rename_force(const gchar *oldpath, const gchar *newpath)
2437 if (!is_file_entry_exist(oldpath)) {
2441 if (is_file_exist(newpath)) {
2442 if (g_unlink(newpath) < 0)
2443 FILE_OP_ERROR(newpath, "unlink");
2446 return g_rename(oldpath, newpath);
2450 /* this seems to be slower than the stdio version... */
2451 gint copy_file(const gchar *src, const gchar *dest)
2453 gint src_fd, dest_fd;
2457 gchar *dest_bak = NULL;
2459 if ((src_fd = open(src, O_RDONLY)) < 0) {
2460 FILE_OP_ERROR(src, "open");
2464 if (is_file_exist(dest)) {
2465 dest_bak = g_strconcat(dest, ".bak", NULL);
2466 if (rename_force(dest, dest_bak) < 0) {
2467 FILE_OP_ERROR(dest, "rename");
2474 if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
2475 FILE_OP_ERROR(dest, "open");
2478 if (rename(dest_bak, dest) < 0)
2479 FILE_OP_ERROR(dest_bak, "rename");
2485 while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
2490 n_write = write(dest_fd, bufp, len);
2492 g_warning("writing to %s failed.\n", dest);
2497 if (rename(dest_bak, dest) < 0)
2498 FILE_OP_ERROR(dest_bak, "rename");
2511 if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
2512 g_warning("File copy from %s to %s failed.\n", src, dest);
2515 if (rename(dest_bak, dest) < 0)
2516 FILE_OP_ERROR(dest_bak, "rename");
2529 * Append src file body to the tail of dest file.
2530 * Now keep_backup has no effects.
2532 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2534 FILE *src_fp, *dest_fp;
2538 gboolean err = FALSE;
2540 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2541 FILE_OP_ERROR(src, "fopen");
2545 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2546 FILE_OP_ERROR(dest, "fopen");
2551 if (change_file_mode_rw(dest_fp, dest) < 0) {
2552 FILE_OP_ERROR(dest, "chmod");
2553 g_warning("can't change file mode\n");
2556 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2557 if (n_read < sizeof(buf) && ferror(src_fp))
2559 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2560 g_warning("writing to %s failed.\n", dest);
2568 if (ferror(src_fp)) {
2569 FILE_OP_ERROR(src, "fread");
2573 if (fclose(dest_fp) == EOF) {
2574 FILE_OP_ERROR(dest, "fclose");
2586 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2588 FILE *src_fp, *dest_fp;
2591 gchar *dest_bak = NULL;
2592 gboolean err = FALSE;
2594 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2595 FILE_OP_ERROR(src, "fopen");
2598 if (is_file_exist(dest)) {
2599 dest_bak = g_strconcat(dest, ".bak", NULL);
2600 if (rename_force(dest, dest_bak) < 0) {
2601 FILE_OP_ERROR(dest, "rename");
2608 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2609 FILE_OP_ERROR(dest, "fopen");
2612 if (rename_force(dest_bak, dest) < 0)
2613 FILE_OP_ERROR(dest_bak, "rename");
2619 if (change_file_mode_rw(dest_fp, dest) < 0) {
2620 FILE_OP_ERROR(dest, "chmod");
2621 g_warning("can't change file mode\n");
2624 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2625 if (n_read < sizeof(buf) && ferror(src_fp))
2627 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2628 g_warning("writing to %s failed.\n", dest);
2633 if (rename_force(dest_bak, dest) < 0)
2634 FILE_OP_ERROR(dest_bak, "rename");
2641 if (ferror(src_fp)) {
2642 FILE_OP_ERROR(src, "fread");
2646 if (fclose(dest_fp) == EOF) {
2647 FILE_OP_ERROR(dest, "fclose");
2654 if (rename_force(dest_bak, dest) < 0)
2655 FILE_OP_ERROR(dest_bak, "rename");
2661 if (keep_backup == FALSE && dest_bak)
2669 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2671 if (overwrite == FALSE && is_file_exist(dest)) {
2672 g_warning("move_file(): file %s already exists.", dest);
2676 if (rename_force(src, dest) == 0) return 0;
2678 if (EXDEV != errno) {
2679 FILE_OP_ERROR(src, "rename");
2683 if (copy_file(src, dest, FALSE) < 0) return -1;
2690 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2693 gint bytes_left, to_read;
2696 if (fseek(fp, offset, SEEK_SET) < 0) {
2701 bytes_left = length;
2702 to_read = MIN(bytes_left, sizeof(buf));
2704 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2705 if (n_read < to_read && ferror(fp))
2707 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2710 bytes_left -= n_read;
2711 if (bytes_left == 0)
2713 to_read = MIN(bytes_left, sizeof(buf));
2724 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2727 gboolean err = FALSE;
2729 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2730 FILE_OP_ERROR(dest, "fopen");
2734 if (change_file_mode_rw(dest_fp, dest) < 0) {
2735 FILE_OP_ERROR(dest, "chmod");
2736 g_warning("can't change file mode\n");
2739 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2742 if (!err && fclose(dest_fp) == EOF) {
2743 FILE_OP_ERROR(dest, "fclose");
2748 g_warning("writing to %s failed.\n", dest);
2756 /* convert line endings into CRLF. If the last line doesn't end with
2757 * linebreak, add it.
2759 gchar *canonicalize_str(const gchar *str)
2765 for (p = str; *p != '\0'; ++p) {
2772 if (p == str || *(p - 1) != '\n')
2775 out = outp = g_malloc(new_len + 1);
2776 for (p = str; *p != '\0'; ++p) {
2783 if (p == str || *(p - 1) != '\n') {
2792 gint canonicalize_file(const gchar *src, const gchar *dest)
2794 FILE *src_fp, *dest_fp;
2795 gchar buf[BUFFSIZE];
2797 gboolean err = FALSE;
2798 gboolean last_linebreak = FALSE;
2800 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2801 FILE_OP_ERROR(src, "fopen");
2805 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2806 FILE_OP_ERROR(dest, "fopen");
2811 if (change_file_mode_rw(dest_fp, dest) < 0) {
2812 FILE_OP_ERROR(dest, "chmod");
2813 g_warning("can't change file mode\n");
2816 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2820 if (len == 0) break;
2821 last_linebreak = FALSE;
2823 if (buf[len - 1] != '\n') {
2824 last_linebreak = TRUE;
2825 r = fputs(buf, dest_fp);
2826 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2827 r = fputs(buf, dest_fp);
2830 r = fwrite(buf, 1, len - 1, dest_fp);
2835 r = fputs("\r\n", dest_fp);
2839 g_warning("writing to %s failed.\n", dest);
2847 if (last_linebreak == TRUE) {
2848 if (fputs("\r\n", dest_fp) == EOF)
2852 if (ferror(src_fp)) {
2853 FILE_OP_ERROR(src, "fgets");
2857 if (fclose(dest_fp) == EOF) {
2858 FILE_OP_ERROR(dest, "fclose");
2870 gint canonicalize_file_replace(const gchar *file)
2874 tmp_file = get_tmp_file();
2876 if (canonicalize_file(file, tmp_file) < 0) {
2881 if (move_file(tmp_file, file, TRUE) < 0) {
2882 g_warning("can't replace %s .\n", file);
2892 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2894 FILE *src_fp, *dest_fp;
2895 gchar buf[BUFFSIZE];
2896 gboolean err = FALSE;
2898 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2899 FILE_OP_ERROR(src, "fopen");
2903 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2904 FILE_OP_ERROR(dest, "fopen");
2909 if (change_file_mode_rw(dest_fp, dest) < 0) {
2910 FILE_OP_ERROR(dest, "chmod");
2911 g_warning("can't change file mode\n");
2914 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2916 if (fputs(buf, dest_fp) == EOF) {
2917 g_warning("writing to %s failed.\n", dest);
2925 if (ferror(src_fp)) {
2926 FILE_OP_ERROR(src, "fgets");
2930 if (fclose(dest_fp) == EOF) {
2931 FILE_OP_ERROR(dest, "fclose");
2943 gint uncanonicalize_file_replace(const gchar *file)
2947 tmp_file = get_tmp_file();
2949 if (uncanonicalize_file(file, tmp_file) < 0) {
2954 if (move_file(tmp_file, file, TRUE) < 0) {
2955 g_warning("can't replace %s .\n", file);
2965 gchar *normalize_newlines(const gchar *str)
2967 const gchar *p = str;
2970 out = outp = g_malloc(strlen(str) + 1);
2971 for (p = str; *p != '\0'; ++p) {
2973 if (*(p + 1) != '\n')
2984 gchar *get_outgoing_rfc2822_str(FILE *fp)
2986 gchar buf[BUFFSIZE];
2990 str = g_string_new(NULL);
2992 /* output header part */
2993 while (fgets(buf, sizeof(buf), fp) != NULL) {
2995 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3002 else if (next != ' ' && next != '\t') {
3006 if (fgets(buf, sizeof(buf), fp) == NULL)
3010 g_string_append(str, buf);
3011 g_string_append(str, "\r\n");
3017 /* output body part */
3018 while (fgets(buf, sizeof(buf), fp) != NULL) {
3021 g_string_append_c(str, '.');
3022 g_string_append(str, buf);
3023 g_string_append(str, "\r\n");
3027 g_string_free(str, FALSE);
3033 * Create a new boundary in a way that it is very unlikely that this
3034 * will occur in the following text. It would be easy to ensure
3035 * uniqueness if everything is either quoted-printable or base64
3036 * encoded (note that conversion is allowed), but because MIME bodies
3037 * may be nested, it may happen that the same boundary has already
3038 * been used. We avoid scanning the message for conflicts and hope the
3041 * boundary := 0*69<bchars> bcharsnospace
3042 * bchars := bcharsnospace / " "
3043 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3044 * "+" / "_" / "," / "-" / "." /
3045 * "/" / ":" / "=" / "?"
3047 * some special characters removed because of buggy MTAs
3050 gchar *generate_mime_boundary(const gchar *prefix)
3052 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3053 "abcdefghijklmnopqrstuvwxyz"
3059 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3060 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3063 get_rfc822_date(buf_date, sizeof(buf_date));
3064 subst_char(buf_date, ' ', '_');
3065 subst_char(buf_date, ',', '_');
3066 subst_char(buf_date, ':', '_');
3068 return g_strdup_printf("%s_%s_%s", prefix ? prefix : "Multipart",
3069 buf_date, buf_uniq);
3072 gint change_file_mode_rw(FILE *fp, const gchar *file)
3075 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3077 return g_chmod(file, S_IRUSR|S_IWUSR);
3081 FILE *my_tmpfile(void)
3084 const gchar suffix[] = ".XXXXXX";
3085 const gchar *tmpdir;
3087 const gchar *progname;
3093 tmpdir = get_tmp_dir();
3094 tmplen = strlen(tmpdir);
3095 progname = g_get_prgname();
3096 if (progname == NULL)
3097 progname = "sylpheed-claws";
3098 proglen = strlen(progname);
3099 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3102 memcpy(fname, tmpdir, tmplen);
3103 fname[tmplen] = G_DIR_SEPARATOR;
3104 memcpy(fname + tmplen + 1, progname, proglen);
3105 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3107 fd = mkstemp(fname);
3113 fp = fdopen(fd, "w+b");
3118 #endif /* HAVE_MKSTEMP */
3123 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3127 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3128 fd = mkstemp(*filename);
3130 return fdopen(fd, "w+");
3133 FILE *str_open_as_stream(const gchar *str)
3138 g_return_val_if_fail(str != NULL, NULL);
3142 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3147 if (len == 0) return fp;
3149 if (fwrite(str, 1, len, fp) != len) {
3150 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3159 gint str_write_to_file(const gchar *str, const gchar *file)
3164 g_return_val_if_fail(str != NULL, -1);
3165 g_return_val_if_fail(file != NULL, -1);
3167 if ((fp = g_fopen(file, "wb")) == NULL) {
3168 FILE_OP_ERROR(file, "fopen");
3178 if (fwrite(str, 1, len, fp) != len) {
3179 FILE_OP_ERROR(file, "fwrite");
3185 if (fclose(fp) == EOF) {
3186 FILE_OP_ERROR(file, "fclose");
3194 gchar *file_read_to_str(const gchar *file)
3199 g_return_val_if_fail(file != NULL, NULL);
3201 if ((fp = g_fopen(file, "rb")) == NULL) {
3202 FILE_OP_ERROR(file, "fopen");
3206 str = file_read_stream_to_str(fp);
3213 gchar *file_read_stream_to_str(FILE *fp)
3220 g_return_val_if_fail(fp != NULL, NULL);
3222 array = g_byte_array_new();
3224 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3225 if (n_read < sizeof(buf) && ferror(fp))
3227 g_byte_array_append(array, buf, n_read);
3231 FILE_OP_ERROR("file stream", "fread");
3232 g_byte_array_free(array, TRUE);
3237 g_byte_array_append(array, buf, 1);
3238 str = (gchar *)array->data;
3239 g_byte_array_free(array, FALSE);
3241 if (!g_utf8_validate(str, -1, NULL)) {
3242 const gchar *src_codeset, *dest_codeset;
3244 src_codeset = conv_get_locale_charset_str();
3245 dest_codeset = CS_UTF_8;
3246 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3254 gint execute_async(gchar *const argv[])
3256 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3258 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3259 NULL, NULL, NULL, FALSE) == FALSE) {
3260 g_warning("Can't execute command: %s\n", argv[0]);
3267 gint execute_sync(gchar *const argv[])
3271 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3273 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3274 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3275 g_warning("Can't execute command: %s\n", argv[0]);
3280 if (WIFEXITED(status))
3281 return WEXITSTATUS(status);
3289 gint execute_command_line(const gchar *cmdline, gboolean async)
3294 debug_print("execute_command_line(): executing: %s\n", cmdline);
3296 argv = strsplit_with_quote(cmdline, " ", 0);
3299 ret = execute_async(argv);
3301 ret = execute_sync(argv);
3308 gchar *get_command_output(const gchar *cmdline)
3310 gchar *child_stdout;
3313 g_return_val_if_fail(cmdline != NULL, NULL);
3315 debug_print("get_command_output(): executing: %s\n", cmdline);
3317 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3319 g_warning("Can't execute command: %s\n", cmdline);
3323 return child_stdout;
3326 static gint is_unchanged_uri_char(char c)
3338 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3344 for(i = 0; i < strlen(uri) ; i++) {
3345 if (is_unchanged_uri_char(uri[i])) {
3346 if (k + 2 >= bufsize)
3348 encoded_uri[k++] = uri[i];
3351 char * hexa = "0123456789ABCDEF";
3353 if (k + 4 >= bufsize)
3355 encoded_uri[k++] = '%';
3356 encoded_uri[k++] = hexa[uri[i] / 16];
3357 encoded_uri[k++] = hexa[uri[i] % 16];
3363 gint open_uri(const gchar *uri, const gchar *cmdline)
3365 gchar buf[BUFFSIZE];
3367 gchar encoded_uri[BUFFSIZE];
3369 g_return_val_if_fail(uri != NULL, -1);
3371 /* an option to choose whether to use encode_uri or not ? */
3372 encode_uri(encoded_uri, BUFFSIZE, uri);
3375 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3376 !strchr(p + 2, '%'))
3377 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3380 g_warning("Open URI command line is invalid "
3381 "(there must be only one '%%s'): %s",
3383 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3386 execute_command_line(buf, TRUE);
3391 time_t remote_tzoffset_sec(const gchar *zone)
3393 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3399 time_t remoteoffset;
3401 strncpy(zone3, zone, 3);
3405 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3406 (c == '+' || c == '-')) {
3407 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3409 remoteoffset = -remoteoffset;
3410 } else if (!strncmp(zone, "UT" , 2) ||
3411 !strncmp(zone, "GMT", 2)) {
3413 } else if (strlen(zone3) == 3) {
3414 for (p = ustzstr; *p != '\0'; p += 3) {
3415 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3416 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3417 remoteoffset = iustz * 3600;
3423 } else if (strlen(zone3) == 1) {
3425 case 'Z': remoteoffset = 0; break;
3426 case 'A': remoteoffset = -1; break;
3427 case 'B': remoteoffset = -2; break;
3428 case 'C': remoteoffset = -3; break;
3429 case 'D': remoteoffset = -4; break;
3430 case 'E': remoteoffset = -5; break;
3431 case 'F': remoteoffset = -6; break;
3432 case 'G': remoteoffset = -7; break;
3433 case 'H': remoteoffset = -8; break;
3434 case 'I': remoteoffset = -9; break;
3435 case 'K': remoteoffset = -10; break; /* J is not used */
3436 case 'L': remoteoffset = -11; break;
3437 case 'M': remoteoffset = -12; break;
3438 case 'N': remoteoffset = 1; break;
3439 case 'O': remoteoffset = 2; break;
3440 case 'P': remoteoffset = 3; break;
3441 case 'Q': remoteoffset = 4; break;
3442 case 'R': remoteoffset = 5; break;
3443 case 'S': remoteoffset = 6; break;
3444 case 'T': remoteoffset = 7; break;
3445 case 'U': remoteoffset = 8; break;
3446 case 'V': remoteoffset = 9; break;
3447 case 'W': remoteoffset = 10; break;
3448 case 'X': remoteoffset = 11; break;
3449 case 'Y': remoteoffset = 12; break;
3450 default: remoteoffset = 0; break;
3452 remoteoffset = remoteoffset * 3600;
3456 return remoteoffset;
3459 time_t tzoffset_sec(time_t *now)
3465 lt = localtime(now);
3467 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3469 if (lt->tm_year < gmt.tm_year)
3471 else if (lt->tm_year > gmt.tm_year)
3473 else if (lt->tm_yday < gmt.tm_yday)
3475 else if (lt->tm_yday > gmt.tm_yday)
3478 if (off >= 24 * 60) /* should be impossible */
3479 off = 23 * 60 + 59; /* if not, insert silly value */
3480 if (off <= -24 * 60)
3481 off = -(23 * 60 + 59);
3486 /* calculate timezone offset */
3487 gchar *tzoffset(time_t *now)
3489 static gchar offset_string[6];
3495 lt = localtime(now);
3497 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3499 if (lt->tm_year < gmt.tm_year)
3501 else if (lt->tm_year > gmt.tm_year)
3503 else if (lt->tm_yday < gmt.tm_yday)
3505 else if (lt->tm_yday > gmt.tm_yday)
3513 if (off >= 24 * 60) /* should be impossible */
3514 off = 23 * 60 + 59; /* if not, insert silly value */
3516 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3518 return offset_string;
3521 void get_rfc822_date(gchar *buf, gint len)
3525 gchar day[4], mon[4];
3526 gint dd, hh, mm, ss, yyyy;
3531 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3532 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3533 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3534 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3537 /* just a wrapper to suppress the warning of gcc about %c */
3538 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3539 const struct tm *tm)
3541 return strftime(s, max, format, tm);
3544 void debug_set_mode(gboolean mode)
3549 gboolean debug_get_mode(void)
3554 void debug_print_real(const gchar *format, ...)
3557 gchar buf[BUFFSIZE];
3559 if (!debug_mode) return;
3561 va_start(args, format);
3562 g_vsnprintf(buf, sizeof(buf), format, args);
3568 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3570 if (subject == NULL)
3573 subject += subject_get_prefix_length(subject);
3575 return g_hash_table_lookup(subject_table, subject);
3578 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3581 if (subject == NULL || *subject == 0)
3583 subject += subject_get_prefix_length(subject);
3584 g_hash_table_insert(subject_table, subject, data);
3587 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3589 if (subject == NULL)
3592 subject += subject_get_prefix_length(subject);
3593 g_hash_table_remove(subject_table, subject);
3597 *\brief Check if a string is prefixed with known (combinations)
3598 * of prefixes. The function assumes that each prefix
3599 * is terminated by zero or exactly _one_ space.
3601 *\param str String to check for a prefixes
3603 *\return int Number of chars in the prefix that should be skipped
3604 * for a "clean" subject line. If no prefix was found, 0
3607 int subject_get_prefix_length(const gchar *subject)
3609 /*!< Array with allowable reply prefixes regexps. */
3610 static const gchar * const prefixes[] = {
3611 "Re\\:", /* "Re:" */
3612 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3613 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3614 "Aw\\:", /* "Aw:" (German) */
3615 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3616 "Res\\:", /* "Res:" (Brazilian Outlook) */
3617 "Fw\\:", /* "Fw:" Forward */
3618 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3619 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3620 "Rif\\:" /* "Rif:" (Italian Outlook) */
3623 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3626 static regex_t regex;
3627 static gboolean init_;
3629 if (!subject) return 0;
3630 if (!*subject) return 0;
3633 GString *s = g_string_new("");
3635 for (n = 0; n < PREFIXES; n++)
3636 /* Terminate each prefix regexpression by a
3637 * "\ ?" (zero or ONE space), and OR them */
3638 g_string_append_printf(s, "(%s\\ ?)%s",
3643 g_string_prepend(s, "(");
3644 g_string_append(s, ")+"); /* match at least once */
3645 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3648 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3649 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3650 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3651 debug_print("Error compiling regexp %s\n", s->str);
3652 g_string_free(s, TRUE);
3656 g_string_free(s, TRUE);
3660 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3666 guint g_stricase_hash(gconstpointer gptr)
3668 guint hash_result = 0;
3671 for (str = gptr; str && *str; str++) {
3672 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3673 else hash_result += *str;
3679 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3681 const char *str1 = gptr1;
3682 const char *str2 = gptr2;
3684 return !g_utf8_collate(str1, str2);
3687 gint g_int_compare(gconstpointer a, gconstpointer b)
3689 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3692 gchar *generate_msgid(gchar *buf, gint len)
3701 addr = g_strconcat("@", get_domain_name(), NULL);
3703 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3704 lt->tm_year + 1900, lt->tm_mon + 1,
3705 lt->tm_mday, lt->tm_hour,
3706 lt->tm_min, lt->tm_sec,
3707 (guint) rand(), addr);
3714 quote_cmd_argument()
3716 return a quoted string safely usable in argument of a command.
3718 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3721 gint quote_cmd_argument(gchar * result, guint size,
3731 for(p = path ; * p != '\0' ; p ++) {
3733 if (isalnum((guchar)*p) || (* p == '/')) {
3734 if (remaining > 0) {
3740 result[size - 1] = '\0';
3745 if (remaining >= 2) {
3753 result[size - 1] = '\0';
3758 if (remaining > 0) {
3762 result[size - 1] = '\0';
3776 static void g_node_map_recursive(GNode *node, gpointer data)
3778 GNodeMapData *mapdata = (GNodeMapData *) data;
3780 GNodeMapData newmapdata;
3783 newdata = mapdata->func(node->data, mapdata->data);
3784 if (newdata != NULL) {
3785 newnode = g_node_new(newdata);
3786 g_node_append(mapdata->parent, newnode);
3788 newmapdata.parent = newnode;
3789 newmapdata.func = mapdata->func;
3790 newmapdata.data = mapdata->data;
3792 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3796 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3799 GNodeMapData mapdata;
3801 g_return_val_if_fail(node != NULL, NULL);
3802 g_return_val_if_fail(func != NULL, NULL);
3804 root = g_node_new(func(node->data, data));
3806 mapdata.parent = root;
3807 mapdata.func = func;
3808 mapdata.data = data;
3810 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3815 #define HEX_TO_INT(val, hex) \
3819 if ('0' <= c && c <= '9') { \
3821 } else if ('a' <= c && c <= 'f') { \
3822 val = c - 'a' + 10; \
3823 } else if ('A' <= c && c <= 'F') { \
3824 val = c - 'A' + 10; \
3830 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3837 if (hi == -1 || lo == -1)
3840 *out = (hi << 4) + lo;
3844 #define INT_TO_HEX(hex, val) \
3847 hex = '0' + (val); \
3849 hex = 'A' + (val) - 10; \
3852 void get_hex_str(gchar *out, guchar ch)
3856 INT_TO_HEX(hex, ch >> 4);
3858 INT_TO_HEX(hex, ch & 0x0f);
3864 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3866 #define G_PRINT_REF g_print
3870 *\brief Register ref counted pointer. It is based on GBoxed, so should
3871 * work with anything that uses the GType system. The semantics
3872 * are similar to a C++ auto pointer, with the exception that
3873 * C doesn't have automatic closure (calling destructors) when
3874 * exiting a block scope.
3875 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3876 * function directly.
3878 *\return GType A GType type.
3880 GType g_auto_pointer_register(void)
3882 static GType auto_pointer_type;
3883 if (!auto_pointer_type)
3885 g_boxed_type_register_static
3886 ("G_TYPE_AUTO_POINTER",
3887 (GBoxedCopyFunc) g_auto_pointer_copy,
3888 (GBoxedFreeFunc) g_auto_pointer_free);
3889 return auto_pointer_type;
3893 *\brief Structure with g_new() allocated pointer guarded by the
3896 typedef struct AutoPointerRef {
3897 void (*free) (gpointer);
3903 *\brief The auto pointer opaque structure that references the
3904 * pointer guard block.
3906 typedef struct AutoPointer {
3907 AutoPointerRef *ref;
3908 gpointer ptr; /*!< access to protected pointer */
3912 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3916 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3917 * ... when assigning, copying and freeing storage elements
3919 * gtk_list_store_new(N_S_COLUMNS,
3920 * G_TYPE_AUTO_POINTER,
3924 * Template *precious_data = g_new0(Template, 1);
3925 * g_pointer protect = g_auto_pointer_new(precious_data);
3927 * gtk_list_store_set(container, &iter,
3931 * ... the gtk_list_store has copied the pointer and
3932 * ... incremented its reference count, we should free
3933 * ... the auto pointer (in C++ a destructor would do
3934 * ... this for us when leaving block scope)
3936 * g_auto_pointer_free(protect);
3938 * ... gtk_list_store_set() now manages the data. When
3939 * ... *explicitly* requesting a pointer from the list
3940 * ... store, don't forget you get a copy that should be
3941 * ... freed with g_auto_pointer_free() eventually.
3945 *\param pointer Pointer to be guarded.
3947 *\return GAuto * Pointer that should be used in containers with
3950 GAuto *g_auto_pointer_new(gpointer p)
3952 AutoPointerRef *ref;
3958 ref = g_new0(AutoPointerRef, 1);
3959 ptr = g_new0(AutoPointer, 1);
3969 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3975 *\brief Allocate an autopointer using the passed \a free function to
3976 * free the guarded pointer
3978 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3985 aptr = g_auto_pointer_new(p);
3986 aptr->ref->free = free_;
3990 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3992 if (auto_ptr == NULL)
3994 return ((AutoPointer *) auto_ptr)->ptr;
3998 *\brief Copies an auto pointer by. It's mostly not necessary
3999 * to call this function directly, unless you copy/assign
4000 * the guarded pointer.
4002 *\param auto_ptr Auto pointer returned by previous call to
4003 * g_auto_pointer_new_XXX()
4005 *\return gpointer An auto pointer
4007 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4010 AutoPointerRef *ref;
4013 if (auto_ptr == NULL)
4018 newp = g_new0(AutoPointer, 1);
4021 newp->ptr = ref->pointer;
4025 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4031 *\brief Free an auto pointer
4033 void g_auto_pointer_free(GAuto *auto_ptr)
4036 AutoPointerRef *ref;
4038 if (auto_ptr == NULL)
4044 if (--(ref->cnt) == 0) {
4046 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4048 ref->free(ref->pointer);
4053 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4058 void replace_returns(gchar *str)
4063 while (strstr(str, "\n")) {
4064 *strstr(str, "\n") = ' ';
4066 while (strstr(str, "\r")) {
4067 *strstr(str, "\r") = ' ';
4071 /* get_uri_part() - retrieves a URI starting from scanpos.
4072 Returns TRUE if succesful */
4073 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4074 const gchar **bp, const gchar **ep)
4078 g_return_val_if_fail(start != NULL, FALSE);
4079 g_return_val_if_fail(scanpos != NULL, FALSE);
4080 g_return_val_if_fail(bp != NULL, FALSE);
4081 g_return_val_if_fail(ep != NULL, FALSE);
4085 /* find end point of URI */
4086 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4087 if (!isgraph(*(const guchar *)ep_) ||
4088 !IS_ASCII(*(const guchar *)ep_) ||
4089 strchr("[]{}()<>\"", *ep_))
4093 /* no punctuation at end of string */
4095 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4096 * should pass some URI type to this function and decide on that whether
4097 * to perform punctuation stripping */
4099 #define IS_REAL_PUNCT(ch) (ispunct(ch) && ((ch) != '/'))
4101 for (; ep_ - 1 > scanpos + 1 &&
4102 IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
4106 #undef IS_REAL_PUNCT
4113 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4115 return g_strndup(bp, ep - bp);
4118 /* valid mail address characters */
4119 #define IS_RFC822_CHAR(ch) \
4123 !g_ascii_isspace(ch) && \
4124 !strchr("(),;<>\"", (ch)))
4126 /* alphabet and number within 7bit ASCII */
4127 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4128 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4130 static GHashTable *create_domain_tab(void)
4132 static const gchar *toplvl_domains [] = {
4134 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4135 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4136 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4137 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4138 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4139 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4140 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4141 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4142 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4143 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4144 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4145 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4146 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4147 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4148 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4149 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4150 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4151 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4152 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4153 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4154 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4155 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4156 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4157 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4158 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4159 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4162 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4164 g_return_val_if_fail(htab, NULL);
4165 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4166 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4170 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4172 const gint MAX_LVL_DOM_NAME_LEN = 6;
4173 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4174 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4177 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4180 for (p = buf; p < m && first < last; *p++ = *first++)
4184 return g_hash_table_lookup(tab, buf) != NULL;
4187 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4188 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4189 const gchar **bp, const gchar **ep)
4191 /* more complex than the uri part because we need to scan back and forward starting from
4192 * the scan position. */
4193 gboolean result = FALSE;
4194 const gchar *bp_ = NULL;
4195 const gchar *ep_ = NULL;
4196 static GHashTable *dom_tab;
4197 const gchar *last_dot = NULL;
4198 const gchar *prelast_dot = NULL;
4199 const gchar *last_tld_char = NULL;
4201 /* the informative part of the email address (describing the name
4202 * of the email address owner) may contain quoted parts. the
4203 * closure stack stores the last encountered quotes. */
4204 gchar closure_stack[128];
4205 gchar *ptr = closure_stack;
4207 g_return_val_if_fail(start != NULL, FALSE);
4208 g_return_val_if_fail(scanpos != NULL, FALSE);
4209 g_return_val_if_fail(bp != NULL, FALSE);
4210 g_return_val_if_fail(ep != NULL, FALSE);
4213 dom_tab = create_domain_tab();
4214 g_return_val_if_fail(dom_tab, FALSE);
4216 /* scan start of address */
4217 for (bp_ = scanpos - 1;
4218 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4221 /* TODO: should start with an alnum? */
4223 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4226 if (bp_ != scanpos) {
4227 /* scan end of address */
4228 for (ep_ = scanpos + 1;
4229 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4231 prelast_dot = last_dot;
4233 if (*(last_dot + 1) == '.') {
4234 if (prelast_dot == NULL)
4236 last_dot = prelast_dot;
4241 /* TODO: really should terminate with an alnum? */
4242 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4247 if (last_dot == NULL)
4249 if (last_dot >= ep_)
4250 last_dot = prelast_dot;
4251 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4255 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4256 if (*last_tld_char == '?')
4259 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4266 if (!result) return FALSE;
4268 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4269 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4270 && IS_RFC822_CHAR(*(ep_ + 3))) {
4271 /* this informative part with an @ in it is
4272 * followed by the email address */
4275 /* go to matching '>' (or next non-rfc822 char, like \n) */
4276 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4279 /* include the bracket */
4280 if (*ep_ == '>') ep_++;
4282 /* include the leading quote */
4290 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4291 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4294 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4295 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4298 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4299 #define IN_STACK() (ptr > closure_stack)
4300 /* has underrun check */
4301 #define POP_STACK() if(IN_STACK()) --ptr
4302 /* has overrun check */
4303 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4304 /* has underrun check */
4305 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4309 /* scan for the informative part. */
4310 for (bp_ -= 2; bp_ >= start; bp_--) {
4311 /* if closure on the stack keep scanning */
4312 if (PEEK_STACK() == *bp_) {
4316 if (*bp_ == '\'' || *bp_ == '"') {
4321 /* if nothing in the closure stack, do the special conditions
4322 * the following if..else expression simply checks whether
4323 * a token is acceptable. if not acceptable, the clause
4324 * should terminate the loop with a 'break' */
4325 if (!PEEK_STACK()) {
4327 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4328 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4329 /* hyphens are allowed, but only in
4331 } else if (!strchr(",;:=?./+<>!&\r\n\t", *bp_)) {
4332 /* but anything not being a punctiation
4335 break; /* anything else is rejected */
4348 /* scan forward (should start with an alnum) */
4349 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4359 #undef IS_ASCII_ALNUM
4360 #undef IS_RFC822_CHAR
4362 gchar *make_email_string(const gchar *bp, const gchar *ep)
4364 /* returns a mailto: URI; mailto: is also used to detect the
4365 * uri type later on in the button_pressed signal handler */
4369 tmp = g_strndup(bp, ep - bp);
4370 result = g_strconcat("mailto:", tmp, NULL);
4376 gchar *make_http_string(const gchar *bp, const gchar *ep)
4378 /* returns an http: URI; */
4382 tmp = g_strndup(bp, ep - bp);
4383 result = g_strconcat("http://", tmp, NULL);
4389 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4391 FILE *fp = fopen(path, "rb");
4392 gchar buf[BUFFSIZE];
4393 gchar *result = NULL;
4396 while (fgets(buf, sizeof (buf), fp) != NULL) {
4397 gchar **parts = g_strsplit(buf, ";", -1);
4398 gchar *trimmed = parts[0];
4399 while (trimmed[0] == ' ')
4401 while (trimmed[strlen(trimmed)-1] == ' ')
4402 trimmed[strlen(trimmed)-1] = '\0';
4404 if (!strcmp(trimmed, type)) {
4406 while (trimmed[0] == ' ')
4408 while (trimmed[strlen(trimmed)-1] == ' ')
4409 trimmed[strlen(trimmed)-1] = '\0';
4410 while (trimmed[strlen(trimmed)-1] == '\n')
4411 trimmed[strlen(trimmed)-1] = '\0';
4412 while (trimmed[strlen(trimmed)-1] == '\r')
4413 trimmed[strlen(trimmed)-1] = '\0';
4414 result = g_strdup(trimmed);
4417 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4418 gchar *start = g_strdup(result);
4419 gchar *end = g_strdup(strstr(result, "%s")+2);
4421 *strstr(start, "%s") = '\0';
4422 tmp = g_strconcat(start,"'%s'",end, NULL);
4435 gchar *mailcap_get_command_for_type(const gchar *type)
4437 gchar *result = NULL;
4439 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4440 result = mailcap_get_command_in_file(path, type);
4444 result = mailcap_get_command_in_file("/etc/mailcap", type);