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 #define divide(num,divisor,i,d) \
343 if (d > 1000) d /= 1000; \
344 else if (d > 100) d /= 100; \
345 else if (d > 10) d /= 10; \
348 gchar *to_human_readable(off_t size)
350 static gchar str[14];
351 static gchar *b_format = NULL, *kb_format = NULL,
352 *mb_format = NULL, *gb_format = NULL;
353 register int t = 0, r = 0;
354 if (b_format == NULL) {
356 kb_format = _("%d.%dKB");
357 mb_format = _("%.2fMB");
358 gb_format = _("%.2fGB");
362 g_snprintf(str, sizeof(str), b_format, (gint)size);
364 } else if (size >> 10 < 1024) {
365 divide(size, (1 << 10), t, r);
366 g_snprintf(str, sizeof(str), kb_format, t, r);
368 } else if (size >> 20 < 1024) {
369 g_snprintf(str, sizeof(str), mb_format, (gfloat)size / (1 << 20));
372 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
377 /* strcmp with NULL-checking */
378 gint strcmp2(const gchar *s1, const gchar *s2)
380 if (s1 == NULL || s2 == NULL)
383 return strcmp(s1, s2);
385 /* strstr with NULL-checking */
386 gchar *strstr2(const gchar *s1, const gchar *s2)
388 if (s1 == NULL || s2 == NULL)
391 return strstr(s1, s2);
394 gint path_cmp(const gchar *s1, const gchar *s2)
399 gchar *s1buf, *s2buf;
402 if (s1 == NULL || s2 == NULL) return -1;
403 if (*s1 == '\0' || *s2 == '\0') return -1;
406 s1buf = g_strdup (s1);
407 s2buf = g_strdup (s2);
408 subst_char (s1buf, '/', G_DIR_SEPARATOR);
409 subst_char (s2buf, '/', G_DIR_SEPARATOR);
412 #endif /* !G_OS_WIN32 */
417 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
418 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
420 rc = strncmp(s1, s2, MAX(len1, len2));
424 #endif /* !G_OS_WIN32 */
428 /* remove trailing return code */
429 gchar *strretchomp(gchar *str)
433 if (!*str) return str;
435 for (s = str + strlen(str) - 1;
436 s >= str && (*s == '\n' || *s == '\r');
443 /* remove trailing character */
444 gchar *strtailchomp(gchar *str, gchar tail_char)
448 if (!*str) return str;
449 if (tail_char == '\0') return str;
451 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
457 /* remove CR (carriage return) */
458 gchar *strcrchomp(gchar *str)
462 if (!*str) return str;
464 s = str + strlen(str) - 1;
465 if (*s == '\n' && s > str && *(s - 1) == '\r') {
473 void file_strip_crs(const gchar *file)
475 FILE *fp = NULL, *outfp = NULL;
477 gchar *out = get_tmp_file();
481 fp = fopen(file, "rb");
485 outfp = fopen(out, "wb");
491 while (fgets(buf, sizeof (buf), fp) != NULL) {
498 rename_force(out, file);
503 /* Similar to `strstr' but this function ignores the case of both strings. */
504 gchar *strcasestr(const gchar *haystack, const gchar *needle)
506 register size_t haystack_len, needle_len;
508 haystack_len = strlen(haystack);
509 needle_len = strlen(needle);
511 if (haystack_len < needle_len || needle_len == 0)
514 while (haystack_len >= needle_len) {
515 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
516 return (gchar *)haystack;
526 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
527 gconstpointer needle, size_t needlelen)
529 const gchar *haystack_ = (const gchar *)haystack;
530 const gchar *needle_ = (const gchar *)needle;
531 const gchar *haystack_cur = (const gchar *)haystack;
534 return memchr(haystack_, *needle_, haystacklen);
536 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
538 if (haystacklen - (haystack_cur - haystack_) < needlelen)
540 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
541 return (gpointer)haystack_cur;
549 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
550 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
552 register const gchar *s = src;
553 register gchar *d = dest;
563 int iswalnum(wint_t wc)
565 return g_ascii_isalnum((int)wc);
570 int iswspace(wint_t wc)
572 return g_ascii_isspace((int)wc);
577 wint_t towlower(wint_t wc)
579 if (wc >= L'A' && wc <= L'Z')
580 return wc + L'a' - L'A';
587 size_t wcslen(const wchar_t *s)
599 /* Copy SRC to DEST. */
600 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
608 } while (c != L'\0');
615 /* Copy no more than N wide-characters of SRC to DEST. */
616 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
626 } while (c != L'\0');
637 /* Duplicate S, returning an identical malloc'd string. */
638 wchar_t *wcsdup(const wchar_t *s)
643 new_str = g_new(wchar_t, wcslen(s) + 1);
651 /* Duplicate no more than N wide-characters of S,
652 returning an identical malloc'd string. */
653 wchar_t *wcsndup(const wchar_t *s, size_t n)
658 new_str = g_new(wchar_t, n + 1);
659 wcsncpy(new_str, s, n);
660 new_str[n] = (wchar_t)0;
667 wchar_t *strdup_mbstowcs(const gchar *s)
672 new_str = g_new(wchar_t, strlen(s) + 1);
673 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
677 new_str = g_realloc(new_str,
678 sizeof(wchar_t) * (wcslen(new_str) + 1));
685 gchar *strdup_wcstombs(const wchar_t *s)
691 len = wcslen(s) * MB_CUR_MAX + 1;
692 new_str = g_new(gchar, len);
693 if (wcstombs(new_str, s, len) < 0) {
697 new_str = g_realloc(new_str, strlen(new_str) + 1);
704 /* Compare S1 and S2, ignoring case. */
705 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
711 c1 = towlower(*s1++);
712 c2 = towlower(*s2++);
715 else if (c1 == 0 && c2 == 0)
722 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
723 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
725 register size_t haystack_len, needle_len;
727 haystack_len = wcslen(haystack);
728 needle_len = wcslen(needle);
730 if (haystack_len < needle_len || needle_len == 0)
733 while (haystack_len >= needle_len) {
734 if (!wcsncasecmp(haystack, needle, needle_len))
735 return (wchar_t *)haystack;
745 gint get_mbs_len(const gchar *s)
755 mb_len = g_utf8_skip[*(guchar *)p];
767 /* Examine if next block is non-ASCII string */
768 gboolean is_next_nonascii(const gchar *s)
772 /* skip head space */
773 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
775 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
776 if (*(guchar *)p > 127 || *(guchar *)p < 32)
783 gint get_next_word_len(const gchar *s)
787 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
793 /* compare subjects */
794 gint subject_compare(const gchar *s1, const gchar *s2)
798 if (!s1 || !s2) return -1;
799 if (!*s1 || !*s2) return -1;
801 Xstrdup_a(str1, s1, return -1);
802 Xstrdup_a(str2, s2, return -1);
804 trim_subject_for_compare(str1);
805 trim_subject_for_compare(str2);
807 if (!*str1 || !*str2) return -1;
809 return strcmp(str1, str2);
812 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
816 if (!s1 || !s2) return -1;
818 Xstrdup_a(str1, s1, return -1);
819 Xstrdup_a(str2, s2, return -1);
821 trim_subject_for_sort(str1);
822 trim_subject_for_sort(str2);
824 return g_utf8_collate(str1, str2);
827 void trim_subject_for_compare(gchar *str)
831 eliminate_parenthesis(str, '[', ']');
832 eliminate_parenthesis(str, '(', ')');
835 srcp = str + subject_get_prefix_length(str);
837 memmove(str, srcp, strlen(srcp) + 1);
840 void trim_subject_for_sort(gchar *str)
846 srcp = str + subject_get_prefix_length(str);
848 memmove(str, srcp, strlen(srcp) + 1);
851 void trim_subject(gchar *str)
853 register gchar *srcp;
859 srcp = str + subject_get_prefix_length(str);
864 } else if (*srcp == '(') {
876 else if (*srcp == cl)
883 while (g_ascii_isspace(*srcp)) srcp++;
884 memmove(str, srcp, strlen(srcp) + 1);
887 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
889 register gchar *srcp, *destp;
894 while ((destp = strchr(destp, op))) {
900 else if (*srcp == cl)
906 while (g_ascii_isspace(*srcp)) srcp++;
907 memmove(destp, srcp, strlen(srcp) + 1);
911 void extract_parenthesis(gchar *str, gchar op, gchar cl)
913 register gchar *srcp, *destp;
918 while ((srcp = strchr(destp, op))) {
921 memmove(destp, srcp + 1, strlen(srcp));
926 else if (*destp == cl)
938 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
941 register gchar *srcp, *destp;
943 gboolean in_quote = FALSE;
947 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
950 memmove(destp, srcp + 1, strlen(srcp));
953 if (*destp == op && !in_quote)
955 else if (*destp == cl && !in_quote)
957 else if (*destp == quote_chr)
969 void eliminate_quote(gchar *str, gchar quote_chr)
971 register gchar *srcp, *destp;
975 while ((destp = strchr(destp, quote_chr))) {
976 if ((srcp = strchr(destp + 1, quote_chr))) {
978 while (g_ascii_isspace(*srcp)) srcp++;
979 memmove(destp, srcp, strlen(srcp) + 1);
987 void extract_quote(gchar *str, gchar quote_chr)
991 if ((str = strchr(str, quote_chr))) {
993 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
994 memmove(p - 1, p, strlen(p) + 1);
999 memmove(str, str + 1, p - str);
1004 void eliminate_address_comment(gchar *str)
1006 register gchar *srcp, *destp;
1011 while ((destp = strchr(destp, '"'))) {
1012 if ((srcp = strchr(destp + 1, '"'))) {
1017 while (g_ascii_isspace(*srcp)) srcp++;
1018 memmove(destp, srcp, strlen(srcp) + 1);
1028 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
1034 else if (*srcp == ')')
1040 while (g_ascii_isspace(*srcp)) srcp++;
1041 memmove(destp, srcp, strlen(srcp) + 1);
1045 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1047 gboolean in_quote = FALSE;
1050 if (*str == c && !in_quote)
1051 return (gchar *)str;
1052 if (*str == quote_chr)
1060 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1062 gboolean in_quote = FALSE;
1065 p = str + strlen(str) - 1;
1067 if (*p == c && !in_quote)
1069 if (*p == quote_chr)
1077 void extract_address(gchar *str)
1079 eliminate_address_comment(str);
1080 if (strchr_with_skip_quote(str, '"', '<'))
1081 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1085 void extract_list_id_str(gchar *str)
1087 if (strchr_with_skip_quote(str, '"', '<'))
1088 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1092 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1097 if (!str) return addr_list;
1099 Xstrdup_a(work, str, return addr_list);
1102 eliminate_address_comment(work);
1105 while (workp && *workp) {
1108 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1114 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1115 extract_parenthesis_with_skip_quote
1116 (workp, '"', '<', '>');
1120 addr_list = g_slist_append(addr_list, g_strdup(workp));
1128 GSList *address_list_append(GSList *addr_list, const gchar *str)
1130 return address_list_append_real(addr_list, str, TRUE);
1133 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1135 return address_list_append_real(addr_list, str, FALSE);
1138 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1142 if (!str) return msgid_list;
1145 while (strp && *strp) {
1146 const gchar *start, *end;
1149 if ((start = strchr(strp, '<')) != NULL) {
1150 end = strchr(start + 1, '>');
1155 msgid = g_strndup(start + 1, end - start - 1);
1158 msgid_list = g_slist_prepend(msgid_list, msgid);
1168 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1172 list = references_list_prepend(NULL, str);
1173 list = g_slist_reverse(list);
1174 msgid_list = g_slist_concat(msgid_list, list);
1179 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1184 if (!str) return group_list;
1186 Xstrdup_a(work, str, return group_list);
1190 while (workp && *workp) {
1193 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1201 group_list = g_slist_append(group_list,
1210 GList *add_history(GList *list, const gchar *str)
1214 g_return_val_if_fail(str != NULL, list);
1216 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1219 list = g_list_remove(list, old->data);
1220 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1223 last = g_list_last(list);
1226 list = g_list_remove(list, last->data);
1230 list = g_list_prepend(list, g_strdup(str));
1235 void remove_return(gchar *str)
1237 register gchar *p = str;
1240 if (*p == '\n' || *p == '\r')
1241 memmove(p, p + 1, strlen(p));
1247 void remove_space(gchar *str)
1249 register gchar *p = str;
1254 while (g_ascii_isspace(*(p + spc)))
1257 memmove(p, p + spc, strlen(p + spc) + 1);
1263 void unfold_line(gchar *str)
1265 register gchar *p = str;
1269 if (*p == '\n' || *p == '\r') {
1272 while (g_ascii_isspace(*(p + spc)))
1275 memmove(p, p + spc, strlen(p + spc) + 1);
1281 void subst_char(gchar *str, gchar orig, gchar subst)
1283 register gchar *p = str;
1292 void subst_chars(gchar *str, gchar *orig, gchar subst)
1294 register gchar *p = str;
1297 if (strchr(orig, *p) != NULL)
1303 void subst_for_filename(gchar *str)
1308 subst_chars(str, "\t\r\n\\/*:", '_');
1310 subst_chars(str, "\t\r\n\\/*", '_');
1314 void subst_for_shellsafe_filename(gchar *str)
1318 subst_for_filename(str);
1319 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1322 gboolean is_header_line(const gchar *str)
1324 if (str[0] == ':') return FALSE;
1326 while (*str != '\0' && *str != ' ') {
1335 gboolean is_ascii_str(const gchar *str)
1337 const guchar *p = (const guchar *)str;
1339 while (*p != '\0') {
1340 if (*p != '\t' && *p != ' ' &&
1341 *p != '\r' && *p != '\n' &&
1342 (*p < 32 || *p >= 127))
1350 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1352 const gchar *first_pos;
1353 const gchar *last_pos;
1354 const gchar *p = str;
1355 gint quote_level = -1;
1357 /* speed up line processing by only searching to the last '>' */
1358 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1359 /* skip a line if it contains a '<' before the initial '>' */
1360 if (memchr(str, '<', first_pos - str) != NULL)
1362 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1366 while (p <= last_pos) {
1367 while (p < last_pos) {
1368 if (g_ascii_isspace(*p))
1374 if (strchr(quote_chars, *p))
1376 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1377 /* any characters are allowed except '-' and space */
1379 && !strchr(quote_chars, *p)
1380 && !g_ascii_isspace(*p)
1383 if (strchr(quote_chars, *p))
1395 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1397 const gchar *p = str, *q;
1398 gint cur_line = 0, len;
1400 while ((q = strchr(p, '\n')) != NULL) {
1402 if (len > max_chars) {
1412 if (len > max_chars) {
1421 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1423 gchar * position = NULL;
1424 gchar * tmp_pos = NULL;
1427 if (quote_chars == NULL)
1430 for (i = 0; i < strlen(quote_chars); i++) {
1431 tmp_pos = strchr (str, quote_chars[i]);
1433 || (tmp_pos != NULL && position >= tmp_pos) )
1439 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1441 gchar * position = NULL;
1442 gchar * tmp_pos = NULL;
1445 if (quote_chars == NULL)
1448 for (i = 0; i < strlen(quote_chars); i++) {
1449 tmp_pos = strrchr (str, quote_chars[i]);
1451 || (tmp_pos != NULL && position <= tmp_pos) )
1457 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1459 register guint haystack_len, needle_len;
1460 gboolean in_squote = FALSE, in_dquote = FALSE;
1462 haystack_len = strlen(haystack);
1463 needle_len = strlen(needle);
1465 if (haystack_len < needle_len || needle_len == 0)
1468 while (haystack_len >= needle_len) {
1469 if (!in_squote && !in_dquote &&
1470 !strncmp(haystack, needle, needle_len))
1471 return (gchar *)haystack;
1473 /* 'foo"bar"' -> foo"bar"
1474 "foo'bar'" -> foo'bar' */
1475 if (*haystack == '\'') {
1478 else if (!in_dquote)
1480 } else if (*haystack == '\"') {
1483 else if (!in_squote)
1494 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1497 gchar quote_chr = '"';
1499 gboolean in_quote = FALSE;
1503 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1507 if (*p == op && !in_quote)
1509 else if (*p == cl && !in_quote)
1511 else if (*p == quote_chr)
1524 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1527 GSList *string_list = NULL, *slist;
1529 const gchar *s_op, *s_cl;
1532 g_return_val_if_fail(str != NULL, NULL);
1535 max_tokens = G_MAXINT;
1537 s_op = strchr_with_skip_quote(str, '"', op);
1538 if (!s_op) return NULL;
1540 s_cl = strchr_parenthesis_close(str, op, cl);
1548 new_string = g_new(gchar, len + 1);
1549 strncpy(new_string, str, len);
1550 new_string[len] = 0;
1551 string_list = g_slist_prepend(string_list, new_string);
1555 while (*str && g_ascii_isspace(*str)) str++;
1557 string_list = g_slist_prepend(string_list,
1560 s_op = strchr_with_skip_quote(str, '"', op);
1561 if (!--max_tokens || !s_op) break;
1565 s_cl = strchr_parenthesis_close(str, op, cl);
1566 } while (--max_tokens && s_cl);
1569 str_array = g_new(gchar*, n);
1573 str_array[i--] = NULL;
1574 for (slist = string_list; slist; slist = slist->next)
1575 str_array[i--] = slist->data;
1577 g_slist_free(string_list);
1582 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1585 GSList *string_list = NULL, *slist;
1586 gchar **str_array, *s, *new_str;
1587 guint i, n = 1, len;
1589 g_return_val_if_fail(str != NULL, NULL);
1590 g_return_val_if_fail(delim != NULL, NULL);
1593 max_tokens = G_MAXINT;
1595 s = strstr_with_skip_quote(str, delim);
1597 guint delimiter_len = strlen(delim);
1601 new_str = g_strndup(str, len);
1603 if (new_str[0] == '\'' || new_str[0] == '\"') {
1604 if (new_str[len - 1] == new_str[0]) {
1605 new_str[len - 1] = '\0';
1606 memmove(new_str, new_str + 1, len - 1);
1609 string_list = g_slist_prepend(string_list, new_str);
1611 str = s + delimiter_len;
1612 s = strstr_with_skip_quote(str, delim);
1613 } while (--max_tokens && s);
1617 new_str = g_strdup(str);
1618 if (new_str[0] == '\'' || new_str[0] == '\"') {
1620 if (new_str[len - 1] == new_str[0]) {
1621 new_str[len - 1] = '\0';
1622 memmove(new_str, new_str + 1, len - 1);
1625 string_list = g_slist_prepend(string_list, new_str);
1629 str_array = g_new(gchar*, n);
1633 str_array[i--] = NULL;
1634 for (slist = string_list; slist; slist = slist->next)
1635 str_array[i--] = slist->data;
1637 g_slist_free(string_list);
1642 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1644 gchar *abbrev_group;
1646 const gchar *p = group;
1649 g_return_val_if_fail(group != NULL, NULL);
1651 last = group + strlen(group);
1652 abbrev_group = ap = g_malloc(strlen(group) + 1);
1657 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1659 while (*p != '.') p++;
1662 return abbrev_group;
1667 return abbrev_group;
1670 gchar *trim_string(const gchar *str, gint len)
1672 const gchar *p = str;
1677 if (!str) return NULL;
1678 if (strlen(str) <= len)
1679 return g_strdup(str);
1680 if (g_utf8_validate(str, -1, NULL) == FALSE)
1681 return g_strdup(str);
1683 while (*p != '\0') {
1684 mb_len = g_utf8_skip[*(guchar *)p];
1687 else if (new_len + mb_len > len)
1694 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1695 return g_strconcat(new_str, "...", NULL);
1698 GList *uri_list_extract_filenames(const gchar *uri_list)
1700 GList *result = NULL;
1702 gchar *escaped_utf8uri;
1708 while (g_ascii_isspace(*p)) p++;
1709 if (!strncmp(p, "file:", 5)) {
1712 while (*q && *q != '\n' && *q != '\r') q++;
1715 gchar *file, *locale_file = NULL;
1717 while (q > p && g_ascii_isspace(*q))
1719 Xalloca(escaped_utf8uri, q - p + 2,
1721 Xalloca(file, q - p + 2,
1724 strncpy(escaped_utf8uri, p, q - p + 1);
1725 escaped_utf8uri[q - p + 1] = '\0';
1726 decode_uri(file, escaped_utf8uri);
1728 * g_filename_from_uri() rejects escaped/locale encoded uri
1729 * string which come from Nautilus.
1731 if (g_utf8_validate(file, -1, NULL))
1733 = conv_codeset_strdup(
1736 conv_get_locale_charset_str());
1738 locale_file = g_strdup(file + 5);
1739 result = g_list_append(result, locale_file);
1743 p = strchr(p, '\n');
1750 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1753 static gint axtoi(const gchar *hexstr)
1755 gint hi, lo, result;
1758 if ('0' <= hi && hi <= '9') {
1761 if ('a' <= hi && hi <= 'f') {
1764 if ('A' <= hi && hi <= 'F') {
1769 if ('0' <= lo && lo <= '9') {
1772 if ('a' <= lo && lo <= 'f') {
1775 if ('A' <= lo && lo <= 'F') {
1778 result = lo + (16 * hi);
1782 gboolean is_uri_string(const gchar *str)
1784 while (str && *str && g_ascii_isspace(*str))
1786 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1787 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1788 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1789 g_ascii_strncasecmp(str, "www.", 4) == 0);
1792 gchar *get_uri_path(const gchar *uri)
1794 while (uri && *uri && g_ascii_isspace(*uri))
1796 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1797 return (gchar *)(uri + 7);
1798 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1799 return (gchar *)(uri + 8);
1800 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1801 return (gchar *)(uri + 6);
1803 return (gchar *)uri;
1806 gint get_uri_len(const gchar *str)
1810 if (is_uri_string(str)) {
1811 for (p = str; *p != '\0'; p++) {
1812 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1821 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1822 * plusses, and escape characters are used)
1824 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1826 gchar *dec = decoded_uri;
1827 const gchar *enc = encoded_uri;
1832 if (isxdigit((guchar)enc[0]) &&
1833 isxdigit((guchar)enc[1])) {
1851 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1852 gchar **subject, gchar **body)
1857 Xstrdup_a(tmp_mailto, mailto, return -1);
1859 if (!strncmp(tmp_mailto, "mailto:", 7))
1862 p = strchr(tmp_mailto, '?');
1869 *to = g_strdup(tmp_mailto);
1872 gchar *field, *value;
1889 if (*value == '\0') continue;
1891 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1892 *cc = g_strdup(value);
1893 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1894 *bcc = g_strdup(value);
1895 } else if (subject && !*subject &&
1896 !g_ascii_strcasecmp(field, "subject")) {
1897 *subject = g_malloc(strlen(value) + 1);
1898 decode_uri(*subject, value);
1899 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1900 *body = g_malloc(strlen(value) + 1);
1901 decode_uri(*body, value);
1910 #include <windows.h>
1911 #ifndef CSIDL_APPDATA
1912 #define CSIDL_APPDATA 0x001a
1914 #ifndef CSIDL_LOCAL_APPDATA
1915 #define CSIDL_LOCAL_APPDATA 0x001c
1917 #ifndef CSIDL_FLAG_CREATE
1918 #define CSIDL_FLAG_CREATE 0x8000
1920 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1924 w32_strerror (int w32_errno)
1926 static char strerr[256];
1927 int ec = (int)GetLastError ();
1931 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1932 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1933 strerr, DIM (strerr)-1, NULL);
1937 static __inline__ void *
1938 dlopen (const char * name, int flag)
1940 void * hd = LoadLibrary (name);
1944 static __inline__ void *
1945 dlsym (void * hd, const char * sym)
1949 void * fnc = GetProcAddress (hd, sym);
1958 static __inline__ const char *
1961 return w32_strerror (0);
1965 static __inline__ int
1977 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1979 static int initialized;
1980 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1984 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1990 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1992 handle = dlopen (dllnames[i], RTLD_LAZY);
1995 func = dlsym (handle, "SHGetFolderPathA");
2006 return func (a,b,c,d,e);
2011 /* Returns a static string with the directroy from which the module
2012 has been loaded. Returns an empty string on error. */
2013 static char *w32_get_module_dir(void)
2015 static char *moddir;
2018 char name[MAX_PATH+10];
2021 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
2024 p = strrchr (name, '\\');
2030 moddir = g_strdup (name);
2034 #endif /* G_OS_WIN32 */
2036 /* Return a static string with the locale dir. */
2037 const gchar *get_locale_dir(void)
2039 static gchar *loc_dir;
2043 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
2044 "\\share\\locale", NULL);
2047 loc_dir = LOCALEDIR;
2053 const gchar *get_home_dir(void)
2056 static char home_dir[MAX_PATH] = "";
2058 if (home_dir[0] == '\0') {
2059 if (w32_shgetfolderpath
2060 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
2061 NULL, 0, home_dir) < 0)
2062 strcpy (home_dir, "C:\\Sylpheed");
2066 static const gchar *homeenv = NULL;
2071 if (!homeenv && g_getenv("HOME") != NULL)
2072 homeenv = g_strdup(g_getenv("HOME"));
2074 homeenv = g_get_home_dir();
2080 const gchar *get_rc_dir(void)
2082 static gchar *rc_dir = NULL;
2085 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2091 const gchar *get_mail_base_dir(void)
2094 static gchar *mail_base_dir = NULL;
2097 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2100 return mail_base_dir;
2102 return get_home_dir();
2106 const gchar *get_news_cache_dir(void)
2108 static gchar *news_cache_dir = NULL;
2110 if (!news_cache_dir)
2111 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2112 NEWS_CACHE_DIR, NULL);
2114 return news_cache_dir;
2117 const gchar *get_imap_cache_dir(void)
2119 static gchar *imap_cache_dir = NULL;
2121 if (!imap_cache_dir)
2122 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2123 IMAP_CACHE_DIR, NULL);
2125 return imap_cache_dir;
2128 const gchar *get_mbox_cache_dir(void)
2130 static gchar *mbox_cache_dir = NULL;
2132 if (!mbox_cache_dir)
2133 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2134 MBOX_CACHE_DIR, NULL);
2136 return mbox_cache_dir;
2139 const gchar *get_mime_tmp_dir(void)
2141 static gchar *mime_tmp_dir = NULL;
2144 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2145 MIME_TMP_DIR, NULL);
2147 return mime_tmp_dir;
2150 const gchar *get_template_dir(void)
2152 static gchar *template_dir = NULL;
2155 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2156 TEMPLATE_DIR, NULL);
2158 return template_dir;
2161 const gchar *get_header_cache_dir(void)
2163 static gchar *header_dir = NULL;
2166 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2167 HEADER_CACHE_DIR, NULL);
2172 /* Return the default directory for Plugins. */
2173 const gchar *get_plugin_dir(void)
2176 static gchar *plugin_dir = NULL;
2179 plugin_dir = g_strconcat(w32_get_module_dir(),
2180 "\\lib\\sylpheed-claws\\plugins\\",
2184 if (is_dir_exist(PLUGINDIR))
2187 static gchar *plugin_dir = NULL;
2189 plugin_dir = g_strconcat(get_rc_dir(),
2190 G_DIR_SEPARATOR_S, "plugins",
2191 G_DIR_SEPARATOR_S, NULL);
2197 const gchar *get_tmp_dir(void)
2199 static gchar *tmp_dir = NULL;
2202 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2208 gchar *get_tmp_file(void)
2211 static guint32 id = 0;
2213 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2214 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2219 const gchar *get_domain_name(void)
2222 static gchar *domain_name = NULL;
2228 if (uname(&uts) < 0) {
2230 domain_name = "unknown";
2232 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2233 perror("gethostbyname");
2234 domain_name = g_strdup(uts.nodename);
2236 domain_name = g_strdup(hp->h_name);
2240 debug_print("domain name = %s\n", domain_name);
2249 off_t get_file_size(const gchar *file)
2253 if (g_stat(file, &s) < 0) {
2254 FILE_OP_ERROR(file, "stat");
2261 off_t get_file_size_as_crlf(const gchar *file)
2265 gchar buf[BUFFSIZE];
2267 if ((fp = g_fopen(file, "rb")) == NULL) {
2268 FILE_OP_ERROR(file, "fopen");
2272 while (fgets(buf, sizeof(buf), fp) != NULL) {
2274 size += strlen(buf) + 2;
2278 FILE_OP_ERROR(file, "fgets");
2287 off_t get_left_file_size(FILE *fp)
2293 if ((pos = ftell(fp)) < 0) {
2297 if (fseek(fp, 0L, SEEK_END) < 0) {
2301 if ((end = ftell(fp)) < 0) {
2306 if (fseek(fp, pos, SEEK_SET) < 0) {
2314 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2321 if (g_stat(file, &s) < 0) {
2322 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2326 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2333 /* Test on whether FILE is a relative file name. This is
2334 * straightforward for Unix but more complex for Windows. */
2335 gboolean is_relative_filename(const gchar *file)
2340 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2341 return FALSE; /* Prefixed with a hostname - this can't
2342 * be a relative name. */
2344 if ( ((*file >= 'a' && *file <= 'z')
2345 || (*file >= 'A' && *file <= 'Z'))
2347 file += 2; /* Skip drive letter. */
2349 return !(*file == '\\' || *file == '/');
2351 return !(*file == G_DIR_SEPARATOR);
2356 gboolean is_dir_exist(const gchar *dir)
2361 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2364 gboolean is_file_entry_exist(const gchar *file)
2369 return g_file_test(file, G_FILE_TEST_EXISTS);
2372 gboolean dirent_is_regular_file(struct dirent *d)
2374 #ifdef HAVE_DIRENT_D_TYPE
2375 if (d->d_type == DT_REG)
2377 else if (d->d_type != DT_UNKNOWN)
2381 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2384 gboolean dirent_is_directory(struct dirent *d)
2386 #ifdef HAVE_DIRENT_D_TYPE
2387 if (d->d_type == DT_DIR)
2389 else if (d->d_type != DT_UNKNOWN)
2393 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2396 gint change_dir(const gchar *dir)
2398 gchar *prevdir = NULL;
2401 prevdir = g_get_current_dir();
2403 if (g_chdir(dir) < 0) {
2404 FILE_OP_ERROR(dir, "chdir");
2405 if (debug_mode) g_free(prevdir);
2407 } else if (debug_mode) {
2410 cwd = g_get_current_dir();
2411 if (strcmp(prevdir, cwd) != 0)
2412 g_print("current dir: %s\n", cwd);
2420 gint make_dir(const gchar *dir)
2422 if (g_mkdir(dir, S_IRWXU) < 0) {
2423 FILE_OP_ERROR(dir, "mkdir");
2426 if (g_chmod(dir, S_IRWXU) < 0)
2427 FILE_OP_ERROR(dir, "chmod");
2432 gint make_dir_hier(const gchar *dir)
2437 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2438 parent_dir = g_strndup(dir, p - dir);
2439 if (*parent_dir != '\0') {
2440 if (!is_dir_exist(parent_dir)) {
2441 if (make_dir(parent_dir) < 0) {
2450 if (!is_dir_exist(dir)) {
2451 if (make_dir(dir) < 0)
2458 gint remove_all_files(const gchar *dir)
2461 const gchar *dir_name;
2464 prev_dir = g_get_current_dir();
2466 if (g_chdir(dir) < 0) {
2467 FILE_OP_ERROR(dir, "chdir");
2472 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2473 g_warning("failed to open directory: %s\n", dir);
2478 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2479 if (g_unlink(dir_name) < 0)
2480 FILE_OP_ERROR(dir_name, "unlink");
2485 if (g_chdir(prev_dir) < 0) {
2486 FILE_OP_ERROR(prev_dir, "chdir");
2496 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2499 const gchar *dir_name;
2503 prev_dir = g_get_current_dir();
2505 if (g_chdir(dir) < 0) {
2506 FILE_OP_ERROR(dir, "chdir");
2511 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2512 g_warning("failed to open directory: %s\n", dir);
2517 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2518 file_no = to_number(dir_name);
2519 if (file_no > 0 && first <= file_no && file_no <= last) {
2520 if (is_dir_exist(dir_name))
2522 if (g_unlink(dir_name) < 0)
2523 FILE_OP_ERROR(dir_name, "unlink");
2529 if (g_chdir(prev_dir) < 0) {
2530 FILE_OP_ERROR(prev_dir, "chdir");
2540 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2543 const gchar *dir_name;
2547 prev_dir = g_get_current_dir();
2549 if (g_chdir(dir) < 0) {
2550 FILE_OP_ERROR(dir, "chdir");
2555 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2556 FILE_OP_ERROR(dir, "opendir");
2561 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2562 file_no = to_number(dir_name);
2563 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2564 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2565 if (is_dir_exist(dir_name))
2567 if (g_unlink(dir_name) < 0)
2568 FILE_OP_ERROR(dir_name, "unlink");
2574 if (g_chdir(prev_dir) < 0) {
2575 FILE_OP_ERROR(prev_dir, "chdir");
2585 gint remove_all_numbered_files(const gchar *dir)
2587 return remove_numbered_files(dir, 0, UINT_MAX);
2590 gint remove_expired_files(const gchar *dir, guint hours)
2593 const gchar *dir_name;
2597 time_t mtime, now, expire_time;
2599 prev_dir = g_get_current_dir();
2601 if (g_chdir(dir) < 0) {
2602 FILE_OP_ERROR(dir, "chdir");
2607 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2608 g_warning("failed to open directory: %s\n", dir);
2614 expire_time = hours * 60 * 60;
2616 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2617 file_no = to_number(dir_name);
2619 if (g_stat(dir_name, &s) < 0) {
2620 FILE_OP_ERROR(dir_name, "stat");
2623 if (S_ISDIR(s.st_mode))
2625 mtime = MAX(s.st_mtime, s.st_atime);
2626 if (now - mtime > expire_time) {
2627 if (g_unlink(dir_name) < 0)
2628 FILE_OP_ERROR(dir_name, "unlink");
2635 if (g_chdir(prev_dir) < 0) {
2636 FILE_OP_ERROR(prev_dir, "chdir");
2646 gint remove_dir_recursive(const gchar *dir)
2650 const gchar *dir_name;
2653 if (g_stat(dir, &s) < 0) {
2654 FILE_OP_ERROR(dir, "stat");
2655 if (ENOENT == errno) return 0;
2659 if (!S_ISDIR(s.st_mode)) {
2660 if (g_unlink(dir) < 0) {
2661 FILE_OP_ERROR(dir, "unlink");
2668 prev_dir = g_get_current_dir();
2669 /* g_print("prev_dir = %s\n", prev_dir); */
2671 if (!path_cmp(prev_dir, dir)) {
2673 if (g_chdir("..") < 0) {
2674 FILE_OP_ERROR(dir, "chdir");
2677 prev_dir = g_get_current_dir();
2680 if (g_chdir(dir) < 0) {
2681 FILE_OP_ERROR(dir, "chdir");
2686 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2687 g_warning("failed to open directory: %s\n", dir);
2693 /* remove all files in the directory */
2694 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2695 /* g_print("removing %s\n", dir_name); */
2697 if (is_dir_exist(dir_name)) {
2698 if (remove_dir_recursive(dir_name) < 0) {
2699 g_warning("can't remove directory\n");
2703 if (g_unlink(dir_name) < 0)
2704 FILE_OP_ERROR(dir_name, "unlink");
2710 if (g_chdir(prev_dir) < 0) {
2711 FILE_OP_ERROR(prev_dir, "chdir");
2718 if (g_rmdir(dir) < 0) {
2719 FILE_OP_ERROR(dir, "rmdir");
2726 gint rename_force(const gchar *oldpath, const gchar *newpath)
2729 if (!is_file_entry_exist(oldpath)) {
2733 if (is_file_exist(newpath)) {
2734 if (g_unlink(newpath) < 0)
2735 FILE_OP_ERROR(newpath, "unlink");
2738 return g_rename(oldpath, newpath);
2742 * Append src file body to the tail of dest file.
2743 * Now keep_backup has no effects.
2745 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2747 FILE *src_fp, *dest_fp;
2751 gboolean err = FALSE;
2753 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2754 FILE_OP_ERROR(src, "fopen");
2758 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2759 FILE_OP_ERROR(dest, "fopen");
2764 if (change_file_mode_rw(dest_fp, dest) < 0) {
2765 FILE_OP_ERROR(dest, "chmod");
2766 g_warning("can't change file mode\n");
2769 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2770 if (n_read < sizeof(buf) && ferror(src_fp))
2772 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2773 g_warning("writing to %s failed.\n", dest);
2781 if (ferror(src_fp)) {
2782 FILE_OP_ERROR(src, "fread");
2786 if (fclose(dest_fp) == EOF) {
2787 FILE_OP_ERROR(dest, "fclose");
2799 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2801 FILE *src_fp, *dest_fp;
2804 gchar *dest_bak = NULL;
2805 gboolean err = FALSE;
2807 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2808 FILE_OP_ERROR(src, "fopen");
2811 if (is_file_exist(dest)) {
2812 dest_bak = g_strconcat(dest, ".bak", NULL);
2813 if (rename_force(dest, dest_bak) < 0) {
2814 FILE_OP_ERROR(dest, "rename");
2821 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2822 FILE_OP_ERROR(dest, "fopen");
2825 if (rename_force(dest_bak, dest) < 0)
2826 FILE_OP_ERROR(dest_bak, "rename");
2832 if (change_file_mode_rw(dest_fp, dest) < 0) {
2833 FILE_OP_ERROR(dest, "chmod");
2834 g_warning("can't change file mode\n");
2837 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2838 if (n_read < sizeof(buf) && ferror(src_fp))
2840 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2841 g_warning("writing to %s failed.\n", dest);
2846 if (rename_force(dest_bak, dest) < 0)
2847 FILE_OP_ERROR(dest_bak, "rename");
2854 if (ferror(src_fp)) {
2855 FILE_OP_ERROR(src, "fread");
2859 if (fclose(dest_fp) == EOF) {
2860 FILE_OP_ERROR(dest, "fclose");
2867 if (rename_force(dest_bak, dest) < 0)
2868 FILE_OP_ERROR(dest_bak, "rename");
2874 if (keep_backup == FALSE && dest_bak)
2882 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2884 if (overwrite == FALSE && is_file_exist(dest)) {
2885 g_warning("move_file(): file %s already exists.", dest);
2889 if (rename_force(src, dest) == 0) return 0;
2891 if (EXDEV != errno) {
2892 FILE_OP_ERROR(src, "rename");
2896 if (copy_file(src, dest, FALSE) < 0) return -1;
2903 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2906 gint bytes_left, to_read;
2909 if (fseek(fp, offset, SEEK_SET) < 0) {
2914 bytes_left = length;
2915 to_read = MIN(bytes_left, sizeof(buf));
2917 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2918 if (n_read < to_read && ferror(fp))
2920 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2923 bytes_left -= n_read;
2924 if (bytes_left == 0)
2926 to_read = MIN(bytes_left, sizeof(buf));
2937 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2940 gboolean err = FALSE;
2942 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2943 FILE_OP_ERROR(dest, "fopen");
2947 if (change_file_mode_rw(dest_fp, dest) < 0) {
2948 FILE_OP_ERROR(dest, "chmod");
2949 g_warning("can't change file mode\n");
2952 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2955 if (!err && fclose(dest_fp) == EOF) {
2956 FILE_OP_ERROR(dest, "fclose");
2961 g_warning("writing to %s failed.\n", dest);
2969 /* convert line endings into CRLF. If the last line doesn't end with
2970 * linebreak, add it.
2972 gchar *canonicalize_str(const gchar *str)
2978 for (p = str; *p != '\0'; ++p) {
2985 if (p == str || *(p - 1) != '\n')
2988 out = outp = g_malloc(new_len + 1);
2989 for (p = str; *p != '\0'; ++p) {
2996 if (p == str || *(p - 1) != '\n') {
3005 gint canonicalize_file(const gchar *src, const gchar *dest)
3007 FILE *src_fp, *dest_fp;
3008 gchar buf[BUFFSIZE];
3010 gboolean err = FALSE;
3011 gboolean last_linebreak = FALSE;
3013 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3014 FILE_OP_ERROR(src, "fopen");
3018 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3019 FILE_OP_ERROR(dest, "fopen");
3024 if (change_file_mode_rw(dest_fp, dest) < 0) {
3025 FILE_OP_ERROR(dest, "chmod");
3026 g_warning("can't change file mode\n");
3029 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3033 if (len == 0) break;
3034 last_linebreak = FALSE;
3036 if (buf[len - 1] != '\n') {
3037 last_linebreak = TRUE;
3038 r = fputs(buf, dest_fp);
3039 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3040 r = fputs(buf, dest_fp);
3043 r = fwrite(buf, 1, len - 1, dest_fp);
3048 r = fputs("\r\n", dest_fp);
3052 g_warning("writing to %s failed.\n", dest);
3060 if (last_linebreak == TRUE) {
3061 if (fputs("\r\n", dest_fp) == EOF)
3065 if (ferror(src_fp)) {
3066 FILE_OP_ERROR(src, "fgets");
3070 if (fclose(dest_fp) == EOF) {
3071 FILE_OP_ERROR(dest, "fclose");
3083 gint canonicalize_file_replace(const gchar *file)
3087 tmp_file = get_tmp_file();
3089 if (canonicalize_file(file, tmp_file) < 0) {
3094 if (move_file(tmp_file, file, TRUE) < 0) {
3095 g_warning("can't replace %s .\n", file);
3105 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3107 FILE *src_fp, *dest_fp;
3108 gchar buf[BUFFSIZE];
3109 gboolean err = FALSE;
3111 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3112 FILE_OP_ERROR(src, "fopen");
3116 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3117 FILE_OP_ERROR(dest, "fopen");
3122 if (change_file_mode_rw(dest_fp, dest) < 0) {
3123 FILE_OP_ERROR(dest, "chmod");
3124 g_warning("can't change file mode\n");
3127 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3129 if (fputs(buf, dest_fp) == EOF) {
3130 g_warning("writing to %s failed.\n", dest);
3138 if (ferror(src_fp)) {
3139 FILE_OP_ERROR(src, "fgets");
3143 if (fclose(dest_fp) == EOF) {
3144 FILE_OP_ERROR(dest, "fclose");
3156 gint uncanonicalize_file_replace(const gchar *file)
3160 tmp_file = get_tmp_file();
3162 if (uncanonicalize_file(file, tmp_file) < 0) {
3167 if (move_file(tmp_file, file, TRUE) < 0) {
3168 g_warning("can't replace %s .\n", file);
3178 gchar *normalize_newlines(const gchar *str)
3180 const gchar *p = str;
3183 out = outp = g_malloc(strlen(str) + 1);
3184 for (p = str; *p != '\0'; ++p) {
3186 if (*(p + 1) != '\n')
3197 gchar *get_outgoing_rfc2822_str(FILE *fp)
3199 gchar buf[BUFFSIZE];
3203 str = g_string_new(NULL);
3205 /* output header part */
3206 while (fgets(buf, sizeof(buf), fp) != NULL) {
3208 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3215 else if (next != ' ' && next != '\t') {
3219 if (fgets(buf, sizeof(buf), fp) == NULL)
3223 g_string_append(str, buf);
3224 g_string_append(str, "\r\n");
3230 /* output body part */
3231 while (fgets(buf, sizeof(buf), fp) != NULL) {
3234 g_string_append_c(str, '.');
3235 g_string_append(str, buf);
3236 g_string_append(str, "\r\n");
3240 g_string_free(str, FALSE);
3246 * Create a new boundary in a way that it is very unlikely that this
3247 * will occur in the following text. It would be easy to ensure
3248 * uniqueness if everything is either quoted-printable or base64
3249 * encoded (note that conversion is allowed), but because MIME bodies
3250 * may be nested, it may happen that the same boundary has already
3253 * boundary := 0*69<bchars> bcharsnospace
3254 * bchars := bcharsnospace / " "
3255 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3256 * "+" / "_" / "," / "-" / "." /
3257 * "/" / ":" / "=" / "?"
3259 * some special characters removed because of buggy MTAs
3262 gchar *generate_mime_boundary(const gchar *prefix)
3264 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3265 "abcdefghijklmnopqrstuvwxyz"
3270 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3271 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3274 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3278 gint change_file_mode_rw(FILE *fp, const gchar *file)
3281 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3283 return g_chmod(file, S_IRUSR|S_IWUSR);
3287 FILE *my_tmpfile(void)
3289 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3290 const gchar suffix[] = ".XXXXXX";
3291 const gchar *tmpdir;
3293 const gchar *progname;
3300 tmpdir = get_tmp_dir();
3301 tmplen = strlen(tmpdir);
3302 progname = g_get_prgname();
3303 if (progname == NULL)
3304 progname = "sylpheed-claws";
3305 proglen = strlen(progname);
3306 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3309 memcpy(fname, tmpdir, tmplen);
3310 fname[tmplen] = G_DIR_SEPARATOR;
3311 memcpy(fname + tmplen + 1, progname, proglen);
3312 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3314 fd = mkstemp(fname);
3321 /* verify that we can write in the file after unlinking */
3322 if (write(fd, buf, 1) < 0) {
3329 fp = fdopen(fd, "w+b");
3337 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3342 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3346 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3347 dir, G_DIR_SEPARATOR);
3348 fd = mkstemp_name(template, filename);
3351 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3352 fd = mkstemp(*filename);
3354 return fdopen(fd, "w+");
3357 FILE *str_open_as_stream(const gchar *str)
3362 g_return_val_if_fail(str != NULL, NULL);
3366 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3371 if (len == 0) return fp;
3373 if (fwrite(str, 1, len, fp) != len) {
3374 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3383 gint str_write_to_file(const gchar *str, const gchar *file)
3388 g_return_val_if_fail(str != NULL, -1);
3389 g_return_val_if_fail(file != NULL, -1);
3391 if ((fp = g_fopen(file, "wb")) == NULL) {
3392 FILE_OP_ERROR(file, "fopen");
3402 if (fwrite(str, 1, len, fp) != len) {
3403 FILE_OP_ERROR(file, "fwrite");
3409 if (fclose(fp) == EOF) {
3410 FILE_OP_ERROR(file, "fclose");
3418 gchar *file_read_to_str(const gchar *file)
3423 g_return_val_if_fail(file != NULL, NULL);
3425 if ((fp = g_fopen(file, "rb")) == NULL) {
3426 FILE_OP_ERROR(file, "fopen");
3430 str = file_read_stream_to_str(fp);
3437 gchar *file_read_stream_to_str(FILE *fp)
3444 g_return_val_if_fail(fp != NULL, NULL);
3446 array = g_byte_array_new();
3448 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3449 if (n_read < sizeof(buf) && ferror(fp))
3451 g_byte_array_append(array, buf, n_read);
3455 FILE_OP_ERROR("file stream", "fread");
3456 g_byte_array_free(array, TRUE);
3461 g_byte_array_append(array, buf, 1);
3462 str = (gchar *)array->data;
3463 g_byte_array_free(array, FALSE);
3465 if (!g_utf8_validate(str, -1, NULL)) {
3466 const gchar *src_codeset, *dest_codeset;
3468 src_codeset = conv_get_locale_charset_str();
3469 dest_codeset = CS_UTF_8;
3470 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3478 gint execute_async(gchar *const argv[])
3480 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3482 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3483 NULL, NULL, NULL, FALSE) == FALSE) {
3484 g_warning("Can't execute command: %s\n", argv[0]);
3491 gint execute_sync(gchar *const argv[])
3495 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3497 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3498 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3499 g_warning("Can't execute command: %s\n", argv[0]);
3504 if (WIFEXITED(status))
3505 return WEXITSTATUS(status);
3513 gint execute_command_line(const gchar *cmdline, gboolean async)
3518 debug_print("execute_command_line(): executing: %s\n", cmdline);
3520 argv = strsplit_with_quote(cmdline, " ", 0);
3523 ret = execute_async(argv);
3525 ret = execute_sync(argv);
3532 gchar *get_command_output(const gchar *cmdline)
3534 gchar *child_stdout;
3537 g_return_val_if_fail(cmdline != NULL, NULL);
3539 debug_print("get_command_output(): executing: %s\n", cmdline);
3541 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3543 g_warning("Can't execute command: %s\n", cmdline);
3547 return child_stdout;
3550 static gint is_unchanged_uri_char(char c)
3562 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3568 for(i = 0; i < strlen(uri) ; i++) {
3569 if (is_unchanged_uri_char(uri[i])) {
3570 if (k + 2 >= bufsize)
3572 encoded_uri[k++] = uri[i];
3575 char * hexa = "0123456789ABCDEF";
3577 if (k + 4 >= bufsize)
3579 encoded_uri[k++] = '%';
3580 encoded_uri[k++] = hexa[uri[i] / 16];
3581 encoded_uri[k++] = hexa[uri[i] % 16];
3587 gint open_uri(const gchar *uri, const gchar *cmdline)
3589 gchar buf[BUFFSIZE];
3591 gchar encoded_uri[BUFFSIZE];
3593 g_return_val_if_fail(uri != NULL, -1);
3595 /* an option to choose whether to use encode_uri or not ? */
3596 encode_uri(encoded_uri, BUFFSIZE, uri);
3599 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3600 !strchr(p + 2, '%'))
3601 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3604 g_warning("Open URI command line is invalid "
3605 "(there must be only one '%%s'): %s",
3607 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3610 execute_command_line(buf, TRUE);
3615 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3617 gchar buf[BUFFSIZE];
3620 g_return_val_if_fail(filepath != NULL, -1);
3623 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3624 !strchr(p + 2, '%'))
3625 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3628 g_warning("Open Text Editor command line is invalid "
3629 "(there must be only one '%%s'): %s",
3631 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3634 execute_command_line(buf, TRUE);
3639 time_t remote_tzoffset_sec(const gchar *zone)
3641 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3647 time_t remoteoffset;
3649 strncpy(zone3, zone, 3);
3653 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3654 (c == '+' || c == '-')) {
3655 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3657 remoteoffset = -remoteoffset;
3658 } else if (!strncmp(zone, "UT" , 2) ||
3659 !strncmp(zone, "GMT", 2)) {
3661 } else if (strlen(zone3) == 3) {
3662 for (p = ustzstr; *p != '\0'; p += 3) {
3663 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3664 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3665 remoteoffset = iustz * 3600;
3671 } else if (strlen(zone3) == 1) {
3673 case 'Z': remoteoffset = 0; break;
3674 case 'A': remoteoffset = -1; break;
3675 case 'B': remoteoffset = -2; break;
3676 case 'C': remoteoffset = -3; break;
3677 case 'D': remoteoffset = -4; break;
3678 case 'E': remoteoffset = -5; break;
3679 case 'F': remoteoffset = -6; break;
3680 case 'G': remoteoffset = -7; break;
3681 case 'H': remoteoffset = -8; break;
3682 case 'I': remoteoffset = -9; break;
3683 case 'K': remoteoffset = -10; break; /* J is not used */
3684 case 'L': remoteoffset = -11; break;
3685 case 'M': remoteoffset = -12; break;
3686 case 'N': remoteoffset = 1; break;
3687 case 'O': remoteoffset = 2; break;
3688 case 'P': remoteoffset = 3; break;
3689 case 'Q': remoteoffset = 4; break;
3690 case 'R': remoteoffset = 5; break;
3691 case 'S': remoteoffset = 6; break;
3692 case 'T': remoteoffset = 7; break;
3693 case 'U': remoteoffset = 8; break;
3694 case 'V': remoteoffset = 9; break;
3695 case 'W': remoteoffset = 10; break;
3696 case 'X': remoteoffset = 11; break;
3697 case 'Y': remoteoffset = 12; break;
3698 default: remoteoffset = 0; break;
3700 remoteoffset = remoteoffset * 3600;
3704 return remoteoffset;
3707 time_t tzoffset_sec(time_t *now)
3713 lt = localtime(now);
3715 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3717 if (lt->tm_year < gmt.tm_year)
3719 else if (lt->tm_year > gmt.tm_year)
3721 else if (lt->tm_yday < gmt.tm_yday)
3723 else if (lt->tm_yday > gmt.tm_yday)
3726 if (off >= 24 * 60) /* should be impossible */
3727 off = 23 * 60 + 59; /* if not, insert silly value */
3728 if (off <= -24 * 60)
3729 off = -(23 * 60 + 59);
3734 /* calculate timezone offset */
3735 gchar *tzoffset(time_t *now)
3737 static gchar offset_string[6];
3743 lt = localtime(now);
3745 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3747 if (lt->tm_year < gmt.tm_year)
3749 else if (lt->tm_year > gmt.tm_year)
3751 else if (lt->tm_yday < gmt.tm_yday)
3753 else if (lt->tm_yday > gmt.tm_yday)
3761 if (off >= 24 * 60) /* should be impossible */
3762 off = 23 * 60 + 59; /* if not, insert silly value */
3764 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3766 return offset_string;
3769 void get_rfc822_date(gchar *buf, gint len)
3773 gchar day[4], mon[4];
3774 gint dd, hh, mm, ss, yyyy;
3779 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3780 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3781 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3782 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3785 /* just a wrapper to suppress the warning of gcc about %c */
3786 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3787 const struct tm *tm)
3789 return strftime(s, max, format, tm);
3792 void debug_set_mode(gboolean mode)
3797 gboolean debug_get_mode(void)
3802 void debug_print_real(const gchar *format, ...)
3805 gchar buf[BUFFSIZE];
3807 if (!debug_mode) return;
3809 va_start(args, format);
3810 g_vsnprintf(buf, sizeof(buf), format, args);
3817 const char * debug_srcname(const char *file)
3819 const char *s = strrchr (file, '/');
3824 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3826 if (subject == NULL)
3829 subject += subject_get_prefix_length(subject);
3831 return g_hash_table_lookup(subject_table, subject);
3834 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3837 if (subject == NULL || *subject == 0)
3839 subject += subject_get_prefix_length(subject);
3840 g_hash_table_insert(subject_table, subject, data);
3843 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3845 if (subject == NULL)
3848 subject += subject_get_prefix_length(subject);
3849 g_hash_table_remove(subject_table, subject);
3853 *\brief Check if a string is prefixed with known (combinations)
3854 * of prefixes. The function assumes that each prefix
3855 * is terminated by zero or exactly _one_ space.
3857 *\param str String to check for a prefixes
3859 *\return int Number of chars in the prefix that should be skipped
3860 * for a "clean" subject line. If no prefix was found, 0
3863 int subject_get_prefix_length(const gchar *subject)
3865 /*!< Array with allowable reply prefixes regexps. */
3866 static const gchar * const prefixes[] = {
3867 "Re\\:", /* "Re:" */
3868 "RE\\:", /* "RE:" (outlook) */
3869 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3870 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3871 "Aw\\:", /* "Aw:" (German) */
3872 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3873 "Res\\:", /* "Res:" (Brazilian Outlook) */
3874 "Fw\\:", /* "Fw:" Forward */
3875 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3876 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3877 "Rif\\:", /* "Rif:" (Italian Outlook) */
3878 "SV\\:", /* "SV" (Norwegian) */
3879 "Sv\\:", /* "Sv" (Norwegian) */
3880 "VS\\:", /* "VS" (Norwegian) */
3881 "Vs\\:", /* "Vs" (Norwegian) */
3882 "AD\\:", /* "AD" (Norwegian) */
3883 "Ad\\:" /* "Ad" (Norwegian) */
3886 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3889 static regex_t regex;
3890 static gboolean init_;
3892 if (!subject) return 0;
3893 if (!*subject) return 0;
3896 GString *s = g_string_new("");
3898 for (n = 0; n < PREFIXES; n++)
3899 /* Terminate each prefix regexpression by a
3900 * "\ ?" (zero or ONE space), and OR them */
3901 g_string_append_printf(s, "(%s\\ ?)%s",
3906 g_string_prepend(s, "(");
3907 g_string_append(s, ")+"); /* match at least once */
3908 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3911 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3912 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3913 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3914 debug_print("Error compiling regexp %s\n", s->str);
3915 g_string_free(s, TRUE);
3919 g_string_free(s, TRUE);
3923 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3929 guint g_stricase_hash(gconstpointer gptr)
3931 guint hash_result = 0;
3934 for (str = gptr; str && *str; str++) {
3935 hash_result += toupper(*str);
3941 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3943 const char *str1 = gptr1;
3944 const char *str2 = gptr2;
3946 return !strcasecmp(str1, str2);
3949 gint g_int_compare(gconstpointer a, gconstpointer b)
3951 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3954 gchar *generate_msgid(gchar *buf, gint len)
3963 addr = g_strconcat("@", get_domain_name(), NULL);
3965 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3966 lt->tm_year + 1900, lt->tm_mon + 1,
3967 lt->tm_mday, lt->tm_hour,
3968 lt->tm_min, lt->tm_sec,
3969 (guint) rand(), addr);
3976 quote_cmd_argument()
3978 return a quoted string safely usable in argument of a command.
3980 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3983 gint quote_cmd_argument(gchar * result, guint size,
3993 for(p = path ; * p != '\0' ; p ++) {
3995 if (isalnum((guchar)*p) || (* p == '/')) {
3996 if (remaining > 0) {
4002 result[size - 1] = '\0';
4007 if (remaining >= 2) {
4015 result[size - 1] = '\0';
4020 if (remaining > 0) {
4024 result[size - 1] = '\0';
4038 static void g_node_map_recursive(GNode *node, gpointer data)
4040 GNodeMapData *mapdata = (GNodeMapData *) data;
4042 GNodeMapData newmapdata;
4045 newdata = mapdata->func(node->data, mapdata->data);
4046 if (newdata != NULL) {
4047 newnode = g_node_new(newdata);
4048 g_node_append(mapdata->parent, newnode);
4050 newmapdata.parent = newnode;
4051 newmapdata.func = mapdata->func;
4052 newmapdata.data = mapdata->data;
4054 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4058 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4061 GNodeMapData mapdata;
4063 g_return_val_if_fail(node != NULL, NULL);
4064 g_return_val_if_fail(func != NULL, NULL);
4066 root = g_node_new(func(node->data, data));
4068 mapdata.parent = root;
4069 mapdata.func = func;
4070 mapdata.data = data;
4072 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4077 #define HEX_TO_INT(val, hex) \
4081 if ('0' <= c && c <= '9') { \
4083 } else if ('a' <= c && c <= 'f') { \
4084 val = c - 'a' + 10; \
4085 } else if ('A' <= c && c <= 'F') { \
4086 val = c - 'A' + 10; \
4092 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4099 if (hi == -1 || lo == -1)
4102 *out = (hi << 4) + lo;
4106 #define INT_TO_HEX(hex, val) \
4109 hex = '0' + (val); \
4111 hex = 'A' + (val) - 10; \
4114 void get_hex_str(gchar *out, guchar ch)
4118 INT_TO_HEX(hex, ch >> 4);
4120 INT_TO_HEX(hex, ch & 0x0f);
4126 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4128 #define G_PRINT_REF g_print
4132 *\brief Register ref counted pointer. It is based on GBoxed, so should
4133 * work with anything that uses the GType system. The semantics
4134 * are similar to a C++ auto pointer, with the exception that
4135 * C doesn't have automatic closure (calling destructors) when
4136 * exiting a block scope.
4137 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4138 * function directly.
4140 *\return GType A GType type.
4142 GType g_auto_pointer_register(void)
4144 static GType auto_pointer_type;
4145 if (!auto_pointer_type)
4147 g_boxed_type_register_static
4148 ("G_TYPE_AUTO_POINTER",
4149 (GBoxedCopyFunc) g_auto_pointer_copy,
4150 (GBoxedFreeFunc) g_auto_pointer_free);
4151 return auto_pointer_type;
4155 *\brief Structure with g_new() allocated pointer guarded by the
4158 typedef struct AutoPointerRef {
4159 void (*free) (gpointer);
4165 *\brief The auto pointer opaque structure that references the
4166 * pointer guard block.
4168 typedef struct AutoPointer {
4169 AutoPointerRef *ref;
4170 gpointer ptr; /*!< access to protected pointer */
4174 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4178 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4179 * ... when assigning, copying and freeing storage elements
4181 * gtk_list_store_new(N_S_COLUMNS,
4182 * G_TYPE_AUTO_POINTER,
4186 * Template *precious_data = g_new0(Template, 1);
4187 * g_pointer protect = g_auto_pointer_new(precious_data);
4189 * gtk_list_store_set(container, &iter,
4193 * ... the gtk_list_store has copied the pointer and
4194 * ... incremented its reference count, we should free
4195 * ... the auto pointer (in C++ a destructor would do
4196 * ... this for us when leaving block scope)
4198 * g_auto_pointer_free(protect);
4200 * ... gtk_list_store_set() now manages the data. When
4201 * ... *explicitly* requesting a pointer from the list
4202 * ... store, don't forget you get a copy that should be
4203 * ... freed with g_auto_pointer_free() eventually.
4207 *\param pointer Pointer to be guarded.
4209 *\return GAuto * Pointer that should be used in containers with
4212 GAuto *g_auto_pointer_new(gpointer p)
4214 AutoPointerRef *ref;
4220 ref = g_new0(AutoPointerRef, 1);
4221 ptr = g_new0(AutoPointer, 1);
4231 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4237 *\brief Allocate an autopointer using the passed \a free function to
4238 * free the guarded pointer
4240 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4247 aptr = g_auto_pointer_new(p);
4248 aptr->ref->free = free_;
4252 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4254 if (auto_ptr == NULL)
4256 return ((AutoPointer *) auto_ptr)->ptr;
4260 *\brief Copies an auto pointer by. It's mostly not necessary
4261 * to call this function directly, unless you copy/assign
4262 * the guarded pointer.
4264 *\param auto_ptr Auto pointer returned by previous call to
4265 * g_auto_pointer_new_XXX()
4267 *\return gpointer An auto pointer
4269 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4272 AutoPointerRef *ref;
4275 if (auto_ptr == NULL)
4280 newp = g_new0(AutoPointer, 1);
4283 newp->ptr = ref->pointer;
4287 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4293 *\brief Free an auto pointer
4295 void g_auto_pointer_free(GAuto *auto_ptr)
4298 AutoPointerRef *ref;
4300 if (auto_ptr == NULL)
4306 if (--(ref->cnt) == 0) {
4308 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4310 ref->free(ref->pointer);
4315 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4320 void replace_returns(gchar *str)
4325 while (strstr(str, "\n")) {
4326 *strstr(str, "\n") = ' ';
4328 while (strstr(str, "\r")) {
4329 *strstr(str, "\r") = ' ';
4333 /* get_uri_part() - retrieves a URI starting from scanpos.
4334 Returns TRUE if succesful */
4335 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4336 const gchar **bp, const gchar **ep, gboolean hdr)
4339 gint parenthese_cnt = 0;
4341 g_return_val_if_fail(start != NULL, FALSE);
4342 g_return_val_if_fail(scanpos != NULL, FALSE);
4343 g_return_val_if_fail(bp != NULL, FALSE);
4344 g_return_val_if_fail(ep != NULL, FALSE);
4348 /* find end point of URI */
4349 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4350 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4351 !IS_ASCII(*(const guchar *)ep_) ||
4352 strchr("[]{}<>\"", *ep_)) {
4354 } else if (strchr("(", *ep_)) {
4356 } else if (strchr(")", *ep_)) {
4357 if (parenthese_cnt > 0)
4364 /* no punctuation at end of string */
4366 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4367 * should pass some URI type to this function and decide on that whether
4368 * to perform punctuation stripping */
4370 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4372 for (; ep_ - 1 > scanpos + 1 &&
4373 IS_REAL_PUNCT(*(ep_ - 1));
4377 #undef IS_REAL_PUNCT
4384 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4386 while (bp && *bp && g_ascii_isspace(*bp))
4388 return g_strndup(bp, ep - bp);
4391 /* valid mail address characters */
4392 #define IS_RFC822_CHAR(ch) \
4396 !g_ascii_isspace(ch) && \
4397 !strchr("(),;<>\"", (ch)))
4399 /* alphabet and number within 7bit ASCII */
4400 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4401 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4403 static GHashTable *create_domain_tab(void)
4405 static const gchar *toplvl_domains [] = {
4407 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4408 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4409 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4410 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4411 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4412 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4413 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4414 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4415 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4416 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4417 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4418 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4419 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4420 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4421 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4422 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4423 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4424 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4425 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4426 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4427 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4428 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4429 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4430 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4431 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4432 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4435 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4437 g_return_val_if_fail(htab, NULL);
4438 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4439 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4443 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4445 const gint MAX_LVL_DOM_NAME_LEN = 6;
4446 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4447 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4450 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4453 for (p = buf; p < m && first < last; *p++ = *first++)
4457 return g_hash_table_lookup(tab, buf) != NULL;
4460 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4461 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4462 const gchar **bp, const gchar **ep, gboolean hdr)
4464 /* more complex than the uri part because we need to scan back and forward starting from
4465 * the scan position. */
4466 gboolean result = FALSE;
4467 const gchar *bp_ = NULL;
4468 const gchar *ep_ = NULL;
4469 static GHashTable *dom_tab;
4470 const gchar *last_dot = NULL;
4471 const gchar *prelast_dot = NULL;
4472 const gchar *last_tld_char = NULL;
4474 /* the informative part of the email address (describing the name
4475 * of the email address owner) may contain quoted parts. the
4476 * closure stack stores the last encountered quotes. */
4477 gchar closure_stack[128];
4478 gchar *ptr = closure_stack;
4480 g_return_val_if_fail(start != NULL, FALSE);
4481 g_return_val_if_fail(scanpos != NULL, FALSE);
4482 g_return_val_if_fail(bp != NULL, FALSE);
4483 g_return_val_if_fail(ep != NULL, FALSE);
4486 const gchar *start_quote = NULL;
4487 const gchar *end_quote = NULL;
4489 /* go to the real start */
4490 if (start[0] == ',')
4492 if (start[0] == ';')
4494 while (start[0] == '\n' || start[0] == '\r')
4496 while (start[0] == ' ' || start[0] == '\t')
4501 /* check if there are quotes (to skip , in them) */
4502 if (*start == '"') {
4503 start_quote = start;
4505 end_quote = strstr(start, "\"");
4511 /* skip anything between quotes */
4512 if (start_quote && end_quote) {
4517 /* find end (either , or ; or end of line) */
4518 if (strstr(start, ",") && strstr(start, ";"))
4519 *ep = strstr(start,",") < strstr(start, ";")
4520 ? strstr(start, ",") : strstr(start, ";");
4521 else if (strstr(start, ","))
4522 *ep = strstr(start, ",");
4523 else if (strstr(start, ";"))
4524 *ep = strstr(start, ";");
4526 *ep = start+strlen(start);
4528 /* go back to real start */
4529 if (start_quote && end_quote) {
4530 start = start_quote;
4533 /* check there's still an @ in that, or search
4534 * further if possible */
4535 if (strstr(start, "@") && strstr(start, "@") < *ep)
4537 else if (*ep < start+strlen(start)) {
4540 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4548 dom_tab = create_domain_tab();
4549 g_return_val_if_fail(dom_tab, FALSE);
4551 /* scan start of address */
4552 for (bp_ = scanpos - 1;
4553 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4556 /* TODO: should start with an alnum? */
4558 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4561 if (bp_ != scanpos) {
4562 /* scan end of address */
4563 for (ep_ = scanpos + 1;
4564 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4566 prelast_dot = last_dot;
4568 if (*(last_dot + 1) == '.') {
4569 if (prelast_dot == NULL)
4571 last_dot = prelast_dot;
4576 /* TODO: really should terminate with an alnum? */
4577 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4582 if (last_dot == NULL)
4584 if (last_dot >= ep_)
4585 last_dot = prelast_dot;
4586 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4590 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4591 if (*last_tld_char == '?')
4594 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4601 if (!result) return FALSE;
4603 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4604 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4605 && IS_RFC822_CHAR(*(ep_ + 3))) {
4606 /* this informative part with an @ in it is
4607 * followed by the email address */
4610 /* go to matching '>' (or next non-rfc822 char, like \n) */
4611 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4614 /* include the bracket */
4615 if (*ep_ == '>') ep_++;
4617 /* include the leading quote */
4625 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4626 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4629 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4630 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4633 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4634 #define IN_STACK() (ptr > closure_stack)
4635 /* has underrun check */
4636 #define POP_STACK() if(IN_STACK()) --ptr
4637 /* has overrun check */
4638 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4639 /* has underrun check */
4640 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4644 /* scan for the informative part. */
4645 for (bp_ -= 2; bp_ >= start; bp_--) {
4646 /* if closure on the stack keep scanning */
4647 if (PEEK_STACK() == *bp_) {
4651 if (*bp_ == '\'' || *bp_ == '"') {
4656 /* if nothing in the closure stack, do the special conditions
4657 * the following if..else expression simply checks whether
4658 * a token is acceptable. if not acceptable, the clause
4659 * should terminate the loop with a 'break' */
4660 if (!PEEK_STACK()) {
4662 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4663 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4664 /* hyphens are allowed, but only in
4666 } else if (strchr(" \"'", *bp_)) {
4667 /* but anything not being a punctiation
4670 break; /* anything else is rejected */
4677 /* scan forward (should start with an alnum) */
4678 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4694 #undef IS_ASCII_ALNUM
4695 #undef IS_RFC822_CHAR
4697 gchar *make_email_string(const gchar *bp, const gchar *ep)
4699 /* returns a mailto: URI; mailto: is also used to detect the
4700 * uri type later on in the button_pressed signal handler */
4704 tmp = g_strndup(bp, ep - bp);
4705 result = g_strconcat("mailto:", tmp, NULL);
4711 gchar *make_http_string(const gchar *bp, const gchar *ep)
4713 /* returns an http: URI; */
4717 while (bp && *bp && g_ascii_isspace(*bp))
4719 tmp = g_strndup(bp, ep - bp);
4720 result = g_strconcat("http://", tmp, NULL);
4726 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4728 FILE *fp = fopen(path, "rb");
4729 gchar buf[BUFFSIZE];
4730 gchar *result = NULL;
4733 while (fgets(buf, sizeof (buf), fp) != NULL) {
4734 gchar **parts = g_strsplit(buf, ";", 3);
4735 gchar *trimmed = parts[0];
4736 while (trimmed[0] == ' ')
4738 while (trimmed[strlen(trimmed)-1] == ' ')
4739 trimmed[strlen(trimmed)-1] = '\0';
4741 if (!strcmp(trimmed, type)) {
4742 gboolean needsterminal = FALSE;
4743 if (parts[2] && strstr(parts[2], "needsterminal")) {
4744 needsterminal = TRUE;
4746 if (parts[2] && strstr(parts[2], "test=")) {
4747 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4748 gchar *testcmd = orig_testcmd;
4749 if (strstr(testcmd,";"))
4750 *(strstr(testcmd,";")) = '\0';
4751 while (testcmd[0] == ' ')
4753 while (testcmd[strlen(testcmd)-1] == '\n')
4754 testcmd[strlen(testcmd)-1] = '\0';
4755 while (testcmd[strlen(testcmd)-1] == '\r')
4756 testcmd[strlen(testcmd)-1] = '\0';
4757 while (testcmd[strlen(testcmd)-1] == ' ')
4758 testcmd[strlen(testcmd)-1] = '\0';
4760 if (strstr(testcmd, "%s")) {
4761 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4762 gint res = system(tmp);
4764 g_free(orig_testcmd);
4771 gint res = system(testcmd);
4772 g_free(orig_testcmd);
4782 while (trimmed[0] == ' ')
4784 while (trimmed[strlen(trimmed)-1] == '\n')
4785 trimmed[strlen(trimmed)-1] = '\0';
4786 while (trimmed[strlen(trimmed)-1] == '\r')
4787 trimmed[strlen(trimmed)-1] = '\0';
4788 while (trimmed[strlen(trimmed)-1] == ' ')
4789 trimmed[strlen(trimmed)-1] = '\0';
4790 result = g_strdup(trimmed);
4793 /* if there are no single quotes around %s, add them.
4794 * '.*%s.*' is ok, as in display 'png:%s'
4796 if (strstr(result, "%s")
4797 && !(strstr(result, "'") < strstr(result,"%s") &&
4798 strstr(strstr(result,"%s"), "'"))) {
4799 gchar *start = g_strdup(result);
4800 gchar *end = g_strdup(strstr(result, "%s")+2);
4802 *strstr(start, "%s") = '\0';
4803 tmp = g_strconcat(start,"'%s'",end, NULL);
4809 if (needsterminal) {
4810 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4821 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4823 gchar *result = NULL;
4827 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4828 result = mailcap_get_command_in_file(path, type, file_to_open);
4832 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4836 void mailcap_update_default(const gchar *type, const gchar *command)
4838 gchar *path = NULL, *outpath = NULL;
4839 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4840 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4841 FILE *fp = fopen(path, "rb");
4842 FILE *outfp = fopen(outpath, "wb");
4843 gchar buf[BUFFSIZE];
4856 while (fgets(buf, sizeof (buf), fp) != NULL) {
4857 gchar **parts = g_strsplit(buf, ";", 3);
4858 gchar *trimmed = parts[0];
4859 while (trimmed[0] == ' ')
4861 while (trimmed[strlen(trimmed)-1] == ' ')
4862 trimmed[strlen(trimmed)-1] = '\0';
4864 if (!strcmp(trimmed, type)) {
4873 fprintf(outfp, "%s; %s\n", type, command);
4876 g_rename(outpath, path);
4879 gint copy_dir(const gchar *src, const gchar *dst)
4884 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4885 g_warning("failed to open directory: %s\n", src);
4889 if (make_dir(dst) < 0)
4892 while ((name = g_dir_read_name(dir)) != NULL) {
4893 gchar *old_file, *new_file;
4894 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4895 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4896 debug_print("copying: %s -> %s\n", old_file, new_file);
4897 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4898 gint r = copy_file(old_file, new_file, TRUE);
4901 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4902 gint r = copy_dir(old_file, new_file);
4910 /* crude test to see if a file is an email. */
4911 gboolean file_is_email (const gchar *filename)
4917 if (filename == NULL)
4919 if ((fp = g_fopen(filename, "rb")) == NULL)
4921 while (i < 60 && score < 3
4922 && fgets(buffer, sizeof (buffer), fp) > 0) {
4923 if (!strncmp(buffer, "From:", strlen("From:")))
4925 if (!strncmp(buffer, "To:", strlen("To:")))
4927 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4932 return (score >= 3);
4935 gboolean sc_g_list_bigger(GList *list, gint max)
4939 while (cur && i <= max+1) {
4946 gboolean sc_g_slist_bigger(GSList *list, gint max)
4950 while (cur && i <= max+1) {