2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
46 #include <sys/types.h>
48 # include <sys/wait.h>
55 #include <sys/utsname.h>
66 #include "../codeconv.h"
70 static gboolean debug_mode = FALSE;
72 static GSList *tempfiles=NULL;
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
80 if (G_WIN32_HAVE_WIDECHAR_API()) {
85 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
91 retval = _wchdir(wpath);
103 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104 if (cp_path == NULL) {
109 retval = chdir(cp_path);
122 gint g_chmod(const gchar *path, gint mode)
125 if (G_WIN32_HAVE_WIDECHAR_API()) {
130 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
136 retval = _wchmod(wpath, mode);
148 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149 if (cp_path == NULL) {
154 retval = chmod(cp_path, mode);
163 return chmod(path, mode);
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
170 gint mkstemp_name(const gchar *template, gchar **name_used)
172 static gulong count=0; /* W32-_mktemp only supports up to 27
176 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
178 (S_IRUSR | S_IWUSR));
180 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
182 perror(g_strdup_printf("cant create %s",*name_used));
188 #endif /* G_OS_WIN32 */
191 gint mkstemp(const gchar *template)
194 gint res = mkstemp_name(template, &dummyname);
198 #endif /* G_OS_WIN32 */
200 void list_free_strings(GList *list)
202 list = g_list_first(list);
204 while (list != NULL) {
210 void slist_free_strings(GSList *list)
212 while (list != NULL) {
218 GSList *slist_concat_unique (GSList *first, GSList *second)
226 } else if (second == NULL)
229 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230 if (g_slist_find(ret, tmp->data) == NULL)
231 ret = g_slist_prepend(ret, tmp->data);
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
241 void hash_free_strings(GHashTable *table)
243 g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
252 void hash_free_value_mem(GHashTable *table)
254 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
259 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
262 guint str_case_hash(gconstpointer key)
264 const gchar *p = key;
268 h = g_ascii_tolower(h);
269 for (p += 1; *p != '\0'; p++)
270 h = (h << 5) - h + g_ascii_tolower(*p);
276 void ptr_array_free_strings(GPtrArray *array)
281 g_return_if_fail(array != NULL);
283 for (i = 0; i < array->len; i++) {
284 str = g_ptr_array_index(array, i);
289 gboolean str_find(const gchar *haystack, const gchar *needle)
291 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
296 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
301 return strcmp(haystack, needle) == 0;
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
306 return g_ascii_strcasecmp(haystack, needle) == 0;
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 gchar *to_human_readable(off_t size)
339 static gchar str[14];
342 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
343 else if (size >> 10 < 1024)
344 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
345 else if (size >> 20 < 1024)
346 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
348 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
353 /* strcmp with NULL-checking */
354 gint strcmp2(const gchar *s1, const gchar *s2)
356 if (s1 == NULL || s2 == NULL)
359 return strcmp(s1, s2);
361 /* strstr with NULL-checking */
362 gchar *strstr2(const gchar *s1, const gchar *s2)
364 if (s1 == NULL || s2 == NULL)
367 return strstr(s1, s2);
370 gint path_cmp(const gchar *s1, const gchar *s2)
375 gchar *s1buf, *s2buf;
378 if (s1 == NULL || s2 == NULL) return -1;
379 if (*s1 == '\0' || *s2 == '\0') return -1;
382 s1buf = g_strdup (s1);
383 s2buf = g_strdup (s2);
384 subst_char (s1buf, '/', G_DIR_SEPARATOR);
385 subst_char (s2buf, '/', G_DIR_SEPARATOR);
388 #endif /* !G_OS_WIN32 */
393 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
394 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
396 rc = strncmp(s1, s2, MAX(len1, len2));
400 #endif /* !G_OS_WIN32 */
404 /* remove trailing return code */
405 gchar *strretchomp(gchar *str)
409 if (!*str) return str;
411 for (s = str + strlen(str) - 1;
412 s >= str && (*s == '\n' || *s == '\r');
419 /* remove trailing character */
420 gchar *strtailchomp(gchar *str, gchar tail_char)
424 if (!*str) return str;
425 if (tail_char == '\0') return str;
427 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
433 /* remove CR (carriage return) */
434 gchar *strcrchomp(gchar *str)
438 if (!*str) return str;
440 s = str + strlen(str) - 1;
441 if (*s == '\n' && s > str && *(s - 1) == '\r') {
449 void file_strip_crs(const gchar *file)
451 FILE *fp = NULL, *outfp = NULL;
453 gchar *out = get_tmp_file();
457 fp = fopen(file, "rb");
461 outfp = fopen(out, "wb");
467 while (fgets(buf, sizeof (buf), fp) != NULL) {
474 rename_force(out, file);
479 /* Similar to `strstr' but this function ignores the case of both strings. */
480 gchar *strcasestr(const gchar *haystack, const gchar *needle)
482 register size_t haystack_len, needle_len;
484 haystack_len = strlen(haystack);
485 needle_len = strlen(needle);
487 if (haystack_len < needle_len || needle_len == 0)
490 while (haystack_len >= needle_len) {
491 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
492 return (gchar *)haystack;
502 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
503 gconstpointer needle, size_t needlelen)
505 const gchar *haystack_ = (const gchar *)haystack;
506 const gchar *needle_ = (const gchar *)needle;
507 const gchar *haystack_cur = (const gchar *)haystack;
510 return memchr(haystack_, *needle_, haystacklen);
512 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
514 if (haystacklen - (haystack_cur - haystack_) < needlelen)
516 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
517 return (gpointer)haystack_cur;
525 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
526 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
528 register const gchar *s = src;
529 register gchar *d = dest;
539 int iswalnum(wint_t wc)
541 return g_ascii_isalnum((int)wc);
546 int iswspace(wint_t wc)
548 return g_ascii_isspace((int)wc);
553 wint_t towlower(wint_t wc)
555 if (wc >= L'A' && wc <= L'Z')
556 return wc + L'a' - L'A';
563 size_t wcslen(const wchar_t *s)
575 /* Copy SRC to DEST. */
576 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
584 } while (c != L'\0');
591 /* Copy no more than N wide-characters of SRC to DEST. */
592 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
602 } while (c != L'\0');
613 /* Duplicate S, returning an identical malloc'd string. */
614 wchar_t *wcsdup(const wchar_t *s)
619 new_str = g_new(wchar_t, wcslen(s) + 1);
627 /* Duplicate no more than N wide-characters of S,
628 returning an identical malloc'd string. */
629 wchar_t *wcsndup(const wchar_t *s, size_t n)
634 new_str = g_new(wchar_t, n + 1);
635 wcsncpy(new_str, s, n);
636 new_str[n] = (wchar_t)0;
643 wchar_t *strdup_mbstowcs(const gchar *s)
648 new_str = g_new(wchar_t, strlen(s) + 1);
649 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
653 new_str = g_realloc(new_str,
654 sizeof(wchar_t) * (wcslen(new_str) + 1));
661 gchar *strdup_wcstombs(const wchar_t *s)
667 len = wcslen(s) * MB_CUR_MAX + 1;
668 new_str = g_new(gchar, len);
669 if (wcstombs(new_str, s, len) < 0) {
673 new_str = g_realloc(new_str, strlen(new_str) + 1);
680 /* Compare S1 and S2, ignoring case. */
681 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
687 c1 = towlower(*s1++);
688 c2 = towlower(*s2++);
691 else if (c1 == 0 && c2 == 0)
698 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
699 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
701 register size_t haystack_len, needle_len;
703 haystack_len = wcslen(haystack);
704 needle_len = wcslen(needle);
706 if (haystack_len < needle_len || needle_len == 0)
709 while (haystack_len >= needle_len) {
710 if (!wcsncasecmp(haystack, needle, needle_len))
711 return (wchar_t *)haystack;
721 gint get_mbs_len(const gchar *s)
731 mb_len = g_utf8_skip[*(guchar *)p];
743 /* Examine if next block is non-ASCII string */
744 gboolean is_next_nonascii(const gchar *s)
748 /* skip head space */
749 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
751 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
752 if (*(guchar *)p > 127 || *(guchar *)p < 32)
759 gint get_next_word_len(const gchar *s)
763 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
769 /* compare subjects */
770 gint subject_compare(const gchar *s1, const gchar *s2)
774 if (!s1 || !s2) return -1;
775 if (!*s1 || !*s2) return -1;
777 Xstrdup_a(str1, s1, return -1);
778 Xstrdup_a(str2, s2, return -1);
780 trim_subject_for_compare(str1);
781 trim_subject_for_compare(str2);
783 if (!*str1 || !*str2) return -1;
785 return strcmp(str1, str2);
788 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
792 if (!s1 || !s2) return -1;
794 Xstrdup_a(str1, s1, return -1);
795 Xstrdup_a(str2, s2, return -1);
797 trim_subject_for_sort(str1);
798 trim_subject_for_sort(str2);
800 return g_utf8_collate(str1, str2);
803 void trim_subject_for_compare(gchar *str)
807 eliminate_parenthesis(str, '[', ']');
808 eliminate_parenthesis(str, '(', ')');
811 srcp = str + subject_get_prefix_length(str);
813 memmove(str, srcp, strlen(srcp) + 1);
816 void trim_subject_for_sort(gchar *str)
822 srcp = str + subject_get_prefix_length(str);
824 memmove(str, srcp, strlen(srcp) + 1);
827 void trim_subject(gchar *str)
829 register gchar *srcp;
835 srcp = str + subject_get_prefix_length(str);
840 } else if (*srcp == '(') {
852 else if (*srcp == cl)
859 while (g_ascii_isspace(*srcp)) srcp++;
860 memmove(str, srcp, strlen(srcp) + 1);
863 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
865 register gchar *srcp, *destp;
870 while ((destp = strchr(destp, op))) {
876 else if (*srcp == cl)
882 while (g_ascii_isspace(*srcp)) srcp++;
883 memmove(destp, srcp, strlen(srcp) + 1);
887 void extract_parenthesis(gchar *str, gchar op, gchar cl)
889 register gchar *srcp, *destp;
894 while ((srcp = strchr(destp, op))) {
897 memmove(destp, srcp + 1, strlen(srcp));
902 else if (*destp == cl)
914 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
917 register gchar *srcp, *destp;
919 gboolean in_quote = FALSE;
923 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
926 memmove(destp, srcp + 1, strlen(srcp));
929 if (*destp == op && !in_quote)
931 else if (*destp == cl && !in_quote)
933 else if (*destp == quote_chr)
945 void eliminate_quote(gchar *str, gchar quote_chr)
947 register gchar *srcp, *destp;
951 while ((destp = strchr(destp, quote_chr))) {
952 if ((srcp = strchr(destp + 1, quote_chr))) {
954 while (g_ascii_isspace(*srcp)) srcp++;
955 memmove(destp, srcp, strlen(srcp) + 1);
963 void extract_quote(gchar *str, gchar quote_chr)
967 if ((str = strchr(str, quote_chr))) {
969 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
970 memmove(p - 1, p, strlen(p) + 1);
975 memmove(str, str + 1, p - str);
980 void eliminate_address_comment(gchar *str)
982 register gchar *srcp, *destp;
987 while ((destp = strchr(destp, '"'))) {
988 if ((srcp = strchr(destp + 1, '"'))) {
993 while (g_ascii_isspace(*srcp)) srcp++;
994 memmove(destp, srcp, strlen(srcp) + 1);
1004 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
1010 else if (*srcp == ')')
1016 while (g_ascii_isspace(*srcp)) srcp++;
1017 memmove(destp, srcp, strlen(srcp) + 1);
1021 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1023 gboolean in_quote = FALSE;
1026 if (*str == c && !in_quote)
1027 return (gchar *)str;
1028 if (*str == quote_chr)
1036 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1038 gboolean in_quote = FALSE;
1041 p = str + strlen(str) - 1;
1043 if (*p == c && !in_quote)
1045 if (*p == quote_chr)
1053 void extract_address(gchar *str)
1055 eliminate_address_comment(str);
1056 if (strchr_with_skip_quote(str, '"', '<'))
1057 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1061 void extract_list_id_str(gchar *str)
1063 if (strchr_with_skip_quote(str, '"', '<'))
1064 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1068 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1073 if (!str) return addr_list;
1075 Xstrdup_a(work, str, return addr_list);
1078 eliminate_address_comment(work);
1081 while (workp && *workp) {
1084 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1090 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1091 extract_parenthesis_with_skip_quote
1092 (workp, '"', '<', '>');
1096 addr_list = g_slist_append(addr_list, g_strdup(workp));
1104 GSList *address_list_append(GSList *addr_list, const gchar *str)
1106 return address_list_append_real(addr_list, str, TRUE);
1109 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1111 return address_list_append_real(addr_list, str, FALSE);
1114 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1118 if (!str) return msgid_list;
1121 while (strp && *strp) {
1122 const gchar *start, *end;
1125 if ((start = strchr(strp, '<')) != NULL) {
1126 end = strchr(start + 1, '>');
1131 msgid = g_strndup(start + 1, end - start - 1);
1134 msgid_list = g_slist_prepend(msgid_list, msgid);
1144 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1148 list = references_list_prepend(NULL, str);
1149 list = g_slist_reverse(list);
1150 msgid_list = g_slist_concat(msgid_list, list);
1155 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1160 if (!str) return group_list;
1162 Xstrdup_a(work, str, return group_list);
1166 while (workp && *workp) {
1169 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1177 group_list = g_slist_append(group_list,
1186 GList *add_history(GList *list, const gchar *str)
1190 g_return_val_if_fail(str != NULL, list);
1192 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1195 list = g_list_remove(list, old->data);
1196 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1199 last = g_list_last(list);
1202 list = g_list_remove(list, last->data);
1206 list = g_list_prepend(list, g_strdup(str));
1211 void remove_return(gchar *str)
1213 register gchar *p = str;
1216 if (*p == '\n' || *p == '\r')
1217 memmove(p, p + 1, strlen(p));
1223 void remove_space(gchar *str)
1225 register gchar *p = str;
1230 while (g_ascii_isspace(*(p + spc)))
1233 memmove(p, p + spc, strlen(p + spc) + 1);
1239 void unfold_line(gchar *str)
1241 register gchar *p = str;
1245 if (*p == '\n' || *p == '\r') {
1248 while (g_ascii_isspace(*(p + spc)))
1251 memmove(p, p + spc, strlen(p + spc) + 1);
1257 void subst_char(gchar *str, gchar orig, gchar subst)
1259 register gchar *p = str;
1268 void subst_chars(gchar *str, gchar *orig, gchar subst)
1270 register gchar *p = str;
1273 if (strchr(orig, *p) != NULL)
1279 void subst_for_filename(gchar *str)
1284 subst_chars(str, "\t\r\n\\/*:", '_');
1286 subst_chars(str, "\t\r\n\\/*", '_');
1290 void subst_for_shellsafe_filename(gchar *str)
1294 subst_for_filename(str);
1295 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1298 gboolean is_header_line(const gchar *str)
1300 if (str[0] == ':') return FALSE;
1302 while (*str != '\0' && *str != ' ') {
1311 gboolean is_ascii_str(const gchar *str)
1313 const guchar *p = (const guchar *)str;
1315 while (*p != '\0') {
1316 if (*p != '\t' && *p != ' ' &&
1317 *p != '\r' && *p != '\n' &&
1318 (*p < 32 || *p >= 127))
1326 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1328 const gchar *first_pos;
1329 const gchar *last_pos;
1330 const gchar *p = str;
1331 gint quote_level = -1;
1333 /* speed up line processing by only searching to the last '>' */
1334 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1335 /* skip a line if it contains a '<' before the initial '>' */
1336 if (memchr(str, '<', first_pos - str) != NULL)
1338 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1342 while (p <= last_pos) {
1343 while (p < last_pos) {
1344 if (g_ascii_isspace(*p))
1350 if (strchr(quote_chars, *p))
1352 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1353 /* any characters are allowed except '-' and space */
1355 && !strchr(quote_chars, *p)
1356 && !g_ascii_isspace(*p)
1359 if (strchr(quote_chars, *p))
1371 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1373 const gchar *p = str, *q;
1374 gint cur_line = 0, len;
1376 while ((q = strchr(p, '\n')) != NULL) {
1378 if (len > max_chars) {
1388 if (len > max_chars) {
1397 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1399 gchar * position = NULL;
1400 gchar * tmp_pos = NULL;
1403 if (quote_chars == NULL)
1406 for (i = 0; i < strlen(quote_chars); i++) {
1407 tmp_pos = strchr (str, quote_chars[i]);
1409 || (tmp_pos != NULL && position >= tmp_pos) )
1415 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1417 gchar * position = NULL;
1418 gchar * tmp_pos = NULL;
1421 if (quote_chars == NULL)
1424 for (i = 0; i < strlen(quote_chars); i++) {
1425 tmp_pos = strrchr (str, quote_chars[i]);
1427 || (tmp_pos != NULL && position <= tmp_pos) )
1433 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1435 register guint haystack_len, needle_len;
1436 gboolean in_squote = FALSE, in_dquote = FALSE;
1438 haystack_len = strlen(haystack);
1439 needle_len = strlen(needle);
1441 if (haystack_len < needle_len || needle_len == 0)
1444 while (haystack_len >= needle_len) {
1445 if (!in_squote && !in_dquote &&
1446 !strncmp(haystack, needle, needle_len))
1447 return (gchar *)haystack;
1449 /* 'foo"bar"' -> foo"bar"
1450 "foo'bar'" -> foo'bar' */
1451 if (*haystack == '\'') {
1454 else if (!in_dquote)
1456 } else if (*haystack == '\"') {
1459 else if (!in_squote)
1470 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1473 gchar quote_chr = '"';
1475 gboolean in_quote = FALSE;
1479 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1483 if (*p == op && !in_quote)
1485 else if (*p == cl && !in_quote)
1487 else if (*p == quote_chr)
1500 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1503 GSList *string_list = NULL, *slist;
1505 const gchar *s_op, *s_cl;
1508 g_return_val_if_fail(str != NULL, NULL);
1511 max_tokens = G_MAXINT;
1513 s_op = strchr_with_skip_quote(str, '"', op);
1514 if (!s_op) return NULL;
1516 s_cl = strchr_parenthesis_close(str, op, cl);
1524 new_string = g_new(gchar, len + 1);
1525 strncpy(new_string, str, len);
1526 new_string[len] = 0;
1527 string_list = g_slist_prepend(string_list, new_string);
1531 while (*str && g_ascii_isspace(*str)) str++;
1533 string_list = g_slist_prepend(string_list,
1536 s_op = strchr_with_skip_quote(str, '"', op);
1537 if (!--max_tokens || !s_op) break;
1541 s_cl = strchr_parenthesis_close(str, op, cl);
1542 } while (--max_tokens && s_cl);
1545 str_array = g_new(gchar*, n);
1549 str_array[i--] = NULL;
1550 for (slist = string_list; slist; slist = slist->next)
1551 str_array[i--] = slist->data;
1553 g_slist_free(string_list);
1558 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1561 GSList *string_list = NULL, *slist;
1562 gchar **str_array, *s, *new_str;
1563 guint i, n = 1, len;
1565 g_return_val_if_fail(str != NULL, NULL);
1566 g_return_val_if_fail(delim != NULL, NULL);
1569 max_tokens = G_MAXINT;
1571 s = strstr_with_skip_quote(str, delim);
1573 guint delimiter_len = strlen(delim);
1577 new_str = g_strndup(str, len);
1579 if (new_str[0] == '\'' || new_str[0] == '\"') {
1580 if (new_str[len - 1] == new_str[0]) {
1581 new_str[len - 1] = '\0';
1582 memmove(new_str, new_str + 1, len - 1);
1585 string_list = g_slist_prepend(string_list, new_str);
1587 str = s + delimiter_len;
1588 s = strstr_with_skip_quote(str, delim);
1589 } while (--max_tokens && s);
1593 new_str = g_strdup(str);
1594 if (new_str[0] == '\'' || new_str[0] == '\"') {
1596 if (new_str[len - 1] == new_str[0]) {
1597 new_str[len - 1] = '\0';
1598 memmove(new_str, new_str + 1, len - 1);
1601 string_list = g_slist_prepend(string_list, new_str);
1605 str_array = g_new(gchar*, n);
1609 str_array[i--] = NULL;
1610 for (slist = string_list; slist; slist = slist->next)
1611 str_array[i--] = slist->data;
1613 g_slist_free(string_list);
1618 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1620 gchar *abbrev_group;
1622 const gchar *p = group;
1625 g_return_val_if_fail(group != NULL, NULL);
1627 last = group + strlen(group);
1628 abbrev_group = ap = g_malloc(strlen(group) + 1);
1633 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1635 while (*p != '.') p++;
1638 return abbrev_group;
1643 return abbrev_group;
1646 gchar *trim_string(const gchar *str, gint len)
1648 const gchar *p = str;
1653 if (!str) return NULL;
1654 if (strlen(str) <= len)
1655 return g_strdup(str);
1656 if (g_utf8_validate(str, -1, NULL) == FALSE)
1657 return g_strdup(str);
1659 while (*p != '\0') {
1660 mb_len = g_utf8_skip[*(guchar *)p];
1663 else if (new_len + mb_len > len)
1670 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1671 return g_strconcat(new_str, "...", NULL);
1674 GList *uri_list_extract_filenames(const gchar *uri_list)
1676 GList *result = NULL;
1678 gchar *escaped_utf8uri;
1684 while (g_ascii_isspace(*p)) p++;
1685 if (!strncmp(p, "file:", 5)) {
1688 while (*q && *q != '\n' && *q != '\r') q++;
1691 gchar *file, *locale_file = NULL;
1693 while (q > p && g_ascii_isspace(*q))
1695 Xalloca(escaped_utf8uri, q - p + 2,
1697 Xalloca(file, q - p + 2,
1700 strncpy(escaped_utf8uri, p, q - p + 1);
1701 escaped_utf8uri[q - p + 1] = '\0';
1702 decode_uri(file, escaped_utf8uri);
1704 * g_filename_from_uri() rejects escaped/locale encoded uri
1705 * string which come from Nautilus.
1707 if (g_utf8_validate(file, -1, NULL))
1709 = conv_codeset_strdup(
1712 conv_get_locale_charset_str());
1714 locale_file = g_strdup(file + 5);
1715 result = g_list_append(result, locale_file);
1719 p = strchr(p, '\n');
1726 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1729 static gint axtoi(const gchar *hexstr)
1731 gint hi, lo, result;
1734 if ('0' <= hi && hi <= '9') {
1737 if ('a' <= hi && hi <= 'f') {
1740 if ('A' <= hi && hi <= 'F') {
1745 if ('0' <= lo && lo <= '9') {
1748 if ('a' <= lo && lo <= 'f') {
1751 if ('A' <= lo && lo <= 'F') {
1754 result = lo + (16 * hi);
1758 gboolean is_uri_string(const gchar *str)
1760 while (str && *str && g_ascii_isspace(*str))
1762 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1763 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1764 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1765 g_ascii_strncasecmp(str, "www.", 4) == 0);
1768 gchar *get_uri_path(const gchar *uri)
1770 while (uri && *uri && g_ascii_isspace(*uri))
1772 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1773 return (gchar *)(uri + 7);
1774 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1775 return (gchar *)(uri + 8);
1776 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1777 return (gchar *)(uri + 6);
1779 return (gchar *)uri;
1782 gint get_uri_len(const gchar *str)
1786 if (is_uri_string(str)) {
1787 for (p = str; *p != '\0'; p++) {
1788 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1797 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1798 * plusses, and escape characters are used)
1800 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1802 gchar *dec = decoded_uri;
1803 const gchar *enc = encoded_uri;
1808 if (isxdigit((guchar)enc[0]) &&
1809 isxdigit((guchar)enc[1])) {
1827 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1828 gchar **subject, gchar **body)
1833 Xstrdup_a(tmp_mailto, mailto, return -1);
1835 if (!strncmp(tmp_mailto, "mailto:", 7))
1838 p = strchr(tmp_mailto, '?');
1845 *to = g_strdup(tmp_mailto);
1848 gchar *field, *value;
1865 if (*value == '\0') continue;
1867 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1868 *cc = g_strdup(value);
1869 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1870 *bcc = g_strdup(value);
1871 } else if (subject && !*subject &&
1872 !g_ascii_strcasecmp(field, "subject")) {
1873 *subject = g_malloc(strlen(value) + 1);
1874 decode_uri(*subject, value);
1875 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1876 *body = g_malloc(strlen(value) + 1);
1877 decode_uri(*body, value);
1886 #include <windows.h>
1887 #ifndef CSIDL_APPDATA
1888 #define CSIDL_APPDATA 0x001a
1890 #ifndef CSIDL_LOCAL_APPDATA
1891 #define CSIDL_LOCAL_APPDATA 0x001c
1893 #ifndef CSIDL_FLAG_CREATE
1894 #define CSIDL_FLAG_CREATE 0x8000
1896 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1900 w32_strerror (int w32_errno)
1902 static char strerr[256];
1903 int ec = (int)GetLastError ();
1907 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1908 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1909 strerr, DIM (strerr)-1, NULL);
1913 static __inline__ void *
1914 dlopen (const char * name, int flag)
1916 void * hd = LoadLibrary (name);
1920 static __inline__ void *
1921 dlsym (void * hd, const char * sym)
1925 void * fnc = GetProcAddress (hd, sym);
1934 static __inline__ const char *
1937 return w32_strerror (0);
1941 static __inline__ int
1953 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1955 static int initialized;
1956 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1960 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1966 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1968 handle = dlopen (dllnames[i], RTLD_LAZY);
1971 func = dlsym (handle, "SHGetFolderPathA");
1982 return func (a,b,c,d,e);
1987 /* Returns a static string with the directroy from which the module
1988 has been loaded. Returns an empty string on error. */
1989 static char *w32_get_module_dir(void)
1991 static char *moddir;
1994 char name[MAX_PATH+10];
1997 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
2000 p = strrchr (name, '\\');
2006 moddir = g_strdup (name);
2010 #endif /* G_OS_WIN32 */
2012 /* Return a static string with the locale dir. */
2013 const gchar *get_locale_dir(void)
2015 static gchar *loc_dir;
2019 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
2020 "\\share\\locale", NULL);
2023 loc_dir = LOCALEDIR;
2029 const gchar *get_home_dir(void)
2032 static char home_dir[MAX_PATH] = "";
2034 if (home_dir[0] == '\0') {
2035 if (w32_shgetfolderpath
2036 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
2037 NULL, 0, home_dir) < 0)
2038 strcpy (home_dir, "C:\\Sylpheed");
2042 static const gchar *homeenv = NULL;
2047 if (!homeenv && g_getenv("HOME") != NULL)
2048 homeenv = g_strdup(g_getenv("HOME"));
2050 homeenv = g_get_home_dir();
2056 const gchar *get_rc_dir(void)
2058 static gchar *rc_dir = NULL;
2061 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2067 const gchar *get_mail_base_dir(void)
2070 static gchar *mail_base_dir = NULL;
2073 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2076 return mail_base_dir;
2078 return get_home_dir();
2082 const gchar *get_news_cache_dir(void)
2084 static gchar *news_cache_dir = NULL;
2086 if (!news_cache_dir)
2087 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2088 NEWS_CACHE_DIR, NULL);
2090 return news_cache_dir;
2093 const gchar *get_imap_cache_dir(void)
2095 static gchar *imap_cache_dir = NULL;
2097 if (!imap_cache_dir)
2098 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2099 IMAP_CACHE_DIR, NULL);
2101 return imap_cache_dir;
2104 const gchar *get_mbox_cache_dir(void)
2106 static gchar *mbox_cache_dir = NULL;
2108 if (!mbox_cache_dir)
2109 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2110 MBOX_CACHE_DIR, NULL);
2112 return mbox_cache_dir;
2115 const gchar *get_mime_tmp_dir(void)
2117 static gchar *mime_tmp_dir = NULL;
2120 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2121 MIME_TMP_DIR, NULL);
2123 return mime_tmp_dir;
2126 const gchar *get_template_dir(void)
2128 static gchar *template_dir = NULL;
2131 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2132 TEMPLATE_DIR, NULL);
2134 return template_dir;
2137 const gchar *get_header_cache_dir(void)
2139 static gchar *header_dir = NULL;
2142 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2143 HEADER_CACHE_DIR, NULL);
2148 /* Return the default directory for Plugins. */
2149 const gchar *get_plugin_dir(void)
2152 static gchar *plugin_dir = NULL;
2155 plugin_dir = g_strconcat(w32_get_module_dir(),
2156 "\\lib\\sylpheed-claws\\plugins\\",
2160 if (is_dir_exist(PLUGINDIR))
2163 static gchar *plugin_dir = NULL;
2165 plugin_dir = g_strconcat(get_rc_dir(),
2166 G_DIR_SEPARATOR_S, "plugins",
2167 G_DIR_SEPARATOR_S, NULL);
2173 const gchar *get_tmp_dir(void)
2175 static gchar *tmp_dir = NULL;
2178 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2184 gchar *get_tmp_file(void)
2187 static guint32 id = 0;
2189 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2190 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2195 const gchar *get_domain_name(void)
2198 static gchar *domain_name = NULL;
2204 if (uname(&uts) < 0) {
2206 domain_name = "unknown";
2208 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2209 perror("gethostbyname");
2210 domain_name = g_strdup(uts.nodename);
2212 domain_name = g_strdup(hp->h_name);
2216 debug_print("domain name = %s\n", domain_name);
2225 off_t get_file_size(const gchar *file)
2229 if (g_stat(file, &s) < 0) {
2230 FILE_OP_ERROR(file, "stat");
2237 off_t get_file_size_as_crlf(const gchar *file)
2241 gchar buf[BUFFSIZE];
2243 if ((fp = g_fopen(file, "rb")) == NULL) {
2244 FILE_OP_ERROR(file, "fopen");
2248 while (fgets(buf, sizeof(buf), fp) != NULL) {
2250 size += strlen(buf) + 2;
2254 FILE_OP_ERROR(file, "fgets");
2263 off_t get_left_file_size(FILE *fp)
2269 if ((pos = ftell(fp)) < 0) {
2273 if (fseek(fp, 0L, SEEK_END) < 0) {
2277 if ((end = ftell(fp)) < 0) {
2282 if (fseek(fp, pos, SEEK_SET) < 0) {
2290 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2297 if (g_stat(file, &s) < 0) {
2298 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2302 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2309 /* Test on whether FILE is a relative file name. This is
2310 * straightforward for Unix but more complex for Windows. */
2311 gboolean is_relative_filename(const gchar *file)
2316 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2317 return FALSE; /* Prefixed with a hostname - this can't
2318 * be a relative name. */
2320 if ( ((*file >= 'a' && *file <= 'z')
2321 || (*file >= 'A' && *file <= 'Z'))
2323 file += 2; /* Skip drive letter. */
2325 return !(*file == '\\' || *file == '/');
2327 return !(*file == G_DIR_SEPARATOR);
2332 gboolean is_dir_exist(const gchar *dir)
2337 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2340 gboolean is_file_entry_exist(const gchar *file)
2345 return g_file_test(file, G_FILE_TEST_EXISTS);
2348 gboolean dirent_is_regular_file(struct dirent *d)
2350 #ifdef HAVE_DIRENT_D_TYPE
2351 if (d->d_type == DT_REG)
2353 else if (d->d_type != DT_UNKNOWN)
2357 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2360 gboolean dirent_is_directory(struct dirent *d)
2362 #ifdef HAVE_DIRENT_D_TYPE
2363 if (d->d_type == DT_DIR)
2365 else if (d->d_type != DT_UNKNOWN)
2369 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2372 gint change_dir(const gchar *dir)
2374 gchar *prevdir = NULL;
2377 prevdir = g_get_current_dir();
2379 if (g_chdir(dir) < 0) {
2380 FILE_OP_ERROR(dir, "chdir");
2381 if (debug_mode) g_free(prevdir);
2383 } else if (debug_mode) {
2386 cwd = g_get_current_dir();
2387 if (strcmp(prevdir, cwd) != 0)
2388 g_print("current dir: %s\n", cwd);
2396 gint make_dir(const gchar *dir)
2398 if (g_mkdir(dir, S_IRWXU) < 0) {
2399 FILE_OP_ERROR(dir, "mkdir");
2402 if (g_chmod(dir, S_IRWXU) < 0)
2403 FILE_OP_ERROR(dir, "chmod");
2408 gint make_dir_hier(const gchar *dir)
2413 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2414 parent_dir = g_strndup(dir, p - dir);
2415 if (*parent_dir != '\0') {
2416 if (!is_dir_exist(parent_dir)) {
2417 if (make_dir(parent_dir) < 0) {
2426 if (!is_dir_exist(dir)) {
2427 if (make_dir(dir) < 0)
2434 gint remove_all_files(const gchar *dir)
2437 const gchar *dir_name;
2440 prev_dir = g_get_current_dir();
2442 if (g_chdir(dir) < 0) {
2443 FILE_OP_ERROR(dir, "chdir");
2448 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2449 g_warning("failed to open directory: %s\n", dir);
2454 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2455 if (g_unlink(dir_name) < 0)
2456 FILE_OP_ERROR(dir_name, "unlink");
2461 if (g_chdir(prev_dir) < 0) {
2462 FILE_OP_ERROR(prev_dir, "chdir");
2472 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2475 const gchar *dir_name;
2479 prev_dir = g_get_current_dir();
2481 if (g_chdir(dir) < 0) {
2482 FILE_OP_ERROR(dir, "chdir");
2487 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2488 g_warning("failed to open directory: %s\n", dir);
2493 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2494 file_no = to_number(dir_name);
2495 if (file_no > 0 && first <= file_no && file_no <= last) {
2496 if (is_dir_exist(dir_name))
2498 if (g_unlink(dir_name) < 0)
2499 FILE_OP_ERROR(dir_name, "unlink");
2505 if (g_chdir(prev_dir) < 0) {
2506 FILE_OP_ERROR(prev_dir, "chdir");
2516 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2519 const gchar *dir_name;
2523 prev_dir = g_get_current_dir();
2525 if (g_chdir(dir) < 0) {
2526 FILE_OP_ERROR(dir, "chdir");
2531 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2532 FILE_OP_ERROR(dir, "opendir");
2537 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2538 file_no = to_number(dir_name);
2539 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2540 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2541 if (is_dir_exist(dir_name))
2543 if (g_unlink(dir_name) < 0)
2544 FILE_OP_ERROR(dir_name, "unlink");
2550 if (g_chdir(prev_dir) < 0) {
2551 FILE_OP_ERROR(prev_dir, "chdir");
2561 gint remove_all_numbered_files(const gchar *dir)
2563 return remove_numbered_files(dir, 0, UINT_MAX);
2566 gint remove_expired_files(const gchar *dir, guint hours)
2569 const gchar *dir_name;
2573 time_t mtime, now, expire_time;
2575 prev_dir = g_get_current_dir();
2577 if (g_chdir(dir) < 0) {
2578 FILE_OP_ERROR(dir, "chdir");
2583 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2584 g_warning("failed to open directory: %s\n", dir);
2590 expire_time = hours * 60 * 60;
2592 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2593 file_no = to_number(dir_name);
2595 if (g_stat(dir_name, &s) < 0) {
2596 FILE_OP_ERROR(dir_name, "stat");
2599 if (S_ISDIR(s.st_mode))
2601 mtime = MAX(s.st_mtime, s.st_atime);
2602 if (now - mtime > expire_time) {
2603 if (g_unlink(dir_name) < 0)
2604 FILE_OP_ERROR(dir_name, "unlink");
2611 if (g_chdir(prev_dir) < 0) {
2612 FILE_OP_ERROR(prev_dir, "chdir");
2622 gint remove_dir_recursive(const gchar *dir)
2626 const gchar *dir_name;
2629 if (g_stat(dir, &s) < 0) {
2630 FILE_OP_ERROR(dir, "stat");
2631 if (ENOENT == errno) return 0;
2635 if (!S_ISDIR(s.st_mode)) {
2636 if (g_unlink(dir) < 0) {
2637 FILE_OP_ERROR(dir, "unlink");
2644 prev_dir = g_get_current_dir();
2645 /* g_print("prev_dir = %s\n", prev_dir); */
2647 if (!path_cmp(prev_dir, dir)) {
2649 if (g_chdir("..") < 0) {
2650 FILE_OP_ERROR(dir, "chdir");
2653 prev_dir = g_get_current_dir();
2656 if (g_chdir(dir) < 0) {
2657 FILE_OP_ERROR(dir, "chdir");
2662 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2663 g_warning("failed to open directory: %s\n", dir);
2669 /* remove all files in the directory */
2670 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2671 /* g_print("removing %s\n", dir_name); */
2673 if (is_dir_exist(dir_name)) {
2674 if (remove_dir_recursive(dir_name) < 0) {
2675 g_warning("can't remove directory\n");
2679 if (g_unlink(dir_name) < 0)
2680 FILE_OP_ERROR(dir_name, "unlink");
2686 if (g_chdir(prev_dir) < 0) {
2687 FILE_OP_ERROR(prev_dir, "chdir");
2694 if (g_rmdir(dir) < 0) {
2695 FILE_OP_ERROR(dir, "rmdir");
2702 gint rename_force(const gchar *oldpath, const gchar *newpath)
2705 if (!is_file_entry_exist(oldpath)) {
2709 if (is_file_exist(newpath)) {
2710 if (g_unlink(newpath) < 0)
2711 FILE_OP_ERROR(newpath, "unlink");
2714 return g_rename(oldpath, newpath);
2718 * Append src file body to the tail of dest file.
2719 * Now keep_backup has no effects.
2721 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2723 FILE *src_fp, *dest_fp;
2727 gboolean err = FALSE;
2729 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2730 FILE_OP_ERROR(src, "fopen");
2734 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2735 FILE_OP_ERROR(dest, "fopen");
2740 if (change_file_mode_rw(dest_fp, dest) < 0) {
2741 FILE_OP_ERROR(dest, "chmod");
2742 g_warning("can't change file mode\n");
2745 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2746 if (n_read < sizeof(buf) && ferror(src_fp))
2748 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2749 g_warning("writing to %s failed.\n", dest);
2757 if (ferror(src_fp)) {
2758 FILE_OP_ERROR(src, "fread");
2762 if (fclose(dest_fp) == EOF) {
2763 FILE_OP_ERROR(dest, "fclose");
2775 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2777 FILE *src_fp, *dest_fp;
2780 gchar *dest_bak = NULL;
2781 gboolean err = FALSE;
2783 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2784 FILE_OP_ERROR(src, "fopen");
2787 if (is_file_exist(dest)) {
2788 dest_bak = g_strconcat(dest, ".bak", NULL);
2789 if (rename_force(dest, dest_bak) < 0) {
2790 FILE_OP_ERROR(dest, "rename");
2797 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2798 FILE_OP_ERROR(dest, "fopen");
2801 if (rename_force(dest_bak, dest) < 0)
2802 FILE_OP_ERROR(dest_bak, "rename");
2808 if (change_file_mode_rw(dest_fp, dest) < 0) {
2809 FILE_OP_ERROR(dest, "chmod");
2810 g_warning("can't change file mode\n");
2813 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2814 if (n_read < sizeof(buf) && ferror(src_fp))
2816 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2817 g_warning("writing to %s failed.\n", dest);
2822 if (rename_force(dest_bak, dest) < 0)
2823 FILE_OP_ERROR(dest_bak, "rename");
2830 if (ferror(src_fp)) {
2831 FILE_OP_ERROR(src, "fread");
2835 if (fclose(dest_fp) == EOF) {
2836 FILE_OP_ERROR(dest, "fclose");
2843 if (rename_force(dest_bak, dest) < 0)
2844 FILE_OP_ERROR(dest_bak, "rename");
2850 if (keep_backup == FALSE && dest_bak)
2858 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2860 if (overwrite == FALSE && is_file_exist(dest)) {
2861 g_warning("move_file(): file %s already exists.", dest);
2865 if (rename_force(src, dest) == 0) return 0;
2867 if (EXDEV != errno) {
2868 FILE_OP_ERROR(src, "rename");
2872 if (copy_file(src, dest, FALSE) < 0) return -1;
2879 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2882 gint bytes_left, to_read;
2885 if (fseek(fp, offset, SEEK_SET) < 0) {
2890 bytes_left = length;
2891 to_read = MIN(bytes_left, sizeof(buf));
2893 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2894 if (n_read < to_read && ferror(fp))
2896 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2899 bytes_left -= n_read;
2900 if (bytes_left == 0)
2902 to_read = MIN(bytes_left, sizeof(buf));
2913 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2916 gboolean err = FALSE;
2918 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2919 FILE_OP_ERROR(dest, "fopen");
2923 if (change_file_mode_rw(dest_fp, dest) < 0) {
2924 FILE_OP_ERROR(dest, "chmod");
2925 g_warning("can't change file mode\n");
2928 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2931 if (!err && fclose(dest_fp) == EOF) {
2932 FILE_OP_ERROR(dest, "fclose");
2937 g_warning("writing to %s failed.\n", dest);
2945 /* convert line endings into CRLF. If the last line doesn't end with
2946 * linebreak, add it.
2948 gchar *canonicalize_str(const gchar *str)
2954 for (p = str; *p != '\0'; ++p) {
2961 if (p == str || *(p - 1) != '\n')
2964 out = outp = g_malloc(new_len + 1);
2965 for (p = str; *p != '\0'; ++p) {
2972 if (p == str || *(p - 1) != '\n') {
2981 gint canonicalize_file(const gchar *src, const gchar *dest)
2983 FILE *src_fp, *dest_fp;
2984 gchar buf[BUFFSIZE];
2986 gboolean err = FALSE;
2987 gboolean last_linebreak = FALSE;
2989 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2990 FILE_OP_ERROR(src, "fopen");
2994 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2995 FILE_OP_ERROR(dest, "fopen");
3000 if (change_file_mode_rw(dest_fp, dest) < 0) {
3001 FILE_OP_ERROR(dest, "chmod");
3002 g_warning("can't change file mode\n");
3005 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3009 if (len == 0) break;
3010 last_linebreak = FALSE;
3012 if (buf[len - 1] != '\n') {
3013 last_linebreak = TRUE;
3014 r = fputs(buf, dest_fp);
3015 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3016 r = fputs(buf, dest_fp);
3019 r = fwrite(buf, 1, len - 1, dest_fp);
3024 r = fputs("\r\n", dest_fp);
3028 g_warning("writing to %s failed.\n", dest);
3036 if (last_linebreak == TRUE) {
3037 if (fputs("\r\n", dest_fp) == EOF)
3041 if (ferror(src_fp)) {
3042 FILE_OP_ERROR(src, "fgets");
3046 if (fclose(dest_fp) == EOF) {
3047 FILE_OP_ERROR(dest, "fclose");
3059 gint canonicalize_file_replace(const gchar *file)
3063 tmp_file = get_tmp_file();
3065 if (canonicalize_file(file, tmp_file) < 0) {
3070 if (move_file(tmp_file, file, TRUE) < 0) {
3071 g_warning("can't replace %s .\n", file);
3081 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3083 FILE *src_fp, *dest_fp;
3084 gchar buf[BUFFSIZE];
3085 gboolean err = FALSE;
3087 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3088 FILE_OP_ERROR(src, "fopen");
3092 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3093 FILE_OP_ERROR(dest, "fopen");
3098 if (change_file_mode_rw(dest_fp, dest) < 0) {
3099 FILE_OP_ERROR(dest, "chmod");
3100 g_warning("can't change file mode\n");
3103 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3105 if (fputs(buf, dest_fp) == EOF) {
3106 g_warning("writing to %s failed.\n", dest);
3114 if (ferror(src_fp)) {
3115 FILE_OP_ERROR(src, "fgets");
3119 if (fclose(dest_fp) == EOF) {
3120 FILE_OP_ERROR(dest, "fclose");
3132 gint uncanonicalize_file_replace(const gchar *file)
3136 tmp_file = get_tmp_file();
3138 if (uncanonicalize_file(file, tmp_file) < 0) {
3143 if (move_file(tmp_file, file, TRUE) < 0) {
3144 g_warning("can't replace %s .\n", file);
3154 gchar *normalize_newlines(const gchar *str)
3156 const gchar *p = str;
3159 out = outp = g_malloc(strlen(str) + 1);
3160 for (p = str; *p != '\0'; ++p) {
3162 if (*(p + 1) != '\n')
3173 gchar *get_outgoing_rfc2822_str(FILE *fp)
3175 gchar buf[BUFFSIZE];
3179 str = g_string_new(NULL);
3181 /* output header part */
3182 while (fgets(buf, sizeof(buf), fp) != NULL) {
3184 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3191 else if (next != ' ' && next != '\t') {
3195 if (fgets(buf, sizeof(buf), fp) == NULL)
3199 g_string_append(str, buf);
3200 g_string_append(str, "\r\n");
3206 /* output body part */
3207 while (fgets(buf, sizeof(buf), fp) != NULL) {
3210 g_string_append_c(str, '.');
3211 g_string_append(str, buf);
3212 g_string_append(str, "\r\n");
3216 g_string_free(str, FALSE);
3222 * Create a new boundary in a way that it is very unlikely that this
3223 * will occur in the following text. It would be easy to ensure
3224 * uniqueness if everything is either quoted-printable or base64
3225 * encoded (note that conversion is allowed), but because MIME bodies
3226 * may be nested, it may happen that the same boundary has already
3229 * boundary := 0*69<bchars> bcharsnospace
3230 * bchars := bcharsnospace / " "
3231 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3232 * "+" / "_" / "," / "-" / "." /
3233 * "/" / ":" / "=" / "?"
3235 * some special characters removed because of buggy MTAs
3238 gchar *generate_mime_boundary(const gchar *prefix)
3240 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3241 "abcdefghijklmnopqrstuvwxyz"
3246 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3247 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3250 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3254 gint change_file_mode_rw(FILE *fp, const gchar *file)
3257 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3259 return g_chmod(file, S_IRUSR|S_IWUSR);
3263 FILE *my_tmpfile(void)
3265 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3266 const gchar suffix[] = ".XXXXXX";
3267 const gchar *tmpdir;
3269 const gchar *progname;
3276 tmpdir = get_tmp_dir();
3277 tmplen = strlen(tmpdir);
3278 progname = g_get_prgname();
3279 if (progname == NULL)
3280 progname = "sylpheed-claws";
3281 proglen = strlen(progname);
3282 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3285 memcpy(fname, tmpdir, tmplen);
3286 fname[tmplen] = G_DIR_SEPARATOR;
3287 memcpy(fname + tmplen + 1, progname, proglen);
3288 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3290 fd = mkstemp(fname);
3297 /* verify that we can write in the file after unlinking */
3298 if (write(fd, buf, 1) < 0) {
3305 fp = fdopen(fd, "w+b");
3313 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3318 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3322 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3323 dir, G_DIR_SEPARATOR);
3324 fd = mkstemp_name(template, filename);
3327 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3328 fd = mkstemp(*filename);
3330 return fdopen(fd, "w+");
3333 FILE *str_open_as_stream(const gchar *str)
3338 g_return_val_if_fail(str != NULL, NULL);
3342 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3347 if (len == 0) return fp;
3349 if (fwrite(str, 1, len, fp) != len) {
3350 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3359 gint str_write_to_file(const gchar *str, const gchar *file)
3364 g_return_val_if_fail(str != NULL, -1);
3365 g_return_val_if_fail(file != NULL, -1);
3367 if ((fp = g_fopen(file, "wb")) == NULL) {
3368 FILE_OP_ERROR(file, "fopen");
3378 if (fwrite(str, 1, len, fp) != len) {
3379 FILE_OP_ERROR(file, "fwrite");
3385 if (fclose(fp) == EOF) {
3386 FILE_OP_ERROR(file, "fclose");
3394 gchar *file_read_to_str(const gchar *file)
3399 g_return_val_if_fail(file != NULL, NULL);
3401 if ((fp = g_fopen(file, "rb")) == NULL) {
3402 FILE_OP_ERROR(file, "fopen");
3406 str = file_read_stream_to_str(fp);
3413 gchar *file_read_stream_to_str(FILE *fp)
3420 g_return_val_if_fail(fp != NULL, NULL);
3422 array = g_byte_array_new();
3424 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3425 if (n_read < sizeof(buf) && ferror(fp))
3427 g_byte_array_append(array, buf, n_read);
3431 FILE_OP_ERROR("file stream", "fread");
3432 g_byte_array_free(array, TRUE);
3437 g_byte_array_append(array, buf, 1);
3438 str = (gchar *)array->data;
3439 g_byte_array_free(array, FALSE);
3441 if (!g_utf8_validate(str, -1, NULL)) {
3442 const gchar *src_codeset, *dest_codeset;
3444 src_codeset = conv_get_locale_charset_str();
3445 dest_codeset = CS_UTF_8;
3446 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3454 gint execute_async(gchar *const argv[])
3456 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3458 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3459 NULL, NULL, NULL, FALSE) == FALSE) {
3460 g_warning("Can't execute command: %s\n", argv[0]);
3467 gint execute_sync(gchar *const argv[])
3471 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3473 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3474 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3475 g_warning("Can't execute command: %s\n", argv[0]);
3480 if (WIFEXITED(status))
3481 return WEXITSTATUS(status);
3489 gint execute_command_line(const gchar *cmdline, gboolean async)
3494 debug_print("execute_command_line(): executing: %s\n", cmdline);
3496 argv = strsplit_with_quote(cmdline, " ", 0);
3499 ret = execute_async(argv);
3501 ret = execute_sync(argv);
3508 gchar *get_command_output(const gchar *cmdline)
3510 gchar *child_stdout;
3513 g_return_val_if_fail(cmdline != NULL, NULL);
3515 debug_print("get_command_output(): executing: %s\n", cmdline);
3517 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3519 g_warning("Can't execute command: %s\n", cmdline);
3523 return child_stdout;
3526 static gint is_unchanged_uri_char(char c)
3538 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3544 for(i = 0; i < strlen(uri) ; i++) {
3545 if (is_unchanged_uri_char(uri[i])) {
3546 if (k + 2 >= bufsize)
3548 encoded_uri[k++] = uri[i];
3551 char * hexa = "0123456789ABCDEF";
3553 if (k + 4 >= bufsize)
3555 encoded_uri[k++] = '%';
3556 encoded_uri[k++] = hexa[uri[i] / 16];
3557 encoded_uri[k++] = hexa[uri[i] % 16];
3563 gint open_uri(const gchar *uri, const gchar *cmdline)
3565 gchar buf[BUFFSIZE];
3567 gchar encoded_uri[BUFFSIZE];
3569 g_return_val_if_fail(uri != NULL, -1);
3571 /* an option to choose whether to use encode_uri or not ? */
3572 encode_uri(encoded_uri, BUFFSIZE, uri);
3575 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3576 !strchr(p + 2, '%'))
3577 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3580 g_warning("Open URI command line is invalid "
3581 "(there must be only one '%%s'): %s",
3583 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3586 execute_command_line(buf, TRUE);
3591 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3593 gchar buf[BUFFSIZE];
3596 g_return_val_if_fail(filepath != NULL, -1);
3599 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3600 !strchr(p + 2, '%'))
3601 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3604 g_warning("Open Text Editor command line is invalid "
3605 "(there must be only one '%%s'): %s",
3607 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3610 execute_command_line(buf, TRUE);
3615 time_t remote_tzoffset_sec(const gchar *zone)
3617 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3623 time_t remoteoffset;
3625 strncpy(zone3, zone, 3);
3629 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3630 (c == '+' || c == '-')) {
3631 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3633 remoteoffset = -remoteoffset;
3634 } else if (!strncmp(zone, "UT" , 2) ||
3635 !strncmp(zone, "GMT", 2)) {
3637 } else if (strlen(zone3) == 3) {
3638 for (p = ustzstr; *p != '\0'; p += 3) {
3639 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3640 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3641 remoteoffset = iustz * 3600;
3647 } else if (strlen(zone3) == 1) {
3649 case 'Z': remoteoffset = 0; break;
3650 case 'A': remoteoffset = -1; break;
3651 case 'B': remoteoffset = -2; break;
3652 case 'C': remoteoffset = -3; break;
3653 case 'D': remoteoffset = -4; break;
3654 case 'E': remoteoffset = -5; break;
3655 case 'F': remoteoffset = -6; break;
3656 case 'G': remoteoffset = -7; break;
3657 case 'H': remoteoffset = -8; break;
3658 case 'I': remoteoffset = -9; break;
3659 case 'K': remoteoffset = -10; break; /* J is not used */
3660 case 'L': remoteoffset = -11; break;
3661 case 'M': remoteoffset = -12; break;
3662 case 'N': remoteoffset = 1; break;
3663 case 'O': remoteoffset = 2; break;
3664 case 'P': remoteoffset = 3; break;
3665 case 'Q': remoteoffset = 4; break;
3666 case 'R': remoteoffset = 5; break;
3667 case 'S': remoteoffset = 6; break;
3668 case 'T': remoteoffset = 7; break;
3669 case 'U': remoteoffset = 8; break;
3670 case 'V': remoteoffset = 9; break;
3671 case 'W': remoteoffset = 10; break;
3672 case 'X': remoteoffset = 11; break;
3673 case 'Y': remoteoffset = 12; break;
3674 default: remoteoffset = 0; break;
3676 remoteoffset = remoteoffset * 3600;
3680 return remoteoffset;
3683 time_t tzoffset_sec(time_t *now)
3689 lt = localtime(now);
3691 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3693 if (lt->tm_year < gmt.tm_year)
3695 else if (lt->tm_year > gmt.tm_year)
3697 else if (lt->tm_yday < gmt.tm_yday)
3699 else if (lt->tm_yday > gmt.tm_yday)
3702 if (off >= 24 * 60) /* should be impossible */
3703 off = 23 * 60 + 59; /* if not, insert silly value */
3704 if (off <= -24 * 60)
3705 off = -(23 * 60 + 59);
3710 /* calculate timezone offset */
3711 gchar *tzoffset(time_t *now)
3713 static gchar offset_string[6];
3719 lt = localtime(now);
3721 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3723 if (lt->tm_year < gmt.tm_year)
3725 else if (lt->tm_year > gmt.tm_year)
3727 else if (lt->tm_yday < gmt.tm_yday)
3729 else if (lt->tm_yday > gmt.tm_yday)
3737 if (off >= 24 * 60) /* should be impossible */
3738 off = 23 * 60 + 59; /* if not, insert silly value */
3740 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3742 return offset_string;
3745 void get_rfc822_date(gchar *buf, gint len)
3749 gchar day[4], mon[4];
3750 gint dd, hh, mm, ss, yyyy;
3755 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3756 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3757 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3758 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3761 /* just a wrapper to suppress the warning of gcc about %c */
3762 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3763 const struct tm *tm)
3765 return strftime(s, max, format, tm);
3768 void debug_set_mode(gboolean mode)
3773 gboolean debug_get_mode(void)
3778 void debug_print_real(const gchar *format, ...)
3781 gchar buf[BUFFSIZE];
3783 if (!debug_mode) return;
3785 va_start(args, format);
3786 g_vsnprintf(buf, sizeof(buf), format, args);
3793 const char * debug_srcname(const char *file)
3795 const char *s = strrchr (file, '/');
3800 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3802 if (subject == NULL)
3805 subject += subject_get_prefix_length(subject);
3807 return g_hash_table_lookup(subject_table, subject);
3810 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3813 if (subject == NULL || *subject == 0)
3815 subject += subject_get_prefix_length(subject);
3816 g_hash_table_insert(subject_table, subject, data);
3819 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3821 if (subject == NULL)
3824 subject += subject_get_prefix_length(subject);
3825 g_hash_table_remove(subject_table, subject);
3829 *\brief Check if a string is prefixed with known (combinations)
3830 * of prefixes. The function assumes that each prefix
3831 * is terminated by zero or exactly _one_ space.
3833 *\param str String to check for a prefixes
3835 *\return int Number of chars in the prefix that should be skipped
3836 * for a "clean" subject line. If no prefix was found, 0
3839 int subject_get_prefix_length(const gchar *subject)
3841 /*!< Array with allowable reply prefixes regexps. */
3842 static const gchar * const prefixes[] = {
3843 "Re\\:", /* "Re:" */
3844 "RE\\:", /* "RE:" (outlook) */
3845 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3846 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3847 "Aw\\:", /* "Aw:" (German) */
3848 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3849 "Res\\:", /* "Res:" (Brazilian Outlook) */
3850 "Fw\\:", /* "Fw:" Forward */
3851 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3852 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3853 "Rif\\:", /* "Rif:" (Italian Outlook) */
3854 "SV\\:", /* "SV" (Norwegian) */
3855 "Sv\\:", /* "Sv" (Norwegian) */
3856 "VS\\:", /* "VS" (Norwegian) */
3857 "Vs\\:", /* "Vs" (Norwegian) */
3858 "AD\\:", /* "AD" (Norwegian) */
3859 "Ad\\:" /* "Ad" (Norwegian) */
3862 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3865 static regex_t regex;
3866 static gboolean init_;
3868 if (!subject) return 0;
3869 if (!*subject) return 0;
3872 GString *s = g_string_new("");
3874 for (n = 0; n < PREFIXES; n++)
3875 /* Terminate each prefix regexpression by a
3876 * "\ ?" (zero or ONE space), and OR them */
3877 g_string_append_printf(s, "(%s\\ ?)%s",
3882 g_string_prepend(s, "(");
3883 g_string_append(s, ")+"); /* match at least once */
3884 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3887 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3888 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3889 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3890 debug_print("Error compiling regexp %s\n", s->str);
3891 g_string_free(s, TRUE);
3895 g_string_free(s, TRUE);
3899 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3905 guint g_stricase_hash(gconstpointer gptr)
3907 guint hash_result = 0;
3910 for (str = gptr; str && *str; str++) {
3911 hash_result += toupper(*str);
3917 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3919 const char *str1 = gptr1;
3920 const char *str2 = gptr2;
3922 return !strcasecmp(str1, str2);
3925 gint g_int_compare(gconstpointer a, gconstpointer b)
3927 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3930 gchar *generate_msgid(gchar *buf, gint len)
3939 addr = g_strconcat("@", get_domain_name(), NULL);
3941 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3942 lt->tm_year + 1900, lt->tm_mon + 1,
3943 lt->tm_mday, lt->tm_hour,
3944 lt->tm_min, lt->tm_sec,
3945 (guint) rand(), addr);
3952 quote_cmd_argument()
3954 return a quoted string safely usable in argument of a command.
3956 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3959 gint quote_cmd_argument(gchar * result, guint size,
3969 for(p = path ; * p != '\0' ; p ++) {
3971 if (isalnum((guchar)*p) || (* p == '/')) {
3972 if (remaining > 0) {
3978 result[size - 1] = '\0';
3983 if (remaining >= 2) {
3991 result[size - 1] = '\0';
3996 if (remaining > 0) {
4000 result[size - 1] = '\0';
4014 static void g_node_map_recursive(GNode *node, gpointer data)
4016 GNodeMapData *mapdata = (GNodeMapData *) data;
4018 GNodeMapData newmapdata;
4021 newdata = mapdata->func(node->data, mapdata->data);
4022 if (newdata != NULL) {
4023 newnode = g_node_new(newdata);
4024 g_node_append(mapdata->parent, newnode);
4026 newmapdata.parent = newnode;
4027 newmapdata.func = mapdata->func;
4028 newmapdata.data = mapdata->data;
4030 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4034 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4037 GNodeMapData mapdata;
4039 g_return_val_if_fail(node != NULL, NULL);
4040 g_return_val_if_fail(func != NULL, NULL);
4042 root = g_node_new(func(node->data, data));
4044 mapdata.parent = root;
4045 mapdata.func = func;
4046 mapdata.data = data;
4048 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4053 #define HEX_TO_INT(val, hex) \
4057 if ('0' <= c && c <= '9') { \
4059 } else if ('a' <= c && c <= 'f') { \
4060 val = c - 'a' + 10; \
4061 } else if ('A' <= c && c <= 'F') { \
4062 val = c - 'A' + 10; \
4068 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4075 if (hi == -1 || lo == -1)
4078 *out = (hi << 4) + lo;
4082 #define INT_TO_HEX(hex, val) \
4085 hex = '0' + (val); \
4087 hex = 'A' + (val) - 10; \
4090 void get_hex_str(gchar *out, guchar ch)
4094 INT_TO_HEX(hex, ch >> 4);
4096 INT_TO_HEX(hex, ch & 0x0f);
4102 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4104 #define G_PRINT_REF g_print
4108 *\brief Register ref counted pointer. It is based on GBoxed, so should
4109 * work with anything that uses the GType system. The semantics
4110 * are similar to a C++ auto pointer, with the exception that
4111 * C doesn't have automatic closure (calling destructors) when
4112 * exiting a block scope.
4113 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4114 * function directly.
4116 *\return GType A GType type.
4118 GType g_auto_pointer_register(void)
4120 static GType auto_pointer_type;
4121 if (!auto_pointer_type)
4123 g_boxed_type_register_static
4124 ("G_TYPE_AUTO_POINTER",
4125 (GBoxedCopyFunc) g_auto_pointer_copy,
4126 (GBoxedFreeFunc) g_auto_pointer_free);
4127 return auto_pointer_type;
4131 *\brief Structure with g_new() allocated pointer guarded by the
4134 typedef struct AutoPointerRef {
4135 void (*free) (gpointer);
4141 *\brief The auto pointer opaque structure that references the
4142 * pointer guard block.
4144 typedef struct AutoPointer {
4145 AutoPointerRef *ref;
4146 gpointer ptr; /*!< access to protected pointer */
4150 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4154 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4155 * ... when assigning, copying and freeing storage elements
4157 * gtk_list_store_new(N_S_COLUMNS,
4158 * G_TYPE_AUTO_POINTER,
4162 * Template *precious_data = g_new0(Template, 1);
4163 * g_pointer protect = g_auto_pointer_new(precious_data);
4165 * gtk_list_store_set(container, &iter,
4169 * ... the gtk_list_store has copied the pointer and
4170 * ... incremented its reference count, we should free
4171 * ... the auto pointer (in C++ a destructor would do
4172 * ... this for us when leaving block scope)
4174 * g_auto_pointer_free(protect);
4176 * ... gtk_list_store_set() now manages the data. When
4177 * ... *explicitly* requesting a pointer from the list
4178 * ... store, don't forget you get a copy that should be
4179 * ... freed with g_auto_pointer_free() eventually.
4183 *\param pointer Pointer to be guarded.
4185 *\return GAuto * Pointer that should be used in containers with
4188 GAuto *g_auto_pointer_new(gpointer p)
4190 AutoPointerRef *ref;
4196 ref = g_new0(AutoPointerRef, 1);
4197 ptr = g_new0(AutoPointer, 1);
4207 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4213 *\brief Allocate an autopointer using the passed \a free function to
4214 * free the guarded pointer
4216 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4223 aptr = g_auto_pointer_new(p);
4224 aptr->ref->free = free_;
4228 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4230 if (auto_ptr == NULL)
4232 return ((AutoPointer *) auto_ptr)->ptr;
4236 *\brief Copies an auto pointer by. It's mostly not necessary
4237 * to call this function directly, unless you copy/assign
4238 * the guarded pointer.
4240 *\param auto_ptr Auto pointer returned by previous call to
4241 * g_auto_pointer_new_XXX()
4243 *\return gpointer An auto pointer
4245 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4248 AutoPointerRef *ref;
4251 if (auto_ptr == NULL)
4256 newp = g_new0(AutoPointer, 1);
4259 newp->ptr = ref->pointer;
4263 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4269 *\brief Free an auto pointer
4271 void g_auto_pointer_free(GAuto *auto_ptr)
4274 AutoPointerRef *ref;
4276 if (auto_ptr == NULL)
4282 if (--(ref->cnt) == 0) {
4284 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4286 ref->free(ref->pointer);
4291 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4296 void replace_returns(gchar *str)
4301 while (strstr(str, "\n")) {
4302 *strstr(str, "\n") = ' ';
4304 while (strstr(str, "\r")) {
4305 *strstr(str, "\r") = ' ';
4309 /* get_uri_part() - retrieves a URI starting from scanpos.
4310 Returns TRUE if succesful */
4311 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4312 const gchar **bp, const gchar **ep, gboolean hdr)
4315 gint parenthese_cnt = 0;
4317 g_return_val_if_fail(start != NULL, FALSE);
4318 g_return_val_if_fail(scanpos != NULL, FALSE);
4319 g_return_val_if_fail(bp != NULL, FALSE);
4320 g_return_val_if_fail(ep != NULL, FALSE);
4324 /* find end point of URI */
4325 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4326 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4327 !IS_ASCII(*(const guchar *)ep_) ||
4328 strchr("[]{}<>\"", *ep_)) {
4330 } else if (strchr("(", *ep_)) {
4332 } else if (strchr(")", *ep_)) {
4333 if (parenthese_cnt > 0)
4340 /* no punctuation at end of string */
4342 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4343 * should pass some URI type to this function and decide on that whether
4344 * to perform punctuation stripping */
4346 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4348 for (; ep_ - 1 > scanpos + 1 &&
4349 IS_REAL_PUNCT(*(ep_ - 1));
4353 #undef IS_REAL_PUNCT
4360 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4362 while (bp && *bp && g_ascii_isspace(*bp))
4364 return g_strndup(bp, ep - bp);
4367 /* valid mail address characters */
4368 #define IS_RFC822_CHAR(ch) \
4372 !g_ascii_isspace(ch) && \
4373 !strchr("(),;<>\"", (ch)))
4375 /* alphabet and number within 7bit ASCII */
4376 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4377 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4379 static GHashTable *create_domain_tab(void)
4381 static const gchar *toplvl_domains [] = {
4383 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4384 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4385 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4386 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4387 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4388 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4389 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4390 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4391 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4392 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4393 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4394 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4395 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4396 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4397 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4398 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4399 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4400 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4401 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4402 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4403 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4404 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4405 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4406 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4407 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4408 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4411 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4413 g_return_val_if_fail(htab, NULL);
4414 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4415 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4419 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4421 const gint MAX_LVL_DOM_NAME_LEN = 6;
4422 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4423 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4426 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4429 for (p = buf; p < m && first < last; *p++ = *first++)
4433 return g_hash_table_lookup(tab, buf) != NULL;
4436 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4437 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4438 const gchar **bp, const gchar **ep, gboolean hdr)
4440 /* more complex than the uri part because we need to scan back and forward starting from
4441 * the scan position. */
4442 gboolean result = FALSE;
4443 const gchar *bp_ = NULL;
4444 const gchar *ep_ = NULL;
4445 static GHashTable *dom_tab;
4446 const gchar *last_dot = NULL;
4447 const gchar *prelast_dot = NULL;
4448 const gchar *last_tld_char = NULL;
4450 /* the informative part of the email address (describing the name
4451 * of the email address owner) may contain quoted parts. the
4452 * closure stack stores the last encountered quotes. */
4453 gchar closure_stack[128];
4454 gchar *ptr = closure_stack;
4456 g_return_val_if_fail(start != NULL, FALSE);
4457 g_return_val_if_fail(scanpos != NULL, FALSE);
4458 g_return_val_if_fail(bp != NULL, FALSE);
4459 g_return_val_if_fail(ep != NULL, FALSE);
4462 const gchar *start_quote = NULL;
4463 const gchar *end_quote = NULL;
4465 /* go to the real start */
4466 if (start[0] == ',')
4468 if (start[0] == ';')
4470 while (start[0] == '\n' || start[0] == '\r')
4472 while (start[0] == ' ' || start[0] == '\t')
4477 /* check if there are quotes (to skip , in them) */
4478 if (*start == '"') {
4479 start_quote = start;
4481 end_quote = strstr(start, "\"");
4487 /* skip anything between quotes */
4488 if (start_quote && end_quote) {
4493 /* find end (either , or ; or end of line) */
4494 if (strstr(start, ",") && strstr(start, ";"))
4495 *ep = strstr(start,",") < strstr(start, ";")
4496 ? strstr(start, ",") : strstr(start, ";");
4497 else if (strstr(start, ","))
4498 *ep = strstr(start, ",");
4499 else if (strstr(start, ";"))
4500 *ep = strstr(start, ";");
4502 *ep = start+strlen(start);
4504 /* go back to real start */
4505 if (start_quote && end_quote) {
4506 start = start_quote;
4509 /* check there's still an @ in that, or search
4510 * further if possible */
4511 if (strstr(start, "@") && strstr(start, "@") < *ep)
4513 else if (*ep < start+strlen(start)) {
4516 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4524 dom_tab = create_domain_tab();
4525 g_return_val_if_fail(dom_tab, FALSE);
4527 /* scan start of address */
4528 for (bp_ = scanpos - 1;
4529 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4532 /* TODO: should start with an alnum? */
4534 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4537 if (bp_ != scanpos) {
4538 /* scan end of address */
4539 for (ep_ = scanpos + 1;
4540 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4542 prelast_dot = last_dot;
4544 if (*(last_dot + 1) == '.') {
4545 if (prelast_dot == NULL)
4547 last_dot = prelast_dot;
4552 /* TODO: really should terminate with an alnum? */
4553 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4558 if (last_dot == NULL)
4560 if (last_dot >= ep_)
4561 last_dot = prelast_dot;
4562 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4566 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4567 if (*last_tld_char == '?')
4570 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4577 if (!result) return FALSE;
4579 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4580 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4581 && IS_RFC822_CHAR(*(ep_ + 3))) {
4582 /* this informative part with an @ in it is
4583 * followed by the email address */
4586 /* go to matching '>' (or next non-rfc822 char, like \n) */
4587 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4590 /* include the bracket */
4591 if (*ep_ == '>') ep_++;
4593 /* include the leading quote */
4601 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4602 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4605 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4606 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4609 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4610 #define IN_STACK() (ptr > closure_stack)
4611 /* has underrun check */
4612 #define POP_STACK() if(IN_STACK()) --ptr
4613 /* has overrun check */
4614 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4615 /* has underrun check */
4616 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4620 /* scan for the informative part. */
4621 for (bp_ -= 2; bp_ >= start; bp_--) {
4622 /* if closure on the stack keep scanning */
4623 if (PEEK_STACK() == *bp_) {
4627 if (*bp_ == '\'' || *bp_ == '"') {
4632 /* if nothing in the closure stack, do the special conditions
4633 * the following if..else expression simply checks whether
4634 * a token is acceptable. if not acceptable, the clause
4635 * should terminate the loop with a 'break' */
4636 if (!PEEK_STACK()) {
4638 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4639 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4640 /* hyphens are allowed, but only in
4642 } else if (strchr(" \"'", *bp_)) {
4643 /* but anything not being a punctiation
4646 break; /* anything else is rejected */
4653 /* scan forward (should start with an alnum) */
4654 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4670 #undef IS_ASCII_ALNUM
4671 #undef IS_RFC822_CHAR
4673 gchar *make_email_string(const gchar *bp, const gchar *ep)
4675 /* returns a mailto: URI; mailto: is also used to detect the
4676 * uri type later on in the button_pressed signal handler */
4680 tmp = g_strndup(bp, ep - bp);
4681 result = g_strconcat("mailto:", tmp, NULL);
4687 gchar *make_http_string(const gchar *bp, const gchar *ep)
4689 /* returns an http: URI; */
4693 while (bp && *bp && g_ascii_isspace(*bp))
4695 tmp = g_strndup(bp, ep - bp);
4696 result = g_strconcat("http://", tmp, NULL);
4702 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4704 FILE *fp = fopen(path, "rb");
4705 gchar buf[BUFFSIZE];
4706 gchar *result = NULL;
4709 while (fgets(buf, sizeof (buf), fp) != NULL) {
4710 gchar **parts = g_strsplit(buf, ";", 3);
4711 gchar *trimmed = parts[0];
4712 while (trimmed[0] == ' ')
4714 while (trimmed[strlen(trimmed)-1] == ' ')
4715 trimmed[strlen(trimmed)-1] = '\0';
4717 if (!strcmp(trimmed, type)) {
4718 gboolean needsterminal = FALSE;
4719 if (parts[2] && strstr(parts[2], "needsterminal")) {
4720 needsterminal = TRUE;
4722 if (parts[2] && strstr(parts[2], "test=")) {
4723 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4724 gchar *testcmd = orig_testcmd;
4725 if (strstr(testcmd,";"))
4726 *(strstr(testcmd,";")) = '\0';
4727 while (testcmd[0] == ' ')
4729 while (testcmd[strlen(testcmd)-1] == '\n')
4730 testcmd[strlen(testcmd)-1] = '\0';
4731 while (testcmd[strlen(testcmd)-1] == '\r')
4732 testcmd[strlen(testcmd)-1] = '\0';
4733 while (testcmd[strlen(testcmd)-1] == ' ')
4734 testcmd[strlen(testcmd)-1] = '\0';
4736 if (strstr(testcmd, "%s")) {
4737 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4738 gint res = system(tmp);
4740 g_free(orig_testcmd);
4747 gint res = system(testcmd);
4748 g_free(orig_testcmd);
4758 while (trimmed[0] == ' ')
4760 while (trimmed[strlen(trimmed)-1] == '\n')
4761 trimmed[strlen(trimmed)-1] = '\0';
4762 while (trimmed[strlen(trimmed)-1] == '\r')
4763 trimmed[strlen(trimmed)-1] = '\0';
4764 while (trimmed[strlen(trimmed)-1] == ' ')
4765 trimmed[strlen(trimmed)-1] = '\0';
4766 result = g_strdup(trimmed);
4769 /* if there are no single quotes around %s, add them.
4770 * '.*%s.*' is ok, as in display 'png:%s'
4772 if (strstr(result, "%s")
4773 && !(strstr(result, "'") < strstr(result,"%s") &&
4774 strstr(strstr(result,"%s"), "'"))) {
4775 gchar *start = g_strdup(result);
4776 gchar *end = g_strdup(strstr(result, "%s")+2);
4778 *strstr(start, "%s") = '\0';
4779 tmp = g_strconcat(start,"'%s'",end, NULL);
4785 if (needsterminal) {
4786 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4797 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4799 gchar *result = NULL;
4803 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4804 result = mailcap_get_command_in_file(path, type, file_to_open);
4808 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4812 void mailcap_update_default(const gchar *type, const gchar *command)
4814 gchar *path = NULL, *outpath = NULL;
4815 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4816 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4817 FILE *fp = fopen(path, "rb");
4818 FILE *outfp = fopen(outpath, "wb");
4819 gchar buf[BUFFSIZE];
4832 while (fgets(buf, sizeof (buf), fp) != NULL) {
4833 gchar **parts = g_strsplit(buf, ";", 3);
4834 gchar *trimmed = parts[0];
4835 while (trimmed[0] == ' ')
4837 while (trimmed[strlen(trimmed)-1] == ' ')
4838 trimmed[strlen(trimmed)-1] = '\0';
4840 if (!strcmp(trimmed, type)) {
4849 fprintf(outfp, "%s; %s\n", type, command);
4852 g_rename(outpath, path);
4855 gint copy_dir(const gchar *src, const gchar *dst)
4860 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4861 g_warning("failed to open directory: %s\n", src);
4865 if (make_dir(dst) < 0)
4868 while ((name = g_dir_read_name(dir)) != NULL) {
4869 gchar *old_file, *new_file;
4870 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4871 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4872 debug_print("copying: %s -> %s\n", old_file, new_file);
4873 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4874 gint r = copy_file(old_file, new_file, TRUE);
4877 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4878 gint r = copy_dir(old_file, new_file);
4886 /* crude test to see if a file is an email. */
4887 gboolean file_is_email (const gchar *filename)
4893 if (filename == NULL)
4895 if ((fp = g_fopen(filename, "rb")) == NULL)
4897 while (i < 60 && score < 3
4898 && fgets(buffer, sizeof (buffer), fp) > 0) {
4899 if (!strncmp(buffer, "From:", strlen("From:")))
4901 if (!strncmp(buffer, "To:", strlen("To:")))
4903 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4908 return (score >= 3);