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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 /* Similar to `strstr' but this function ignores the case of both strings. */
388 gchar *strcasestr(const gchar *haystack, const gchar *needle)
390 register size_t haystack_len, needle_len;
392 haystack_len = strlen(haystack);
393 needle_len = strlen(needle);
395 if (haystack_len < needle_len || needle_len == 0)
398 while (haystack_len >= needle_len) {
399 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
400 return (gchar *)haystack;
410 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
411 gconstpointer needle, size_t needlelen)
413 const gchar *haystack_ = (const gchar *)haystack;
414 const gchar *needle_ = (const gchar *)needle;
415 const gchar *haystack_cur = (const gchar *)haystack;
418 return memchr(haystack_, *needle_, haystacklen);
420 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
422 if (haystacklen - (haystack_cur - haystack_) < needlelen)
424 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
425 return (gpointer)haystack_cur;
433 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
434 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
436 register const gchar *s = src;
437 register gchar *d = dest;
447 int iswalnum(wint_t wc)
449 return g_ascii_isalnum((int)wc);
454 int iswspace(wint_t wc)
456 return g_ascii_isspace((int)wc);
461 wint_t towlower(wint_t wc)
463 if (wc >= L'A' && wc <= L'Z')
464 return wc + L'a' - L'A';
471 size_t wcslen(const wchar_t *s)
483 /* Copy SRC to DEST. */
484 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
492 } while (c != L'\0');
499 /* Copy no more than N wide-characters of SRC to DEST. */
500 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
510 } while (c != L'\0');
521 /* Duplicate S, returning an identical malloc'd string. */
522 wchar_t *wcsdup(const wchar_t *s)
527 new_str = g_new(wchar_t, wcslen(s) + 1);
535 /* Duplicate no more than N wide-characters of S,
536 returning an identical malloc'd string. */
537 wchar_t *wcsndup(const wchar_t *s, size_t n)
542 new_str = g_new(wchar_t, n + 1);
543 wcsncpy(new_str, s, n);
544 new_str[n] = (wchar_t)0;
551 wchar_t *strdup_mbstowcs(const gchar *s)
556 new_str = g_new(wchar_t, strlen(s) + 1);
557 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
561 new_str = g_realloc(new_str,
562 sizeof(wchar_t) * (wcslen(new_str) + 1));
569 gchar *strdup_wcstombs(const wchar_t *s)
575 len = wcslen(s) * MB_CUR_MAX + 1;
576 new_str = g_new(gchar, len);
577 if (wcstombs(new_str, s, len) < 0) {
581 new_str = g_realloc(new_str, strlen(new_str) + 1);
588 /* Compare S1 and S2, ignoring case. */
589 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
595 c1 = towlower(*s1++);
596 c2 = towlower(*s2++);
599 else if (c1 == 0 && c2 == 0)
606 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
607 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
609 register size_t haystack_len, needle_len;
611 haystack_len = wcslen(haystack);
612 needle_len = wcslen(needle);
614 if (haystack_len < needle_len || needle_len == 0)
617 while (haystack_len >= needle_len) {
618 if (!wcsncasecmp(haystack, needle, needle_len))
619 return (wchar_t *)haystack;
629 gint get_mbs_len(const gchar *s)
639 mb_len = g_utf8_skip[*(guchar *)p];
651 /* Examine if next block is non-ASCII string */
652 gboolean is_next_nonascii(const gchar *s)
656 /* skip head space */
657 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
659 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
660 if (*(guchar *)p > 127 || *(guchar *)p < 32)
667 gint get_next_word_len(const gchar *s)
671 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
677 /* compare subjects */
678 gint subject_compare(const gchar *s1, const gchar *s2)
682 if (!s1 || !s2) return -1;
683 if (!*s1 || !*s2) return -1;
685 Xstrdup_a(str1, s1, return -1);
686 Xstrdup_a(str2, s2, return -1);
688 trim_subject_for_compare(str1);
689 trim_subject_for_compare(str2);
691 if (!*str1 || !*str2) return -1;
693 return strcmp(str1, str2);
696 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
700 if (!s1 || !s2) return -1;
702 Xstrdup_a(str1, s1, return -1);
703 Xstrdup_a(str2, s2, return -1);
705 trim_subject_for_sort(str1);
706 trim_subject_for_sort(str2);
708 return g_utf8_collate(str1, str2);
711 void trim_subject_for_compare(gchar *str)
715 eliminate_parenthesis(str, '[', ']');
716 eliminate_parenthesis(str, '(', ')');
719 srcp = str + subject_get_prefix_length(str);
721 memmove(str, srcp, strlen(srcp) + 1);
724 void trim_subject_for_sort(gchar *str)
730 srcp = str + subject_get_prefix_length(str);
732 memmove(str, srcp, strlen(srcp) + 1);
735 void trim_subject(gchar *str)
737 register gchar *srcp;
743 srcp = str + subject_get_prefix_length(str);
748 } else if (*srcp == '(') {
760 else if (*srcp == cl)
767 while (g_ascii_isspace(*srcp)) srcp++;
768 memmove(str, srcp, strlen(srcp) + 1);
771 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
773 register gchar *srcp, *destp;
778 while ((destp = strchr(destp, op))) {
784 else if (*srcp == cl)
790 while (g_ascii_isspace(*srcp)) srcp++;
791 memmove(destp, srcp, strlen(srcp) + 1);
795 void extract_parenthesis(gchar *str, gchar op, gchar cl)
797 register gchar *srcp, *destp;
802 while ((srcp = strchr(destp, op))) {
805 memmove(destp, srcp + 1, strlen(srcp));
810 else if (*destp == cl)
822 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
825 register gchar *srcp, *destp;
827 gboolean in_quote = FALSE;
831 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
834 memmove(destp, srcp + 1, strlen(srcp));
837 if (*destp == op && !in_quote)
839 else if (*destp == cl && !in_quote)
841 else if (*destp == quote_chr)
853 void eliminate_quote(gchar *str, gchar quote_chr)
855 register gchar *srcp, *destp;
859 while ((destp = strchr(destp, quote_chr))) {
860 if ((srcp = strchr(destp + 1, quote_chr))) {
862 while (g_ascii_isspace(*srcp)) srcp++;
863 memmove(destp, srcp, strlen(srcp) + 1);
871 void extract_quote(gchar *str, gchar quote_chr)
875 if ((str = strchr(str, quote_chr))) {
877 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
878 memmove(p - 1, p, strlen(p) + 1);
883 memmove(str, str + 1, p - str);
888 void eliminate_address_comment(gchar *str)
890 register gchar *srcp, *destp;
895 while ((destp = strchr(destp, '"'))) {
896 if ((srcp = strchr(destp + 1, '"'))) {
901 while (g_ascii_isspace(*srcp)) srcp++;
902 memmove(destp, srcp, strlen(srcp) + 1);
912 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
918 else if (*srcp == ')')
924 while (g_ascii_isspace(*srcp)) srcp++;
925 memmove(destp, srcp, strlen(srcp) + 1);
929 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
931 gboolean in_quote = FALSE;
934 if (*str == c && !in_quote)
936 if (*str == quote_chr)
944 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
946 gboolean in_quote = FALSE;
949 p = str + strlen(str) - 1;
951 if (*p == c && !in_quote)
961 void extract_address(gchar *str)
963 eliminate_address_comment(str);
964 if (strchr_with_skip_quote(str, '"', '<'))
965 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
969 void extract_list_id_str(gchar *str)
971 if (strchr_with_skip_quote(str, '"', '<'))
972 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
976 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
981 if (!str) return addr_list;
983 Xstrdup_a(work, str, return addr_list);
986 eliminate_address_comment(work);
989 while (workp && *workp) {
992 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
998 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
999 extract_parenthesis_with_skip_quote
1000 (workp, '"', '<', '>');
1004 addr_list = g_slist_append(addr_list, g_strdup(workp));
1012 GSList *address_list_append(GSList *addr_list, const gchar *str)
1014 return address_list_append_real(addr_list, str, TRUE);
1017 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1019 return address_list_append_real(addr_list, str, FALSE);
1022 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1026 if (!str) return msgid_list;
1029 while (strp && *strp) {
1030 const gchar *start, *end;
1033 if ((start = strchr(strp, '<')) != NULL) {
1034 end = strchr(start + 1, '>');
1039 msgid = g_strndup(start + 1, end - start - 1);
1042 msgid_list = g_slist_prepend(msgid_list, msgid);
1052 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1056 list = references_list_prepend(NULL, str);
1057 list = g_slist_reverse(list);
1058 msgid_list = g_slist_concat(msgid_list, list);
1063 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1068 if (!str) return group_list;
1070 Xstrdup_a(work, str, return group_list);
1074 while (workp && *workp) {
1077 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1085 group_list = g_slist_append(group_list,
1094 GList *add_history(GList *list, const gchar *str)
1098 g_return_val_if_fail(str != NULL, list);
1100 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1103 list = g_list_remove(list, old->data);
1104 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1107 last = g_list_last(list);
1110 g_list_remove(list, last->data);
1114 list = g_list_prepend(list, g_strdup(str));
1119 void remove_return(gchar *str)
1121 register gchar *p = str;
1124 if (*p == '\n' || *p == '\r')
1125 memmove(p, p + 1, strlen(p));
1131 void remove_space(gchar *str)
1133 register gchar *p = str;
1138 while (g_ascii_isspace(*(p + spc)))
1141 memmove(p, p + spc, strlen(p + spc) + 1);
1147 void unfold_line(gchar *str)
1149 register gchar *p = str;
1153 if (*p == '\n' || *p == '\r') {
1156 while (g_ascii_isspace(*(p + spc)))
1159 memmove(p, p + spc, strlen(p + spc) + 1);
1165 void subst_char(gchar *str, gchar orig, gchar subst)
1167 register gchar *p = str;
1176 void subst_chars(gchar *str, gchar *orig, gchar subst)
1178 register gchar *p = str;
1181 if (strchr(orig, *p) != NULL)
1187 void subst_for_filename(gchar *str)
1191 subst_chars(str, "\t\r\n\\/*", '_');
1194 void subst_for_shellsafe_filename(gchar *str)
1198 subst_for_filename(str);
1199 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1202 gboolean is_header_line(const gchar *str)
1204 if (str[0] == ':') return FALSE;
1206 while (*str != '\0' && *str != ' ') {
1215 gboolean is_ascii_str(const gchar *str)
1217 const guchar *p = (const guchar *)str;
1219 while (*p != '\0') {
1220 if (*p != '\t' && *p != ' ' &&
1221 *p != '\r' && *p != '\n' &&
1222 (*p < 32 || *p >= 127))
1230 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1232 const gchar *first_pos;
1233 const gchar *last_pos;
1234 const gchar *p = str;
1235 gint quote_level = -1;
1237 /* speed up line processing by only searching to the last '>' */
1238 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1239 /* skip a line if it contains a '<' before the initial '>' */
1240 if (memchr(str, '<', first_pos - str) != NULL)
1242 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1246 while (p <= last_pos) {
1247 while (p < last_pos) {
1248 if (g_ascii_isspace(*p))
1254 if (strchr(quote_chars, *p))
1256 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1257 /* any characters are allowed except '-' and space */
1259 && !strchr(quote_chars, *p)
1260 && !g_ascii_isspace(*p)
1263 if (strchr(quote_chars, *p))
1275 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1277 const gchar *p = str, *q;
1278 gint cur_line = 0, len;
1280 while ((q = strchr(p, '\n')) != NULL) {
1282 if (len > max_chars) {
1292 if (len > max_chars) {
1301 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1303 gchar * position = NULL;
1304 gchar * tmp_pos = NULL;
1307 if (quote_chars == NULL)
1310 for (i = 0; i < strlen(quote_chars); i++) {
1311 tmp_pos = strchr (str, quote_chars[i]);
1313 || (tmp_pos != NULL && position >= tmp_pos) )
1319 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1321 gchar * position = NULL;
1322 gchar * tmp_pos = NULL;
1325 if (quote_chars == NULL)
1328 for (i = 0; i < strlen(quote_chars); i++) {
1329 tmp_pos = strrchr (str, quote_chars[i]);
1331 || (tmp_pos != NULL && position <= tmp_pos) )
1337 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1339 register guint haystack_len, needle_len;
1340 gboolean in_squote = FALSE, in_dquote = FALSE;
1342 haystack_len = strlen(haystack);
1343 needle_len = strlen(needle);
1345 if (haystack_len < needle_len || needle_len == 0)
1348 while (haystack_len >= needle_len) {
1349 if (!in_squote && !in_dquote &&
1350 !strncmp(haystack, needle, needle_len))
1351 return (gchar *)haystack;
1353 /* 'foo"bar"' -> foo"bar"
1354 "foo'bar'" -> foo'bar' */
1355 if (*haystack == '\'') {
1358 else if (!in_dquote)
1360 } else if (*haystack == '\"') {
1363 else if (!in_squote)
1374 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1377 gchar quote_chr = '"';
1379 gboolean in_quote = FALSE;
1383 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1387 if (*p == op && !in_quote)
1389 else if (*p == cl && !in_quote)
1391 else if (*p == quote_chr)
1404 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1407 GSList *string_list = NULL, *slist;
1409 const gchar *s_op, *s_cl;
1412 g_return_val_if_fail(str != NULL, NULL);
1415 max_tokens = G_MAXINT;
1417 s_op = strchr_with_skip_quote(str, '"', op);
1418 if (!s_op) return NULL;
1420 s_cl = strchr_parenthesis_close(str, op, cl);
1428 new_string = g_new(gchar, len + 1);
1429 strncpy(new_string, str, len);
1430 new_string[len] = 0;
1431 string_list = g_slist_prepend(string_list, new_string);
1435 while (*str && g_ascii_isspace(*str)) str++;
1437 string_list = g_slist_prepend(string_list,
1440 s_op = strchr_with_skip_quote(str, '"', op);
1441 if (!--max_tokens || !s_op) break;
1445 s_cl = strchr_parenthesis_close(str, op, cl);
1446 } while (--max_tokens && s_cl);
1449 str_array = g_new(gchar*, n);
1453 str_array[i--] = NULL;
1454 for (slist = string_list; slist; slist = slist->next)
1455 str_array[i--] = slist->data;
1457 g_slist_free(string_list);
1462 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1465 GSList *string_list = NULL, *slist;
1466 gchar **str_array, *s, *new_str;
1467 guint i, n = 1, len;
1469 g_return_val_if_fail(str != NULL, NULL);
1470 g_return_val_if_fail(delim != NULL, NULL);
1473 max_tokens = G_MAXINT;
1475 s = strstr_with_skip_quote(str, delim);
1477 guint delimiter_len = strlen(delim);
1481 new_str = g_strndup(str, len);
1483 if (new_str[0] == '\'' || new_str[0] == '\"') {
1484 if (new_str[len - 1] == new_str[0]) {
1485 new_str[len - 1] = '\0';
1486 memmove(new_str, new_str + 1, len - 1);
1489 string_list = g_slist_prepend(string_list, new_str);
1491 str = s + delimiter_len;
1492 s = strstr_with_skip_quote(str, delim);
1493 } while (--max_tokens && s);
1497 new_str = g_strdup(str);
1498 if (new_str[0] == '\'' || new_str[0] == '\"') {
1500 if (new_str[len - 1] == new_str[0]) {
1501 new_str[len - 1] = '\0';
1502 memmove(new_str, new_str + 1, len - 1);
1505 string_list = g_slist_prepend(string_list, new_str);
1509 str_array = g_new(gchar*, n);
1513 str_array[i--] = NULL;
1514 for (slist = string_list; slist; slist = slist->next)
1515 str_array[i--] = slist->data;
1517 g_slist_free(string_list);
1522 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1524 gchar *abbrev_group;
1526 const gchar *p = group;
1529 g_return_val_if_fail(group != NULL, NULL);
1531 last = group + strlen(group);
1532 abbrev_group = ap = g_malloc(strlen(group) + 1);
1537 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1539 while (*p != '.') p++;
1542 return abbrev_group;
1547 return abbrev_group;
1550 gchar *trim_string(const gchar *str, gint len)
1552 const gchar *p = str;
1557 if (!str) return NULL;
1558 if (strlen(str) <= len)
1559 return g_strdup(str);
1560 if (g_utf8_validate(str, -1, NULL) == FALSE)
1561 return g_strdup(str);
1563 while (*p != '\0') {
1564 mb_len = g_utf8_skip[*(guchar *)p];
1567 else if (new_len + mb_len > len)
1574 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1575 return g_strconcat(new_str, "...", NULL);
1578 GList *uri_list_extract_filenames(const gchar *uri_list)
1580 GList *result = NULL;
1582 gchar *escaped_utf8uri;
1588 while (g_ascii_isspace(*p)) p++;
1589 if (!strncmp(p, "file:", 5)) {
1592 while (*q && *q != '\n' && *q != '\r') q++;
1595 gchar *file, *locale_file = NULL;
1597 while (q > p && g_ascii_isspace(*q))
1599 Xalloca(escaped_utf8uri, q - p + 2,
1601 Xalloca(file, q - p + 2,
1604 strncpy(escaped_utf8uri, p, q - p + 1);
1605 escaped_utf8uri[q - p + 1] = '\0';
1606 decode_uri(file, escaped_utf8uri);
1608 * g_filename_from_uri() rejects escaped/locale encoded uri
1609 * string which come from Nautilus.
1611 if (g_utf8_validate(file, -1, NULL))
1613 = conv_codeset_strdup(
1616 conv_get_locale_charset_str());
1618 locale_file = g_strdup(file + 5);
1619 result = g_list_append(result, locale_file);
1623 p = strchr(p, '\n');
1630 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1633 static gint axtoi(const gchar *hexstr)
1635 gint hi, lo, result;
1638 if ('0' <= hi && hi <= '9') {
1641 if ('a' <= hi && hi <= 'f') {
1644 if ('A' <= hi && hi <= 'F') {
1649 if ('0' <= lo && lo <= '9') {
1652 if ('a' <= lo && lo <= 'f') {
1655 if ('A' <= lo && lo <= 'F') {
1658 result = lo + (16 * hi);
1662 gboolean is_uri_string(const gchar *str)
1664 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1665 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1666 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1667 g_ascii_strncasecmp(str, "www.", 4) == 0);
1670 gchar *get_uri_path(const gchar *uri)
1672 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1673 return (gchar *)(uri + 7);
1674 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1675 return (gchar *)(uri + 8);
1676 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1677 return (gchar *)(uri + 6);
1679 return (gchar *)uri;
1682 gint get_uri_len(const gchar *str)
1686 if (is_uri_string(str)) {
1687 for (p = str; *p != '\0'; p++) {
1688 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1697 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1698 * plusses, and escape characters are used)
1700 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1702 gchar *dec = decoded_uri;
1703 const gchar *enc = encoded_uri;
1708 if (isxdigit((guchar)enc[0]) &&
1709 isxdigit((guchar)enc[1])) {
1727 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1728 gchar **subject, gchar **body)
1733 Xstrdup_a(tmp_mailto, mailto, return -1);
1735 if (!strncmp(tmp_mailto, "mailto:", 7))
1738 p = strchr(tmp_mailto, '?');
1745 *to = g_strdup(tmp_mailto);
1748 gchar *field, *value;
1765 if (*value == '\0') continue;
1767 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1768 *cc = g_strdup(value);
1769 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1770 *bcc = g_strdup(value);
1771 } else if (subject && !*subject &&
1772 !g_ascii_strcasecmp(field, "subject")) {
1773 *subject = g_malloc(strlen(value) + 1);
1774 decode_uri(*subject, value);
1775 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1776 *body = g_malloc(strlen(value) + 1);
1777 decode_uri(*body, value);
1784 const gchar *get_home_dir(void)
1787 static const gchar *home_dir = NULL;
1790 home_dir = g_get_home_dir();
1792 home_dir = "C:\\Sylpheed";
1797 return g_get_home_dir();
1801 const gchar *get_rc_dir(void)
1803 static gchar *rc_dir = NULL;
1807 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1808 "Application Data", G_DIR_SEPARATOR_S,
1811 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1818 const gchar *get_mail_base_dir(void)
1821 static gchar *mail_base_dir = NULL;
1824 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1827 return mail_base_dir;
1829 return get_home_dir();
1833 const gchar *get_news_cache_dir(void)
1835 static gchar *news_cache_dir = NULL;
1837 if (!news_cache_dir)
1838 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1839 NEWS_CACHE_DIR, NULL);
1841 return news_cache_dir;
1844 const gchar *get_imap_cache_dir(void)
1846 static gchar *imap_cache_dir = NULL;
1848 if (!imap_cache_dir)
1849 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1850 IMAP_CACHE_DIR, NULL);
1852 return imap_cache_dir;
1855 const gchar *get_mbox_cache_dir(void)
1857 static gchar *mbox_cache_dir = NULL;
1859 if (!mbox_cache_dir)
1860 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1861 MBOX_CACHE_DIR, NULL);
1863 return mbox_cache_dir;
1866 const gchar *get_mime_tmp_dir(void)
1868 static gchar *mime_tmp_dir = NULL;
1871 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1872 MIME_TMP_DIR, NULL);
1874 return mime_tmp_dir;
1877 const gchar *get_template_dir(void)
1879 static gchar *template_dir = NULL;
1882 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1883 TEMPLATE_DIR, NULL);
1885 return template_dir;
1888 const gchar *get_header_cache_dir(void)
1890 static gchar *header_dir = NULL;
1893 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1894 HEADER_CACHE_DIR, NULL);
1899 const gchar *get_tmp_dir(void)
1901 static gchar *tmp_dir = NULL;
1904 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1910 gchar *get_tmp_file(void)
1913 static guint32 id = 0;
1915 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1916 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1921 const gchar *get_domain_name(void)
1924 static gchar *domain_name = NULL;
1930 if (uname(&uts) < 0) {
1931 perror("gethostname");
1932 domain_name = "unknown";
1934 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
1935 perror("gethostbyname");
1936 domain_name = g_strdup(uts.nodename);
1938 domain_name = g_strdup(hp->h_name);
1942 debug_print("domain name = %s\n", domain_name);
1951 off_t get_file_size(const gchar *file)
1955 if (g_stat(file, &s) < 0) {
1956 FILE_OP_ERROR(file, "stat");
1963 off_t get_file_size_as_crlf(const gchar *file)
1967 gchar buf[BUFFSIZE];
1969 if ((fp = g_fopen(file, "rb")) == NULL) {
1970 FILE_OP_ERROR(file, "fopen");
1974 while (fgets(buf, sizeof(buf), fp) != NULL) {
1976 size += strlen(buf) + 2;
1980 FILE_OP_ERROR(file, "fgets");
1989 off_t get_left_file_size(FILE *fp)
1995 if ((pos = ftell(fp)) < 0) {
1999 if (fseek(fp, 0L, SEEK_END) < 0) {
2003 if ((end = ftell(fp)) < 0) {
2008 if (fseek(fp, pos, SEEK_SET) < 0) {
2016 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2023 if (g_stat(file, &s) < 0) {
2024 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2028 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2034 gboolean is_dir_exist(const gchar *dir)
2039 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2042 gboolean is_file_entry_exist(const gchar *file)
2047 return g_file_test(file, G_FILE_TEST_EXISTS);
2050 gboolean dirent_is_regular_file(struct dirent *d)
2052 #ifdef HAVE_DIRENT_D_TYPE
2053 if (d->d_type == DT_REG)
2055 else if (d->d_type != DT_UNKNOWN)
2059 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2062 gboolean dirent_is_directory(struct dirent *d)
2064 #ifdef HAVE_DIRENT_D_TYPE
2065 if (d->d_type == DT_DIR)
2067 else if (d->d_type != DT_UNKNOWN)
2071 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2074 gint change_dir(const gchar *dir)
2076 gchar *prevdir = NULL;
2079 prevdir = g_get_current_dir();
2081 if (g_chdir(dir) < 0) {
2082 FILE_OP_ERROR(dir, "chdir");
2083 if (debug_mode) g_free(prevdir);
2085 } else if (debug_mode) {
2088 cwd = g_get_current_dir();
2089 if (strcmp(prevdir, cwd) != 0)
2090 g_print("current dir: %s\n", cwd);
2098 gint make_dir(const gchar *dir)
2100 if (g_mkdir(dir, S_IRWXU) < 0) {
2101 FILE_OP_ERROR(dir, "mkdir");
2104 if (g_chmod(dir, S_IRWXU) < 0)
2105 FILE_OP_ERROR(dir, "chmod");
2110 gint make_dir_hier(const gchar *dir)
2115 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2116 parent_dir = g_strndup(dir, p - dir);
2117 if (*parent_dir != '\0') {
2118 if (!is_dir_exist(parent_dir)) {
2119 if (make_dir(parent_dir) < 0) {
2128 if (!is_dir_exist(dir)) {
2129 if (make_dir(dir) < 0)
2136 gint remove_all_files(const gchar *dir)
2139 const gchar *dir_name;
2142 prev_dir = g_get_current_dir();
2144 if (g_chdir(dir) < 0) {
2145 FILE_OP_ERROR(dir, "chdir");
2150 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2151 g_warning("failed to open directory: %s\n", dir);
2156 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2157 if (g_unlink(dir_name) < 0)
2158 FILE_OP_ERROR(dir_name, "unlink");
2163 if (g_chdir(prev_dir) < 0) {
2164 FILE_OP_ERROR(prev_dir, "chdir");
2174 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2177 const gchar *dir_name;
2181 prev_dir = g_get_current_dir();
2183 if (g_chdir(dir) < 0) {
2184 FILE_OP_ERROR(dir, "chdir");
2189 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2190 g_warning("failed to open directory: %s\n", dir);
2195 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2196 file_no = to_number(dir_name);
2197 if (file_no > 0 && first <= file_no && file_no <= last) {
2198 if (is_dir_exist(dir_name))
2200 if (g_unlink(dir_name) < 0)
2201 FILE_OP_ERROR(dir_name, "unlink");
2207 if (g_chdir(prev_dir) < 0) {
2208 FILE_OP_ERROR(prev_dir, "chdir");
2218 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2221 const gchar *dir_name;
2225 prev_dir = g_get_current_dir();
2227 if (g_chdir(dir) < 0) {
2228 FILE_OP_ERROR(dir, "chdir");
2233 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2234 FILE_OP_ERROR(dir, "opendir");
2239 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2240 file_no = to_number(dir_name);
2241 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2242 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2243 if (is_dir_exist(dir_name))
2245 if (g_unlink(dir_name) < 0)
2246 FILE_OP_ERROR(dir_name, "unlink");
2252 if (g_chdir(prev_dir) < 0) {
2253 FILE_OP_ERROR(prev_dir, "chdir");
2263 gint remove_all_numbered_files(const gchar *dir)
2265 return remove_numbered_files(dir, 0, UINT_MAX);
2268 gint remove_expired_files(const gchar *dir, guint hours)
2271 const gchar *dir_name;
2275 time_t mtime, now, expire_time;
2277 prev_dir = g_get_current_dir();
2279 if (g_chdir(dir) < 0) {
2280 FILE_OP_ERROR(dir, "chdir");
2285 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2286 g_warning("failed to open directory: %s\n", dir);
2292 expire_time = hours * 60 * 60;
2294 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2295 file_no = to_number(dir_name);
2297 if (g_stat(dir_name, &s) < 0) {
2298 FILE_OP_ERROR(dir_name, "stat");
2301 if (S_ISDIR(s.st_mode))
2303 mtime = MAX(s.st_mtime, s.st_atime);
2304 if (now - mtime > expire_time) {
2305 if (g_unlink(dir_name) < 0)
2306 FILE_OP_ERROR(dir_name, "unlink");
2313 if (g_chdir(prev_dir) < 0) {
2314 FILE_OP_ERROR(prev_dir, "chdir");
2324 gint remove_dir_recursive(const gchar *dir)
2328 const gchar *dir_name;
2331 if (g_stat(dir, &s) < 0) {
2332 FILE_OP_ERROR(dir, "stat");
2333 if (ENOENT == errno) return 0;
2337 if (!S_ISDIR(s.st_mode)) {
2338 if (g_unlink(dir) < 0) {
2339 FILE_OP_ERROR(dir, "unlink");
2346 prev_dir = g_get_current_dir();
2347 /* g_print("prev_dir = %s\n", prev_dir); */
2349 if (!path_cmp(prev_dir, dir)) {
2351 if (g_chdir("..") < 0) {
2352 FILE_OP_ERROR(dir, "chdir");
2355 prev_dir = g_get_current_dir();
2358 if (g_chdir(dir) < 0) {
2359 FILE_OP_ERROR(dir, "chdir");
2364 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2365 g_warning("failed to open directory: %s\n", dir);
2371 /* remove all files in the directory */
2372 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2373 /* g_print("removing %s\n", dir_name); */
2375 if (is_dir_exist(dir_name)) {
2376 if (remove_dir_recursive(dir_name) < 0) {
2377 g_warning("can't remove directory\n");
2381 if (g_unlink(dir_name) < 0)
2382 FILE_OP_ERROR(dir_name, "unlink");
2388 if (g_chdir(prev_dir) < 0) {
2389 FILE_OP_ERROR(prev_dir, "chdir");
2396 if (g_rmdir(dir) < 0) {
2397 FILE_OP_ERROR(dir, "rmdir");
2404 gint rename_force(const gchar *oldpath, const gchar *newpath)
2407 if (!is_file_entry_exist(oldpath)) {
2411 if (is_file_exist(newpath)) {
2412 if (g_unlink(newpath) < 0)
2413 FILE_OP_ERROR(newpath, "unlink");
2416 return g_rename(oldpath, newpath);
2420 /* this seems to be slower than the stdio version... */
2421 gint copy_file(const gchar *src, const gchar *dest)
2423 gint src_fd, dest_fd;
2427 gchar *dest_bak = NULL;
2429 if ((src_fd = open(src, O_RDONLY)) < 0) {
2430 FILE_OP_ERROR(src, "open");
2434 if (is_file_exist(dest)) {
2435 dest_bak = g_strconcat(dest, ".bak", NULL);
2436 if (rename_force(dest, dest_bak) < 0) {
2437 FILE_OP_ERROR(dest, "rename");
2444 if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
2445 FILE_OP_ERROR(dest, "open");
2448 if (rename(dest_bak, dest) < 0)
2449 FILE_OP_ERROR(dest_bak, "rename");
2455 while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
2460 n_write = write(dest_fd, bufp, len);
2462 g_warning("writing to %s failed.\n", dest);
2467 if (rename(dest_bak, dest) < 0)
2468 FILE_OP_ERROR(dest_bak, "rename");
2481 if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
2482 g_warning("File copy from %s to %s failed.\n", src, dest);
2485 if (rename(dest_bak, dest) < 0)
2486 FILE_OP_ERROR(dest_bak, "rename");
2499 * Append src file body to the tail of dest file.
2500 * Now keep_backup has no effects.
2502 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2504 FILE *src_fp, *dest_fp;
2508 gboolean err = FALSE;
2510 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2511 FILE_OP_ERROR(src, "fopen");
2515 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2516 FILE_OP_ERROR(dest, "fopen");
2521 if (change_file_mode_rw(dest_fp, dest) < 0) {
2522 FILE_OP_ERROR(dest, "chmod");
2523 g_warning("can't change file mode\n");
2526 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2527 if (n_read < sizeof(buf) && ferror(src_fp))
2529 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2530 g_warning("writing to %s failed.\n", dest);
2538 if (ferror(src_fp)) {
2539 FILE_OP_ERROR(src, "fread");
2543 if (fclose(dest_fp) == EOF) {
2544 FILE_OP_ERROR(dest, "fclose");
2556 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2558 FILE *src_fp, *dest_fp;
2561 gchar *dest_bak = NULL;
2562 gboolean err = FALSE;
2564 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2565 FILE_OP_ERROR(src, "fopen");
2568 if (is_file_exist(dest)) {
2569 dest_bak = g_strconcat(dest, ".bak", NULL);
2570 if (rename_force(dest, dest_bak) < 0) {
2571 FILE_OP_ERROR(dest, "rename");
2578 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2579 FILE_OP_ERROR(dest, "fopen");
2582 if (rename_force(dest_bak, dest) < 0)
2583 FILE_OP_ERROR(dest_bak, "rename");
2589 if (change_file_mode_rw(dest_fp, dest) < 0) {
2590 FILE_OP_ERROR(dest, "chmod");
2591 g_warning("can't change file mode\n");
2594 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2595 if (n_read < sizeof(buf) && ferror(src_fp))
2597 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2598 g_warning("writing to %s failed.\n", dest);
2603 if (rename_force(dest_bak, dest) < 0)
2604 FILE_OP_ERROR(dest_bak, "rename");
2611 if (ferror(src_fp)) {
2612 FILE_OP_ERROR(src, "fread");
2616 if (fclose(dest_fp) == EOF) {
2617 FILE_OP_ERROR(dest, "fclose");
2624 if (rename_force(dest_bak, dest) < 0)
2625 FILE_OP_ERROR(dest_bak, "rename");
2631 if (keep_backup == FALSE && dest_bak)
2639 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2641 if (overwrite == FALSE && is_file_exist(dest)) {
2642 g_warning("move_file(): file %s already exists.", dest);
2646 if (rename_force(src, dest) == 0) return 0;
2648 if (EXDEV != errno) {
2649 FILE_OP_ERROR(src, "rename");
2653 if (copy_file(src, dest, FALSE) < 0) return -1;
2660 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2663 gint bytes_left, to_read;
2666 if (fseek(fp, offset, SEEK_SET) < 0) {
2671 bytes_left = length;
2672 to_read = MIN(bytes_left, sizeof(buf));
2674 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2675 if (n_read < to_read && ferror(fp))
2677 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2680 bytes_left -= n_read;
2681 if (bytes_left == 0)
2683 to_read = MIN(bytes_left, sizeof(buf));
2694 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2697 gboolean err = FALSE;
2699 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2700 FILE_OP_ERROR(dest, "fopen");
2704 if (change_file_mode_rw(dest_fp, dest) < 0) {
2705 FILE_OP_ERROR(dest, "chmod");
2706 g_warning("can't change file mode\n");
2709 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2712 if (!err && fclose(dest_fp) == EOF) {
2713 FILE_OP_ERROR(dest, "fclose");
2718 g_warning("writing to %s failed.\n", dest);
2726 /* convert line endings into CRLF. If the last line doesn't end with
2727 * linebreak, add it.
2729 gchar *canonicalize_str(const gchar *str)
2735 for (p = str; *p != '\0'; ++p) {
2742 if (p == str || *(p - 1) != '\n')
2745 out = outp = g_malloc(new_len + 1);
2746 for (p = str; *p != '\0'; ++p) {
2753 if (p == str || *(p - 1) != '\n') {
2762 gint canonicalize_file(const gchar *src, const gchar *dest)
2764 FILE *src_fp, *dest_fp;
2765 gchar buf[BUFFSIZE];
2767 gboolean err = FALSE;
2768 gboolean last_linebreak = FALSE;
2770 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2771 FILE_OP_ERROR(src, "fopen");
2775 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2776 FILE_OP_ERROR(dest, "fopen");
2781 if (change_file_mode_rw(dest_fp, dest) < 0) {
2782 FILE_OP_ERROR(dest, "chmod");
2783 g_warning("can't change file mode\n");
2786 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2790 if (len == 0) break;
2791 last_linebreak = FALSE;
2793 if (buf[len - 1] != '\n') {
2794 last_linebreak = TRUE;
2795 r = fputs(buf, dest_fp);
2796 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2797 r = fputs(buf, dest_fp);
2800 r = fwrite(buf, 1, len - 1, dest_fp);
2805 r = fputs("\r\n", dest_fp);
2809 g_warning("writing to %s failed.\n", dest);
2817 if (last_linebreak == TRUE) {
2818 if (fputs("\r\n", dest_fp) == EOF)
2822 if (ferror(src_fp)) {
2823 FILE_OP_ERROR(src, "fgets");
2827 if (fclose(dest_fp) == EOF) {
2828 FILE_OP_ERROR(dest, "fclose");
2840 gint canonicalize_file_replace(const gchar *file)
2844 tmp_file = get_tmp_file();
2846 if (canonicalize_file(file, tmp_file) < 0) {
2851 if (move_file(tmp_file, file, TRUE) < 0) {
2852 g_warning("can't replace %s .\n", file);
2862 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2864 FILE *src_fp, *dest_fp;
2865 gchar buf[BUFFSIZE];
2866 gboolean err = FALSE;
2868 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2869 FILE_OP_ERROR(src, "fopen");
2873 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2874 FILE_OP_ERROR(dest, "fopen");
2879 if (change_file_mode_rw(dest_fp, dest) < 0) {
2880 FILE_OP_ERROR(dest, "chmod");
2881 g_warning("can't change file mode\n");
2884 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2886 if (fputs(buf, dest_fp) == EOF) {
2887 g_warning("writing to %s failed.\n", dest);
2895 if (ferror(src_fp)) {
2896 FILE_OP_ERROR(src, "fgets");
2900 if (fclose(dest_fp) == EOF) {
2901 FILE_OP_ERROR(dest, "fclose");
2913 gint uncanonicalize_file_replace(const gchar *file)
2917 tmp_file = get_tmp_file();
2919 if (uncanonicalize_file(file, tmp_file) < 0) {
2924 if (move_file(tmp_file, file, TRUE) < 0) {
2925 g_warning("can't replace %s .\n", file);
2935 gchar *normalize_newlines(const gchar *str)
2937 const gchar *p = str;
2940 out = outp = g_malloc(strlen(str) + 1);
2941 for (p = str; *p != '\0'; ++p) {
2943 if (*(p + 1) != '\n')
2954 gchar *get_outgoing_rfc2822_str(FILE *fp)
2956 gchar buf[BUFFSIZE];
2960 str = g_string_new(NULL);
2962 /* output header part */
2963 while (fgets(buf, sizeof(buf), fp) != NULL) {
2965 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2972 else if (next != ' ' && next != '\t') {
2976 if (fgets(buf, sizeof(buf), fp) == NULL)
2980 g_string_append(str, buf);
2981 g_string_append(str, "\r\n");
2987 /* output body part */
2988 while (fgets(buf, sizeof(buf), fp) != NULL) {
2991 g_string_append_c(str, '.');
2992 g_string_append(str, buf);
2993 g_string_append(str, "\r\n");
2997 g_string_free(str, FALSE);
3003 * Create a new boundary in a way that it is very unlikely that this
3004 * will occur in the following text. It would be easy to ensure
3005 * uniqueness if everything is either quoted-printable or base64
3006 * encoded (note that conversion is allowed), but because MIME bodies
3007 * may be nested, it may happen that the same boundary has already
3008 * been used. We avoid scanning the message for conflicts and hope the
3011 * boundary := 0*69<bchars> bcharsnospace
3012 * bchars := bcharsnospace / " "
3013 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3014 * "+" / "_" / "," / "-" / "." /
3015 * "/" / ":" / "=" / "?"
3017 * some special characters removed because of buggy MTAs
3020 gchar *generate_mime_boundary(const gchar *prefix)
3022 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3023 "abcdefghijklmnopqrstuvwxyz"
3029 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3030 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3033 get_rfc822_date(buf_date, sizeof(buf_date));
3034 subst_char(buf_date, ' ', '_');
3035 subst_char(buf_date, ',', '_');
3036 subst_char(buf_date, ':', '_');
3038 return g_strdup_printf("%s_%s_%s", prefix ? prefix : "Multipart",
3039 buf_date, buf_uniq);
3042 gint change_file_mode_rw(FILE *fp, const gchar *file)
3045 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3047 return g_chmod(file, S_IRUSR|S_IWUSR);
3051 FILE *my_tmpfile(void)
3054 const gchar suffix[] = ".XXXXXX";
3055 const gchar *tmpdir;
3057 const gchar *progname;
3063 tmpdir = get_tmp_dir();
3064 tmplen = strlen(tmpdir);
3065 progname = g_get_prgname();
3066 if (progname == NULL)
3067 progname = "sylpheed-claws";
3068 proglen = strlen(progname);
3069 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3072 memcpy(fname, tmpdir, tmplen);
3073 fname[tmplen] = G_DIR_SEPARATOR;
3074 memcpy(fname + tmplen + 1, progname, proglen);
3075 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3077 fd = mkstemp(fname);
3083 fp = fdopen(fd, "w+b");
3088 #endif /* HAVE_MKSTEMP */
3093 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3097 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3098 fd = mkstemp(*filename);
3100 return fdopen(fd, "w+");
3103 FILE *str_open_as_stream(const gchar *str)
3108 g_return_val_if_fail(str != NULL, NULL);
3112 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3117 if (len == 0) return fp;
3119 if (fwrite(str, 1, len, fp) != len) {
3120 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3129 gint str_write_to_file(const gchar *str, const gchar *file)
3134 g_return_val_if_fail(str != NULL, -1);
3135 g_return_val_if_fail(file != NULL, -1);
3137 if ((fp = g_fopen(file, "wb")) == NULL) {
3138 FILE_OP_ERROR(file, "fopen");
3148 if (fwrite(str, 1, len, fp) != len) {
3149 FILE_OP_ERROR(file, "fwrite");
3155 if (fclose(fp) == EOF) {
3156 FILE_OP_ERROR(file, "fclose");
3164 gchar *file_read_to_str(const gchar *file)
3169 g_return_val_if_fail(file != NULL, NULL);
3171 if ((fp = g_fopen(file, "rb")) == NULL) {
3172 FILE_OP_ERROR(file, "fopen");
3176 str = file_read_stream_to_str(fp);
3183 gchar *file_read_stream_to_str(FILE *fp)
3190 g_return_val_if_fail(fp != NULL, NULL);
3192 array = g_byte_array_new();
3194 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3195 if (n_read < sizeof(buf) && ferror(fp))
3197 g_byte_array_append(array, buf, n_read);
3201 FILE_OP_ERROR("file stream", "fread");
3202 g_byte_array_free(array, TRUE);
3207 g_byte_array_append(array, buf, 1);
3208 str = (gchar *)array->data;
3209 g_byte_array_free(array, FALSE);
3211 if (!g_utf8_validate(str, -1, NULL)) {
3212 const gchar *src_codeset, *dest_codeset;
3214 src_codeset = conv_get_locale_charset_str();
3215 dest_codeset = CS_UTF_8;
3216 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3224 gint execute_async(gchar *const argv[])
3226 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3228 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3229 NULL, NULL, NULL, FALSE) == FALSE) {
3230 g_warning("Can't execute command: %s\n", argv[0]);
3237 gint execute_sync(gchar *const argv[])
3241 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3243 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3244 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3245 g_warning("Can't execute command: %s\n", argv[0]);
3250 if (WIFEXITED(status))
3251 return WEXITSTATUS(status);
3259 gint execute_command_line(const gchar *cmdline, gboolean async)
3264 debug_print("execute_command_line(): executing: %s\n", cmdline);
3266 argv = strsplit_with_quote(cmdline, " ", 0);
3269 ret = execute_async(argv);
3271 ret = execute_sync(argv);
3278 gchar *get_command_output(const gchar *cmdline)
3280 gchar *child_stdout;
3283 g_return_val_if_fail(cmdline != NULL, NULL);
3285 debug_print("get_command_output(): executing: %s\n", cmdline);
3287 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3289 g_warning("Can't execute command: %s\n", cmdline);
3293 return child_stdout;
3296 static gint is_unchanged_uri_char(char c)
3308 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3314 for(i = 0; i < strlen(uri) ; i++) {
3315 if (is_unchanged_uri_char(uri[i])) {
3316 if (k + 2 >= bufsize)
3318 encoded_uri[k++] = uri[i];
3321 char * hexa = "0123456789ABCDEF";
3323 if (k + 4 >= bufsize)
3325 encoded_uri[k++] = '%';
3326 encoded_uri[k++] = hexa[uri[i] / 16];
3327 encoded_uri[k++] = hexa[uri[i] % 16];
3333 gint open_uri(const gchar *uri, const gchar *cmdline)
3335 gchar buf[BUFFSIZE];
3337 gchar encoded_uri[BUFFSIZE];
3339 g_return_val_if_fail(uri != NULL, -1);
3341 /* an option to choose whether to use encode_uri or not ? */
3342 encode_uri(encoded_uri, BUFFSIZE, uri);
3345 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3346 !strchr(p + 2, '%'))
3347 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3350 g_warning("Open URI command line is invalid "
3351 "(there must be only one '%%s'): %s",
3353 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3356 execute_command_line(buf, TRUE);
3361 time_t remote_tzoffset_sec(const gchar *zone)
3363 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3369 time_t remoteoffset;
3371 strncpy(zone3, zone, 3);
3375 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3376 (c == '+' || c == '-')) {
3377 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3379 remoteoffset = -remoteoffset;
3380 } else if (!strncmp(zone, "UT" , 2) ||
3381 !strncmp(zone, "GMT", 2)) {
3383 } else if (strlen(zone3) == 3) {
3384 for (p = ustzstr; *p != '\0'; p += 3) {
3385 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3386 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3387 remoteoffset = iustz * 3600;
3393 } else if (strlen(zone3) == 1) {
3395 case 'Z': remoteoffset = 0; break;
3396 case 'A': remoteoffset = -1; break;
3397 case 'B': remoteoffset = -2; break;
3398 case 'C': remoteoffset = -3; break;
3399 case 'D': remoteoffset = -4; break;
3400 case 'E': remoteoffset = -5; break;
3401 case 'F': remoteoffset = -6; break;
3402 case 'G': remoteoffset = -7; break;
3403 case 'H': remoteoffset = -8; break;
3404 case 'I': remoteoffset = -9; break;
3405 case 'K': remoteoffset = -10; break; /* J is not used */
3406 case 'L': remoteoffset = -11; break;
3407 case 'M': remoteoffset = -12; break;
3408 case 'N': remoteoffset = 1; break;
3409 case 'O': remoteoffset = 2; break;
3410 case 'P': remoteoffset = 3; break;
3411 case 'Q': remoteoffset = 4; break;
3412 case 'R': remoteoffset = 5; break;
3413 case 'S': remoteoffset = 6; break;
3414 case 'T': remoteoffset = 7; break;
3415 case 'U': remoteoffset = 8; break;
3416 case 'V': remoteoffset = 9; break;
3417 case 'W': remoteoffset = 10; break;
3418 case 'X': remoteoffset = 11; break;
3419 case 'Y': remoteoffset = 12; break;
3420 default: remoteoffset = 0; break;
3422 remoteoffset = remoteoffset * 3600;
3426 return remoteoffset;
3429 time_t tzoffset_sec(time_t *now)
3435 lt = localtime(now);
3437 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3439 if (lt->tm_year < gmt.tm_year)
3441 else if (lt->tm_year > gmt.tm_year)
3443 else if (lt->tm_yday < gmt.tm_yday)
3445 else if (lt->tm_yday > gmt.tm_yday)
3448 if (off >= 24 * 60) /* should be impossible */
3449 off = 23 * 60 + 59; /* if not, insert silly value */
3450 if (off <= -24 * 60)
3451 off = -(23 * 60 + 59);
3456 /* calculate timezone offset */
3457 gchar *tzoffset(time_t *now)
3459 static gchar offset_string[6];
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)
3483 if (off >= 24 * 60) /* should be impossible */
3484 off = 23 * 60 + 59; /* if not, insert silly value */
3486 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3488 return offset_string;
3491 void get_rfc822_date(gchar *buf, gint len)
3495 gchar day[4], mon[4];
3496 gint dd, hh, mm, ss, yyyy;
3501 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3502 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3503 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3504 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3507 /* just a wrapper to suppress the warning of gcc about %c */
3508 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3509 const struct tm *tm)
3511 return strftime(s, max, format, tm);
3514 void debug_set_mode(gboolean mode)
3519 gboolean debug_get_mode(void)
3524 void debug_print_real(const gchar *format, ...)
3527 gchar buf[BUFFSIZE];
3529 if (!debug_mode) return;
3531 va_start(args, format);
3532 g_vsnprintf(buf, sizeof(buf), format, args);
3538 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3540 if (subject == NULL)
3543 subject += subject_get_prefix_length(subject);
3545 return g_hash_table_lookup(subject_table, subject);
3548 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3551 if (subject == NULL || *subject == 0)
3553 subject += subject_get_prefix_length(subject);
3554 g_hash_table_insert(subject_table, subject, data);
3557 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3559 if (subject == NULL)
3562 subject += subject_get_prefix_length(subject);
3563 g_hash_table_remove(subject_table, subject);
3567 *\brief Check if a string is prefixed with known (combinations)
3568 * of prefixes. The function assumes that each prefix
3569 * is terminated by zero or exactly _one_ space.
3571 *\param str String to check for a prefixes
3573 *\return int Number of chars in the prefix that should be skipped
3574 * for a "clean" subject line. If no prefix was found, 0
3577 int subject_get_prefix_length(const gchar *subject)
3579 /*!< Array with allowable reply prefixes regexps. */
3580 static const gchar * const prefixes[] = {
3581 "Re\\:", /* "Re:" */
3582 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3583 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3584 "Aw\\:", /* "Aw:" (German) */
3585 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3586 "Res\\:", /* "Res:" (Brazilian Outlook) */
3587 "Fw\\:", /* "Fw:" Forward */
3588 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3589 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3590 "Rif\\:" /* "Rif:" (Italian Outlook) */
3593 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3596 static regex_t regex;
3597 static gboolean init_;
3599 if (!subject) return 0;
3600 if (!*subject) return 0;
3603 GString *s = g_string_new("");
3605 for (n = 0; n < PREFIXES; n++)
3606 /* Terminate each prefix regexpression by a
3607 * "\ ?" (zero or ONE space), and OR them */
3608 g_string_append_printf(s, "(%s\\ ?)%s",
3613 g_string_prepend(s, "(");
3614 g_string_append(s, ")+"); /* match at least once */
3615 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3618 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3619 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3620 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3621 debug_print("Error compiling regexp %s\n", s->str);
3622 g_string_free(s, TRUE);
3626 g_string_free(s, TRUE);
3630 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3636 guint g_stricase_hash(gconstpointer gptr)
3638 guint hash_result = 0;
3641 for (str = gptr; str && *str; str++) {
3642 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3643 else hash_result += *str;
3649 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3651 const char *str1 = gptr1;
3652 const char *str2 = gptr2;
3654 return !g_utf8_collate(str1, str2);
3657 gint g_int_compare(gconstpointer a, gconstpointer b)
3659 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3662 gchar *generate_msgid(gchar *buf, gint len)
3671 addr = g_strconcat("@", get_domain_name(), NULL);
3673 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3674 lt->tm_year + 1900, lt->tm_mon + 1,
3675 lt->tm_mday, lt->tm_hour,
3676 lt->tm_min, lt->tm_sec,
3677 (guint) rand(), addr);
3684 quote_cmd_argument()
3686 return a quoted string safely usable in argument of a command.
3688 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3691 gint quote_cmd_argument(gchar * result, guint size,
3701 for(p = path ; * p != '\0' ; p ++) {
3703 if (isalnum((guchar)*p) || (* p == '/')) {
3704 if (remaining > 0) {
3710 result[size - 1] = '\0';
3715 if (remaining >= 2) {
3723 result[size - 1] = '\0';
3728 if (remaining > 0) {
3732 result[size - 1] = '\0';
3746 static void g_node_map_recursive(GNode *node, gpointer data)
3748 GNodeMapData *mapdata = (GNodeMapData *) data;
3750 GNodeMapData newmapdata;
3753 newdata = mapdata->func(node->data, mapdata->data);
3754 if (newdata != NULL) {
3755 newnode = g_node_new(newdata);
3756 g_node_append(mapdata->parent, newnode);
3758 newmapdata.parent = newnode;
3759 newmapdata.func = mapdata->func;
3760 newmapdata.data = mapdata->data;
3762 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3766 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3769 GNodeMapData mapdata;
3771 g_return_val_if_fail(node != NULL, NULL);
3772 g_return_val_if_fail(func != NULL, NULL);
3774 root = g_node_new(func(node->data, data));
3776 mapdata.parent = root;
3777 mapdata.func = func;
3778 mapdata.data = data;
3780 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3785 #define HEX_TO_INT(val, hex) \
3789 if ('0' <= c && c <= '9') { \
3791 } else if ('a' <= c && c <= 'f') { \
3792 val = c - 'a' + 10; \
3793 } else if ('A' <= c && c <= 'F') { \
3794 val = c - 'A' + 10; \
3800 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3807 if (hi == -1 || lo == -1)
3810 *out = (hi << 4) + lo;
3814 #define INT_TO_HEX(hex, val) \
3817 hex = '0' + (val); \
3819 hex = 'A' + (val) - 10; \
3822 void get_hex_str(gchar *out, guchar ch)
3826 INT_TO_HEX(hex, ch >> 4);
3828 INT_TO_HEX(hex, ch & 0x0f);
3834 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3836 #define G_PRINT_REF g_print
3840 *\brief Register ref counted pointer. It is based on GBoxed, so should
3841 * work with anything that uses the GType system. The semantics
3842 * are similar to a C++ auto pointer, with the exception that
3843 * C doesn't have automatic closure (calling destructors) when
3844 * exiting a block scope.
3845 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3846 * function directly.
3848 *\return GType A GType type.
3850 GType g_auto_pointer_register(void)
3852 static GType auto_pointer_type;
3853 if (!auto_pointer_type)
3855 g_boxed_type_register_static
3856 ("G_TYPE_AUTO_POINTER",
3857 (GBoxedCopyFunc) g_auto_pointer_copy,
3858 (GBoxedFreeFunc) g_auto_pointer_free);
3859 return auto_pointer_type;
3863 *\brief Structure with g_new() allocated pointer guarded by the
3866 typedef struct AutoPointerRef {
3867 void (*free) (gpointer);
3873 *\brief The auto pointer opaque structure that references the
3874 * pointer guard block.
3876 typedef struct AutoPointer {
3877 AutoPointerRef *ref;
3878 gpointer ptr; /*!< access to protected pointer */
3882 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3886 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3887 * ... when assigning, copying and freeing storage elements
3889 * gtk_list_store_new(N_S_COLUMNS,
3890 * G_TYPE_AUTO_POINTER,
3894 * Template *precious_data = g_new0(Template, 1);
3895 * g_pointer protect = g_auto_pointer_new(precious_data);
3897 * gtk_list_store_set(container, &iter,
3901 * ... the gtk_list_store has copied the pointer and
3902 * ... incremented its reference count, we should free
3903 * ... the auto pointer (in C++ a destructor would do
3904 * ... this for us when leaving block scope)
3906 * g_auto_pointer_free(protect);
3908 * ... gtk_list_store_set() now manages the data. When
3909 * ... *explicitly* requesting a pointer from the list
3910 * ... store, don't forget you get a copy that should be
3911 * ... freed with g_auto_pointer_free() eventually.
3915 *\param pointer Pointer to be guarded.
3917 *\return GAuto * Pointer that should be used in containers with
3920 GAuto *g_auto_pointer_new(gpointer p)
3922 AutoPointerRef *ref;
3928 ref = g_new0(AutoPointerRef, 1);
3929 ptr = g_new0(AutoPointer, 1);
3939 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3945 *\brief Allocate an autopointer using the passed \a free function to
3946 * free the guarded pointer
3948 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3955 aptr = g_auto_pointer_new(p);
3956 aptr->ref->free = free_;
3960 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3962 if (auto_ptr == NULL)
3964 return ((AutoPointer *) auto_ptr)->ptr;
3968 *\brief Copies an auto pointer by. It's mostly not necessary
3969 * to call this function directly, unless you copy/assign
3970 * the guarded pointer.
3972 *\param auto_ptr Auto pointer returned by previous call to
3973 * g_auto_pointer_new_XXX()
3975 *\return gpointer An auto pointer
3977 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3980 AutoPointerRef *ref;
3983 if (auto_ptr == NULL)
3988 newp = g_new0(AutoPointer, 1);
3991 newp->ptr = ref->pointer;
3995 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4001 *\brief Free an auto pointer
4003 void g_auto_pointer_free(GAuto *auto_ptr)
4006 AutoPointerRef *ref;
4008 if (auto_ptr == NULL)
4014 if (--(ref->cnt) == 0) {
4016 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4018 ref->free(ref->pointer);
4023 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4028 void replace_returns(gchar *str)
4033 while (strstr(str, "\n")) {
4034 *strstr(str, "\n") = ' ';
4036 while (strstr(str, "\r")) {
4037 *strstr(str, "\r") = ' ';
4041 /* get_uri_part() - retrieves a URI starting from scanpos.
4042 Returns TRUE if succesful */
4043 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4044 const gchar **bp, const gchar **ep)
4048 g_return_val_if_fail(start != NULL, FALSE);
4049 g_return_val_if_fail(scanpos != NULL, FALSE);
4050 g_return_val_if_fail(bp != NULL, FALSE);
4051 g_return_val_if_fail(ep != NULL, FALSE);
4055 /* find end point of URI */
4056 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4057 if (!isgraph(*(const guchar *)ep_) ||
4058 !IS_ASCII(*(const guchar *)ep_) ||
4059 strchr("[]{}()<>\"", *ep_))
4063 /* no punctuation at end of string */
4065 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4066 * should pass some URI type to this function and decide on that whether
4067 * to perform punctuation stripping */
4069 #define IS_REAL_PUNCT(ch) (ispunct(ch) && ((ch) != '/'))
4071 for (; ep_ - 1 > scanpos + 1 &&
4072 IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
4076 #undef IS_REAL_PUNCT
4083 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4085 return g_strndup(bp, ep - bp);
4088 /* valid mail address characters */
4089 #define IS_RFC822_CHAR(ch) \
4093 !g_ascii_isspace(ch) && \
4094 !strchr("(),;<>\"", (ch)))
4096 /* alphabet and number within 7bit ASCII */
4097 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4098 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4100 static GHashTable *create_domain_tab(void)
4102 static const gchar *toplvl_domains [] = {
4104 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4105 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4106 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4107 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4108 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4109 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4110 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4111 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4112 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4113 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4114 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4115 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4116 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4117 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4118 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4119 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4120 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4121 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4122 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4123 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4124 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4125 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4126 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4127 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4128 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4129 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4132 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4134 g_return_val_if_fail(htab, NULL);
4135 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4136 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4140 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4142 const gint MAX_LVL_DOM_NAME_LEN = 6;
4143 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4144 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4147 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4150 for (p = buf; p < m && first < last; *p++ = *first++)
4154 return g_hash_table_lookup(tab, buf) != NULL;
4157 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4158 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4159 const gchar **bp, const gchar **ep)
4161 /* more complex than the uri part because we need to scan back and forward starting from
4162 * the scan position. */
4163 gboolean result = FALSE;
4164 const gchar *bp_ = NULL;
4165 const gchar *ep_ = NULL;
4166 static GHashTable *dom_tab;
4167 const gchar *last_dot = NULL;
4168 const gchar *prelast_dot = NULL;
4169 const gchar *last_tld_char = NULL;
4171 /* the informative part of the email address (describing the name
4172 * of the email address owner) may contain quoted parts. the
4173 * closure stack stores the last encountered quotes. */
4174 gchar closure_stack[128];
4175 gchar *ptr = closure_stack;
4177 g_return_val_if_fail(start != NULL, FALSE);
4178 g_return_val_if_fail(scanpos != NULL, FALSE);
4179 g_return_val_if_fail(bp != NULL, FALSE);
4180 g_return_val_if_fail(ep != NULL, FALSE);
4183 dom_tab = create_domain_tab();
4184 g_return_val_if_fail(dom_tab, FALSE);
4186 /* scan start of address */
4187 for (bp_ = scanpos - 1;
4188 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4191 /* TODO: should start with an alnum? */
4193 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4196 if (bp_ != scanpos) {
4197 /* scan end of address */
4198 for (ep_ = scanpos + 1;
4199 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4201 prelast_dot = last_dot;
4203 if (*(last_dot + 1) == '.') {
4204 if (prelast_dot == NULL)
4206 last_dot = prelast_dot;
4211 /* TODO: really should terminate with an alnum? */
4212 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4217 if (last_dot == NULL)
4219 if (last_dot >= ep_)
4220 last_dot = prelast_dot;
4221 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4225 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4226 if (*last_tld_char == '?')
4229 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4236 if (!result) return FALSE;
4238 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4239 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4240 && IS_RFC822_CHAR(*(ep_ + 3))) {
4241 /* this informative part with an @ in it is
4242 * followed by the email address */
4245 /* go to matching '>' (or next non-rfc822 char, like \n) */
4246 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4249 /* include the bracket */
4250 if (*ep_ == '>') ep_++;
4252 /* include the leading quote */
4260 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4261 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4264 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4265 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4268 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4269 #define IN_STACK() (ptr > closure_stack)
4270 /* has underrun check */
4271 #define POP_STACK() if(IN_STACK()) --ptr
4272 /* has overrun check */
4273 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4274 /* has underrun check */
4275 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4279 /* scan for the informative part. */
4280 for (bp_ -= 2; bp_ >= start; bp_--) {
4281 /* if closure on the stack keep scanning */
4282 if (PEEK_STACK() == *bp_) {
4286 if (*bp_ == '\'' || *bp_ == '"') {
4291 /* if nothing in the closure stack, do the special conditions
4292 * the following if..else expression simply checks whether
4293 * a token is acceptable. if not acceptable, the clause
4294 * should terminate the loop with a 'break' */
4295 if (!PEEK_STACK()) {
4297 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4298 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4299 /* hyphens are allowed, but only in
4301 } else if (!strchr(",;:=?./+<>!&\r\n\t", *bp_)) {
4302 /* but anything not being a punctiation
4305 break; /* anything else is rejected */
4318 /* scan forward (should start with an alnum) */
4319 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4329 #undef IS_ASCII_ALNUM
4330 #undef IS_RFC822_CHAR
4332 gchar *make_email_string(const gchar *bp, const gchar *ep)
4334 /* returns a mailto: URI; mailto: is also used to detect the
4335 * uri type later on in the button_pressed signal handler */
4339 tmp = g_strndup(bp, ep - bp);
4340 result = g_strconcat("mailto:", tmp, NULL);
4346 gchar *make_http_string(const gchar *bp, const gchar *ep)
4348 /* returns an http: URI; */
4352 tmp = g_strndup(bp, ep - bp);
4353 result = g_strconcat("http://", tmp, NULL);