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
3040 * boundary := 0*69<bchars> bcharsnospace
3041 * bchars := bcharsnospace / " "
3042 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3043 * "+" / "_" / "," / "-" / "." /
3044 * "/" / ":" / "=" / "?"
3046 * some special characters removed because of buggy MTAs
3049 gchar *generate_mime_boundary(const gchar *prefix)
3051 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3052 "abcdefghijklmnopqrstuvwxyz"
3057 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3058 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3061 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3065 gint change_file_mode_rw(FILE *fp, const gchar *file)
3068 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3070 return g_chmod(file, S_IRUSR|S_IWUSR);
3074 FILE *my_tmpfile(void)
3077 const gchar suffix[] = ".XXXXXX";
3078 const gchar *tmpdir;
3080 const gchar *progname;
3086 tmpdir = get_tmp_dir();
3087 tmplen = strlen(tmpdir);
3088 progname = g_get_prgname();
3089 if (progname == NULL)
3090 progname = "sylpheed-claws";
3091 proglen = strlen(progname);
3092 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3095 memcpy(fname, tmpdir, tmplen);
3096 fname[tmplen] = G_DIR_SEPARATOR;
3097 memcpy(fname + tmplen + 1, progname, proglen);
3098 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3100 fd = mkstemp(fname);
3106 fp = fdopen(fd, "w+b");
3111 #endif /* HAVE_MKSTEMP */
3116 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3120 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3121 fd = mkstemp(*filename);
3123 return fdopen(fd, "w+");
3126 FILE *str_open_as_stream(const gchar *str)
3131 g_return_val_if_fail(str != NULL, NULL);
3135 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3140 if (len == 0) return fp;
3142 if (fwrite(str, 1, len, fp) != len) {
3143 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3152 gint str_write_to_file(const gchar *str, const gchar *file)
3157 g_return_val_if_fail(str != NULL, -1);
3158 g_return_val_if_fail(file != NULL, -1);
3160 if ((fp = g_fopen(file, "wb")) == NULL) {
3161 FILE_OP_ERROR(file, "fopen");
3171 if (fwrite(str, 1, len, fp) != len) {
3172 FILE_OP_ERROR(file, "fwrite");
3178 if (fclose(fp) == EOF) {
3179 FILE_OP_ERROR(file, "fclose");
3187 gchar *file_read_to_str(const gchar *file)
3192 g_return_val_if_fail(file != NULL, NULL);
3194 if ((fp = g_fopen(file, "rb")) == NULL) {
3195 FILE_OP_ERROR(file, "fopen");
3199 str = file_read_stream_to_str(fp);
3206 gchar *file_read_stream_to_str(FILE *fp)
3213 g_return_val_if_fail(fp != NULL, NULL);
3215 array = g_byte_array_new();
3217 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3218 if (n_read < sizeof(buf) && ferror(fp))
3220 g_byte_array_append(array, buf, n_read);
3224 FILE_OP_ERROR("file stream", "fread");
3225 g_byte_array_free(array, TRUE);
3230 g_byte_array_append(array, buf, 1);
3231 str = (gchar *)array->data;
3232 g_byte_array_free(array, FALSE);
3234 if (!g_utf8_validate(str, -1, NULL)) {
3235 const gchar *src_codeset, *dest_codeset;
3237 src_codeset = conv_get_locale_charset_str();
3238 dest_codeset = CS_UTF_8;
3239 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3247 gint execute_async(gchar *const argv[])
3249 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3251 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3252 NULL, NULL, NULL, FALSE) == FALSE) {
3253 g_warning("Can't execute command: %s\n", argv[0]);
3260 gint execute_sync(gchar *const argv[])
3264 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3266 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3267 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3268 g_warning("Can't execute command: %s\n", argv[0]);
3273 if (WIFEXITED(status))
3274 return WEXITSTATUS(status);
3282 gint execute_command_line(const gchar *cmdline, gboolean async)
3287 debug_print("execute_command_line(): executing: %s\n", cmdline);
3289 argv = strsplit_with_quote(cmdline, " ", 0);
3292 ret = execute_async(argv);
3294 ret = execute_sync(argv);
3301 gchar *get_command_output(const gchar *cmdline)
3303 gchar *child_stdout;
3306 g_return_val_if_fail(cmdline != NULL, NULL);
3308 debug_print("get_command_output(): executing: %s\n", cmdline);
3310 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3312 g_warning("Can't execute command: %s\n", cmdline);
3316 return child_stdout;
3319 static gint is_unchanged_uri_char(char c)
3331 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3337 for(i = 0; i < strlen(uri) ; i++) {
3338 if (is_unchanged_uri_char(uri[i])) {
3339 if (k + 2 >= bufsize)
3341 encoded_uri[k++] = uri[i];
3344 char * hexa = "0123456789ABCDEF";
3346 if (k + 4 >= bufsize)
3348 encoded_uri[k++] = '%';
3349 encoded_uri[k++] = hexa[uri[i] / 16];
3350 encoded_uri[k++] = hexa[uri[i] % 16];
3356 gint open_uri(const gchar *uri, const gchar *cmdline)
3358 gchar buf[BUFFSIZE];
3360 gchar encoded_uri[BUFFSIZE];
3362 g_return_val_if_fail(uri != NULL, -1);
3364 /* an option to choose whether to use encode_uri or not ? */
3365 encode_uri(encoded_uri, BUFFSIZE, uri);
3368 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3369 !strchr(p + 2, '%'))
3370 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3373 g_warning("Open URI command line is invalid "
3374 "(there must be only one '%%s'): %s",
3376 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3379 execute_command_line(buf, TRUE);
3384 time_t remote_tzoffset_sec(const gchar *zone)
3386 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3392 time_t remoteoffset;
3394 strncpy(zone3, zone, 3);
3398 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3399 (c == '+' || c == '-')) {
3400 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3402 remoteoffset = -remoteoffset;
3403 } else if (!strncmp(zone, "UT" , 2) ||
3404 !strncmp(zone, "GMT", 2)) {
3406 } else if (strlen(zone3) == 3) {
3407 for (p = ustzstr; *p != '\0'; p += 3) {
3408 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3409 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3410 remoteoffset = iustz * 3600;
3416 } else if (strlen(zone3) == 1) {
3418 case 'Z': remoteoffset = 0; break;
3419 case 'A': remoteoffset = -1; break;
3420 case 'B': remoteoffset = -2; break;
3421 case 'C': remoteoffset = -3; break;
3422 case 'D': remoteoffset = -4; break;
3423 case 'E': remoteoffset = -5; break;
3424 case 'F': remoteoffset = -6; break;
3425 case 'G': remoteoffset = -7; break;
3426 case 'H': remoteoffset = -8; break;
3427 case 'I': remoteoffset = -9; break;
3428 case 'K': remoteoffset = -10; break; /* J is not used */
3429 case 'L': remoteoffset = -11; break;
3430 case 'M': remoteoffset = -12; break;
3431 case 'N': remoteoffset = 1; break;
3432 case 'O': remoteoffset = 2; break;
3433 case 'P': remoteoffset = 3; break;
3434 case 'Q': remoteoffset = 4; break;
3435 case 'R': remoteoffset = 5; break;
3436 case 'S': remoteoffset = 6; break;
3437 case 'T': remoteoffset = 7; break;
3438 case 'U': remoteoffset = 8; break;
3439 case 'V': remoteoffset = 9; break;
3440 case 'W': remoteoffset = 10; break;
3441 case 'X': remoteoffset = 11; break;
3442 case 'Y': remoteoffset = 12; break;
3443 default: remoteoffset = 0; break;
3445 remoteoffset = remoteoffset * 3600;
3449 return remoteoffset;
3452 time_t tzoffset_sec(time_t *now)
3458 lt = localtime(now);
3460 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3462 if (lt->tm_year < gmt.tm_year)
3464 else if (lt->tm_year > gmt.tm_year)
3466 else if (lt->tm_yday < gmt.tm_yday)
3468 else if (lt->tm_yday > gmt.tm_yday)
3471 if (off >= 24 * 60) /* should be impossible */
3472 off = 23 * 60 + 59; /* if not, insert silly value */
3473 if (off <= -24 * 60)
3474 off = -(23 * 60 + 59);
3479 /* calculate timezone offset */
3480 gchar *tzoffset(time_t *now)
3482 static gchar offset_string[6];
3488 lt = localtime(now);
3490 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3492 if (lt->tm_year < gmt.tm_year)
3494 else if (lt->tm_year > gmt.tm_year)
3496 else if (lt->tm_yday < gmt.tm_yday)
3498 else if (lt->tm_yday > gmt.tm_yday)
3506 if (off >= 24 * 60) /* should be impossible */
3507 off = 23 * 60 + 59; /* if not, insert silly value */
3509 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3511 return offset_string;
3514 void get_rfc822_date(gchar *buf, gint len)
3518 gchar day[4], mon[4];
3519 gint dd, hh, mm, ss, yyyy;
3524 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3525 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3526 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3527 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3530 /* just a wrapper to suppress the warning of gcc about %c */
3531 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3532 const struct tm *tm)
3534 return strftime(s, max, format, tm);
3537 void debug_set_mode(gboolean mode)
3542 gboolean debug_get_mode(void)
3547 void debug_print_real(const gchar *format, ...)
3550 gchar buf[BUFFSIZE];
3552 if (!debug_mode) return;
3554 va_start(args, format);
3555 g_vsnprintf(buf, sizeof(buf), format, args);
3561 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3563 if (subject == NULL)
3566 subject += subject_get_prefix_length(subject);
3568 return g_hash_table_lookup(subject_table, subject);
3571 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3574 if (subject == NULL || *subject == 0)
3576 subject += subject_get_prefix_length(subject);
3577 g_hash_table_insert(subject_table, subject, data);
3580 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3582 if (subject == NULL)
3585 subject += subject_get_prefix_length(subject);
3586 g_hash_table_remove(subject_table, subject);
3590 *\brief Check if a string is prefixed with known (combinations)
3591 * of prefixes. The function assumes that each prefix
3592 * is terminated by zero or exactly _one_ space.
3594 *\param str String to check for a prefixes
3596 *\return int Number of chars in the prefix that should be skipped
3597 * for a "clean" subject line. If no prefix was found, 0
3600 int subject_get_prefix_length(const gchar *subject)
3602 /*!< Array with allowable reply prefixes regexps. */
3603 static const gchar * const prefixes[] = {
3604 "Re\\:", /* "Re:" */
3605 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3606 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3607 "Aw\\:", /* "Aw:" (German) */
3608 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3609 "Res\\:", /* "Res:" (Brazilian Outlook) */
3610 "Fw\\:", /* "Fw:" Forward */
3611 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3612 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3613 "Rif\\:" /* "Rif:" (Italian Outlook) */
3616 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3619 static regex_t regex;
3620 static gboolean init_;
3622 if (!subject) return 0;
3623 if (!*subject) return 0;
3626 GString *s = g_string_new("");
3628 for (n = 0; n < PREFIXES; n++)
3629 /* Terminate each prefix regexpression by a
3630 * "\ ?" (zero or ONE space), and OR them */
3631 g_string_append_printf(s, "(%s\\ ?)%s",
3636 g_string_prepend(s, "(");
3637 g_string_append(s, ")+"); /* match at least once */
3638 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3641 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3642 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3643 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3644 debug_print("Error compiling regexp %s\n", s->str);
3645 g_string_free(s, TRUE);
3649 g_string_free(s, TRUE);
3653 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3659 guint g_stricase_hash(gconstpointer gptr)
3661 guint hash_result = 0;
3664 for (str = gptr; str && *str; str++) {
3665 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3666 else hash_result += *str;
3672 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3674 const char *str1 = gptr1;
3675 const char *str2 = gptr2;
3677 return !g_utf8_collate(str1, str2);
3680 gint g_int_compare(gconstpointer a, gconstpointer b)
3682 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3685 gchar *generate_msgid(gchar *buf, gint len)
3694 addr = g_strconcat("@", get_domain_name(), NULL);
3696 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3697 lt->tm_year + 1900, lt->tm_mon + 1,
3698 lt->tm_mday, lt->tm_hour,
3699 lt->tm_min, lt->tm_sec,
3700 (guint) rand(), addr);
3707 quote_cmd_argument()
3709 return a quoted string safely usable in argument of a command.
3711 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3714 gint quote_cmd_argument(gchar * result, guint size,
3724 for(p = path ; * p != '\0' ; p ++) {
3726 if (isalnum((guchar)*p) || (* p == '/')) {
3727 if (remaining > 0) {
3733 result[size - 1] = '\0';
3738 if (remaining >= 2) {
3746 result[size - 1] = '\0';
3751 if (remaining > 0) {
3755 result[size - 1] = '\0';
3769 static void g_node_map_recursive(GNode *node, gpointer data)
3771 GNodeMapData *mapdata = (GNodeMapData *) data;
3773 GNodeMapData newmapdata;
3776 newdata = mapdata->func(node->data, mapdata->data);
3777 if (newdata != NULL) {
3778 newnode = g_node_new(newdata);
3779 g_node_append(mapdata->parent, newnode);
3781 newmapdata.parent = newnode;
3782 newmapdata.func = mapdata->func;
3783 newmapdata.data = mapdata->data;
3785 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3789 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3792 GNodeMapData mapdata;
3794 g_return_val_if_fail(node != NULL, NULL);
3795 g_return_val_if_fail(func != NULL, NULL);
3797 root = g_node_new(func(node->data, data));
3799 mapdata.parent = root;
3800 mapdata.func = func;
3801 mapdata.data = data;
3803 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3808 #define HEX_TO_INT(val, hex) \
3812 if ('0' <= c && c <= '9') { \
3814 } else if ('a' <= c && c <= 'f') { \
3815 val = c - 'a' + 10; \
3816 } else if ('A' <= c && c <= 'F') { \
3817 val = c - 'A' + 10; \
3823 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3830 if (hi == -1 || lo == -1)
3833 *out = (hi << 4) + lo;
3837 #define INT_TO_HEX(hex, val) \
3840 hex = '0' + (val); \
3842 hex = 'A' + (val) - 10; \
3845 void get_hex_str(gchar *out, guchar ch)
3849 INT_TO_HEX(hex, ch >> 4);
3851 INT_TO_HEX(hex, ch & 0x0f);
3857 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3859 #define G_PRINT_REF g_print
3863 *\brief Register ref counted pointer. It is based on GBoxed, so should
3864 * work with anything that uses the GType system. The semantics
3865 * are similar to a C++ auto pointer, with the exception that
3866 * C doesn't have automatic closure (calling destructors) when
3867 * exiting a block scope.
3868 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3869 * function directly.
3871 *\return GType A GType type.
3873 GType g_auto_pointer_register(void)
3875 static GType auto_pointer_type;
3876 if (!auto_pointer_type)
3878 g_boxed_type_register_static
3879 ("G_TYPE_AUTO_POINTER",
3880 (GBoxedCopyFunc) g_auto_pointer_copy,
3881 (GBoxedFreeFunc) g_auto_pointer_free);
3882 return auto_pointer_type;
3886 *\brief Structure with g_new() allocated pointer guarded by the
3889 typedef struct AutoPointerRef {
3890 void (*free) (gpointer);
3896 *\brief The auto pointer opaque structure that references the
3897 * pointer guard block.
3899 typedef struct AutoPointer {
3900 AutoPointerRef *ref;
3901 gpointer ptr; /*!< access to protected pointer */
3905 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3909 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3910 * ... when assigning, copying and freeing storage elements
3912 * gtk_list_store_new(N_S_COLUMNS,
3913 * G_TYPE_AUTO_POINTER,
3917 * Template *precious_data = g_new0(Template, 1);
3918 * g_pointer protect = g_auto_pointer_new(precious_data);
3920 * gtk_list_store_set(container, &iter,
3924 * ... the gtk_list_store has copied the pointer and
3925 * ... incremented its reference count, we should free
3926 * ... the auto pointer (in C++ a destructor would do
3927 * ... this for us when leaving block scope)
3929 * g_auto_pointer_free(protect);
3931 * ... gtk_list_store_set() now manages the data. When
3932 * ... *explicitly* requesting a pointer from the list
3933 * ... store, don't forget you get a copy that should be
3934 * ... freed with g_auto_pointer_free() eventually.
3938 *\param pointer Pointer to be guarded.
3940 *\return GAuto * Pointer that should be used in containers with
3943 GAuto *g_auto_pointer_new(gpointer p)
3945 AutoPointerRef *ref;
3951 ref = g_new0(AutoPointerRef, 1);
3952 ptr = g_new0(AutoPointer, 1);
3962 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3968 *\brief Allocate an autopointer using the passed \a free function to
3969 * free the guarded pointer
3971 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3978 aptr = g_auto_pointer_new(p);
3979 aptr->ref->free = free_;
3983 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3985 if (auto_ptr == NULL)
3987 return ((AutoPointer *) auto_ptr)->ptr;
3991 *\brief Copies an auto pointer by. It's mostly not necessary
3992 * to call this function directly, unless you copy/assign
3993 * the guarded pointer.
3995 *\param auto_ptr Auto pointer returned by previous call to
3996 * g_auto_pointer_new_XXX()
3998 *\return gpointer An auto pointer
4000 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4003 AutoPointerRef *ref;
4006 if (auto_ptr == NULL)
4011 newp = g_new0(AutoPointer, 1);
4014 newp->ptr = ref->pointer;
4018 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4024 *\brief Free an auto pointer
4026 void g_auto_pointer_free(GAuto *auto_ptr)
4029 AutoPointerRef *ref;
4031 if (auto_ptr == NULL)
4037 if (--(ref->cnt) == 0) {
4039 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4041 ref->free(ref->pointer);
4046 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4051 void replace_returns(gchar *str)
4056 while (strstr(str, "\n")) {
4057 *strstr(str, "\n") = ' ';
4059 while (strstr(str, "\r")) {
4060 *strstr(str, "\r") = ' ';
4064 /* get_uri_part() - retrieves a URI starting from scanpos.
4065 Returns TRUE if succesful */
4066 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4067 const gchar **bp, const gchar **ep)
4071 g_return_val_if_fail(start != NULL, FALSE);
4072 g_return_val_if_fail(scanpos != NULL, FALSE);
4073 g_return_val_if_fail(bp != NULL, FALSE);
4074 g_return_val_if_fail(ep != NULL, FALSE);
4078 /* find end point of URI */
4079 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4080 if (!isgraph(*(const guchar *)ep_) ||
4081 !IS_ASCII(*(const guchar *)ep_) ||
4082 strchr("[]{}()<>\"", *ep_))
4086 /* no punctuation at end of string */
4088 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4089 * should pass some URI type to this function and decide on that whether
4090 * to perform punctuation stripping */
4092 #define IS_REAL_PUNCT(ch) (ispunct(ch) && ((ch) != '/'))
4094 for (; ep_ - 1 > scanpos + 1 &&
4095 IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
4099 #undef IS_REAL_PUNCT
4106 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4108 return g_strndup(bp, ep - bp);
4111 /* valid mail address characters */
4112 #define IS_RFC822_CHAR(ch) \
4116 !g_ascii_isspace(ch) && \
4117 !strchr("(),;<>\"", (ch)))
4119 /* alphabet and number within 7bit ASCII */
4120 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4121 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4123 static GHashTable *create_domain_tab(void)
4125 static const gchar *toplvl_domains [] = {
4127 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4128 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4129 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4130 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4131 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4132 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4133 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4134 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4135 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4136 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4137 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4138 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4139 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4140 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4141 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4142 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4143 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4144 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4145 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4146 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4147 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4148 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4149 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4150 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4151 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4152 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4155 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4157 g_return_val_if_fail(htab, NULL);
4158 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4159 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4163 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4165 const gint MAX_LVL_DOM_NAME_LEN = 6;
4166 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4167 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4170 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4173 for (p = buf; p < m && first < last; *p++ = *first++)
4177 return g_hash_table_lookup(tab, buf) != NULL;
4180 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4181 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4182 const gchar **bp, const gchar **ep)
4184 /* more complex than the uri part because we need to scan back and forward starting from
4185 * the scan position. */
4186 gboolean result = FALSE;
4187 const gchar *bp_ = NULL;
4188 const gchar *ep_ = NULL;
4189 static GHashTable *dom_tab;
4190 const gchar *last_dot = NULL;
4191 const gchar *prelast_dot = NULL;
4192 const gchar *last_tld_char = NULL;
4194 /* the informative part of the email address (describing the name
4195 * of the email address owner) may contain quoted parts. the
4196 * closure stack stores the last encountered quotes. */
4197 gchar closure_stack[128];
4198 gchar *ptr = closure_stack;
4200 g_return_val_if_fail(start != NULL, FALSE);
4201 g_return_val_if_fail(scanpos != NULL, FALSE);
4202 g_return_val_if_fail(bp != NULL, FALSE);
4203 g_return_val_if_fail(ep != NULL, FALSE);
4206 dom_tab = create_domain_tab();
4207 g_return_val_if_fail(dom_tab, FALSE);
4209 /* scan start of address */
4210 for (bp_ = scanpos - 1;
4211 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4214 /* TODO: should start with an alnum? */
4216 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4219 if (bp_ != scanpos) {
4220 /* scan end of address */
4221 for (ep_ = scanpos + 1;
4222 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4224 prelast_dot = last_dot;
4226 if (*(last_dot + 1) == '.') {
4227 if (prelast_dot == NULL)
4229 last_dot = prelast_dot;
4234 /* TODO: really should terminate with an alnum? */
4235 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4240 if (last_dot == NULL)
4242 if (last_dot >= ep_)
4243 last_dot = prelast_dot;
4244 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4248 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4249 if (*last_tld_char == '?')
4252 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4259 if (!result) return FALSE;
4261 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4262 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4263 && IS_RFC822_CHAR(*(ep_ + 3))) {
4264 /* this informative part with an @ in it is
4265 * followed by the email address */
4268 /* go to matching '>' (or next non-rfc822 char, like \n) */
4269 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4272 /* include the bracket */
4273 if (*ep_ == '>') ep_++;
4275 /* include the leading quote */
4283 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4284 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4287 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4288 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4291 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4292 #define IN_STACK() (ptr > closure_stack)
4293 /* has underrun check */
4294 #define POP_STACK() if(IN_STACK()) --ptr
4295 /* has overrun check */
4296 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4297 /* has underrun check */
4298 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4302 /* scan for the informative part. */
4303 for (bp_ -= 2; bp_ >= start; bp_--) {
4304 /* if closure on the stack keep scanning */
4305 if (PEEK_STACK() == *bp_) {
4309 if (*bp_ == '\'' || *bp_ == '"') {
4314 /* if nothing in the closure stack, do the special conditions
4315 * the following if..else expression simply checks whether
4316 * a token is acceptable. if not acceptable, the clause
4317 * should terminate the loop with a 'break' */
4318 if (!PEEK_STACK()) {
4320 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4321 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4322 /* hyphens are allowed, but only in
4324 } else if (!strchr(",;:=?./+<>!&\r\n\t", *bp_)) {
4325 /* but anything not being a punctiation
4328 break; /* anything else is rejected */
4341 /* scan forward (should start with an alnum) */
4342 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4352 #undef IS_ASCII_ALNUM
4353 #undef IS_RFC822_CHAR
4355 gchar *make_email_string(const gchar *bp, const gchar *ep)
4357 /* returns a mailto: URI; mailto: is also used to detect the
4358 * uri type later on in the button_pressed signal handler */
4362 tmp = g_strndup(bp, ep - bp);
4363 result = g_strconcat("mailto:", tmp, NULL);
4369 gchar *make_http_string(const gchar *bp, const gchar *ep)
4371 /* returns an http: URI; */
4375 tmp = g_strndup(bp, ep - bp);
4376 result = g_strconcat("http://", tmp, NULL);
4382 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4384 FILE *fp = fopen(path, "rb");
4385 gchar buf[BUFFSIZE];
4386 gchar *result = NULL;
4389 while (fgets(buf, sizeof (buf), fp) != NULL) {
4390 gchar **parts = g_strsplit(buf, ";", -1);
4391 gchar *trimmed = parts[0];
4392 while (trimmed[0] == ' ')
4394 while (trimmed[strlen(trimmed)-1] == ' ')
4395 trimmed[strlen(trimmed)-1] = '\0';
4397 if (!strcmp(trimmed, type)) {
4399 while (trimmed[0] == ' ')
4401 while (trimmed[strlen(trimmed)-1] == ' ')
4402 trimmed[strlen(trimmed)-1] = '\0';
4403 while (trimmed[strlen(trimmed)-1] == '\n')
4404 trimmed[strlen(trimmed)-1] = '\0';
4405 while (trimmed[strlen(trimmed)-1] == '\r')
4406 trimmed[strlen(trimmed)-1] = '\0';
4407 result = g_strdup(trimmed);
4410 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4411 gchar *start = g_strdup(result);
4412 gchar *end = g_strdup(strstr(result, "%s")+2);
4414 *strstr(start, "%s") = '\0';
4415 tmp = g_strconcat(start,"'%s'",end, NULL);
4428 gchar *mailcap_get_command_for_type(const gchar *type)
4430 gchar *result = NULL;
4432 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4433 result = mailcap_get_command_in_file(path, type);
4437 result = mailcap_get_command_in_file("/etc/mailcap", type);