2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail 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>
35 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
43 #include <sys/types.h>
45 # include <sys/wait.h>
52 #include <sys/utsname.h>
63 #include <osso-browser-interface.h>
68 #include "../codeconv.h"
72 static gboolean debug_mode = FALSE;
74 static GSList *tempfiles=NULL;
78 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
79 gint g_chdir(const gchar *path)
82 if (G_WIN32_HAVE_WIDECHAR_API()) {
87 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
93 retval = _wchdir(wpath);
105 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
106 if (cp_path == NULL) {
111 retval = chdir(cp_path);
124 gint g_chmod(const gchar *path, gint mode)
127 if (G_WIN32_HAVE_WIDECHAR_API()) {
132 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
138 retval = _wchmod(wpath, mode);
150 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
151 if (cp_path == NULL) {
156 retval = chmod(cp_path, mode);
165 return chmod(path, mode);
168 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
172 gint mkstemp_name(const gchar *template, gchar **name_used)
174 static gulong count=0; /* W32-_mktemp only supports up to 27
178 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
179 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
180 (S_IRUSR | S_IWUSR));
182 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
184 perror(g_strdup_printf("cant create %s",*name_used));
190 #endif /* G_OS_WIN32 */
193 gint mkstemp(const gchar *template)
196 gint res = mkstemp_name(template, &dummyname);
200 #endif /* G_OS_WIN32 */
202 void list_free_strings(GList *list)
204 list = g_list_first(list);
206 while (list != NULL) {
212 void slist_free_strings(GSList *list)
214 while (list != NULL) {
220 GSList *slist_concat_unique (GSList *first, GSList *second)
228 } else if (second == NULL)
231 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
232 if (g_slist_find(ret, tmp->data) == NULL)
233 ret = g_slist_prepend(ret, tmp->data);
238 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
243 void hash_free_strings(GHashTable *table)
245 g_hash_table_foreach(table, hash_free_strings_func, NULL);
248 static void hash_free_value_mem_func(gpointer key, gpointer value,
254 void hash_free_value_mem(GHashTable *table)
256 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
259 gint str_case_equal(gconstpointer v, gconstpointer v2)
261 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
264 guint str_case_hash(gconstpointer key)
266 const gchar *p = key;
270 h = g_ascii_tolower(h);
271 for (p += 1; *p != '\0'; p++)
272 h = (h << 5) - h + g_ascii_tolower(*p);
278 void ptr_array_free_strings(GPtrArray *array)
283 g_return_if_fail(array != NULL);
285 for (i = 0; i < array->len; i++) {
286 str = g_ptr_array_index(array, i);
291 gboolean str_find(const gchar *haystack, const gchar *needle)
293 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
296 gboolean str_case_find(const gchar *haystack, const gchar *needle)
298 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
301 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
303 return strcmp(haystack, needle) == 0;
306 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
308 return g_ascii_strcasecmp(haystack, needle) == 0;
311 gint to_number(const gchar *nstr)
313 register const gchar *p;
315 if (*nstr == '\0') return -1;
317 for (p = nstr; *p != '\0'; p++)
318 if (!g_ascii_isdigit(*p)) return -1;
323 /* convert integer into string,
324 nstr must be not lower than 11 characters length */
325 gchar *itos_buf(gchar *nstr, gint n)
327 g_snprintf(nstr, 11, "%d", n);
331 /* convert integer into string */
334 static gchar nstr[11];
336 return itos_buf(nstr, n);
339 #define divide(num,divisor,i,d) \
341 i = num >> divisor; \
342 d = num & ((1<<divisor)-1); \
343 d = (d*100) >> divisor; \
346 gchar *to_human_readable(off_t size)
348 static gchar str[14];
349 static gchar *b_format = NULL, *kb_format = NULL,
350 *mb_format = NULL, *gb_format = NULL;
351 register int t = 0, r = 0;
352 if (b_format == NULL) {
354 kb_format = _("%d.%02dKB");
355 mb_format = _("%d.%02dMB");
356 gb_format = _("%.2fGB");
359 if (size < (off_t)1024) {
360 g_snprintf(str, sizeof(str), b_format, (gint)size);
362 } else if (size >> 10 < (off_t)1024) {
363 divide(size, 10, t, r);
364 g_snprintf(str, sizeof(str), kb_format, t, r);
366 } else if (size >> 20 < (off_t)1024) {
367 divide(size, 20, t, r);
368 g_snprintf(str, sizeof(str), mb_format, t, r);
371 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
376 /* strcmp with NULL-checking */
377 gint strcmp2(const gchar *s1, const gchar *s2)
379 if (s1 == NULL || s2 == NULL)
382 return strcmp(s1, s2);
384 /* strstr with NULL-checking */
385 gchar *strstr2(const gchar *s1, const gchar *s2)
387 if (s1 == NULL || s2 == NULL)
390 return strstr(s1, s2);
393 gint path_cmp(const gchar *s1, const gchar *s2)
398 gchar *s1buf, *s2buf;
401 if (s1 == NULL || s2 == NULL) return -1;
402 if (*s1 == '\0' || *s2 == '\0') return -1;
405 s1buf = g_strdup (s1);
406 s2buf = g_strdup (s2);
407 subst_char (s1buf, '/', G_DIR_SEPARATOR);
408 subst_char (s2buf, '/', G_DIR_SEPARATOR);
411 #endif /* !G_OS_WIN32 */
416 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
417 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
419 rc = strncmp(s1, s2, MAX(len1, len2));
423 #endif /* !G_OS_WIN32 */
427 /* remove trailing return code */
428 gchar *strretchomp(gchar *str)
432 if (!*str) return str;
434 for (s = str + strlen(str) - 1;
435 s >= str && (*s == '\n' || *s == '\r');
442 /* remove trailing character */
443 gchar *strtailchomp(gchar *str, gchar tail_char)
447 if (!*str) return str;
448 if (tail_char == '\0') return str;
450 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
456 /* remove CR (carriage return) */
457 gchar *strcrchomp(gchar *str)
461 if (!*str) return str;
463 s = str + strlen(str) - 1;
464 if (*s == '\n' && s > str && *(s - 1) == '\r') {
472 void file_strip_crs(const gchar *file)
474 FILE *fp = NULL, *outfp = NULL;
476 gchar *out = get_tmp_file();
480 fp = fopen(file, "rb");
484 outfp = fopen(out, "wb");
490 while (fgets(buf, sizeof (buf), fp) != NULL) {
497 rename_force(out, file);
502 /* Similar to `strstr' but this function ignores the case of both strings. */
503 gchar *strcasestr(const gchar *haystack, const gchar *needle)
505 register size_t haystack_len, needle_len;
507 haystack_len = strlen(haystack);
508 needle_len = strlen(needle);
510 if (haystack_len < needle_len || needle_len == 0)
513 while (haystack_len >= needle_len) {
514 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
515 return (gchar *)haystack;
525 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
526 gconstpointer needle, size_t needlelen)
528 const gchar *haystack_ = (const gchar *)haystack;
529 const gchar *needle_ = (const gchar *)needle;
530 const gchar *haystack_cur = (const gchar *)haystack;
533 return memchr(haystack_, *needle_, haystacklen);
535 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
537 if (haystacklen - (haystack_cur - haystack_) < needlelen)
539 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
540 return (gpointer)haystack_cur;
548 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
549 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
551 register const gchar *s = src;
552 register gchar *d = dest;
562 /* Examine if next block is non-ASCII string */
563 gboolean is_next_nonascii(const gchar *s)
567 /* skip head space */
568 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
570 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
571 if (*(guchar *)p > 127 || *(guchar *)p < 32)
578 gint get_next_word_len(const gchar *s)
582 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
588 /* compare subjects */
589 gint subject_compare(const gchar *s1, const gchar *s2)
593 if (!s1 || !s2) return -1;
594 if (!*s1 || !*s2) return -1;
596 Xstrdup_a(str1, s1, return -1);
597 Xstrdup_a(str2, s2, return -1);
599 trim_subject_for_compare(str1);
600 trim_subject_for_compare(str2);
602 if (!*str1 || !*str2) return -1;
604 return strcmp(str1, str2);
607 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
611 if (!s1 || !s2) return -1;
613 Xstrdup_a(str1, s1, return -1);
614 Xstrdup_a(str2, s2, return -1);
616 trim_subject_for_sort(str1);
617 trim_subject_for_sort(str2);
619 return g_utf8_collate(str1, str2);
622 void trim_subject_for_compare(gchar *str)
626 eliminate_parenthesis(str, '[', ']');
627 eliminate_parenthesis(str, '(', ')');
630 srcp = str + subject_get_prefix_length(str);
632 memmove(str, srcp, strlen(srcp) + 1);
635 void trim_subject_for_sort(gchar *str)
641 srcp = str + subject_get_prefix_length(str);
643 memmove(str, srcp, strlen(srcp) + 1);
646 void trim_subject(gchar *str)
648 register gchar *srcp;
654 srcp = str + subject_get_prefix_length(str);
659 } else if (*srcp == '(') {
671 else if (*srcp == cl)
678 while (g_ascii_isspace(*srcp)) srcp++;
679 memmove(str, srcp, strlen(srcp) + 1);
682 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
684 register gchar *srcp, *destp;
689 while ((destp = strchr(destp, op))) {
695 else if (*srcp == cl)
701 while (g_ascii_isspace(*srcp)) srcp++;
702 memmove(destp, srcp, strlen(srcp) + 1);
706 void extract_parenthesis(gchar *str, gchar op, gchar cl)
708 register gchar *srcp, *destp;
713 while ((srcp = strchr(destp, op))) {
716 memmove(destp, srcp + 1, strlen(srcp));
721 else if (*destp == cl)
733 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
736 register gchar *srcp, *destp;
738 gboolean in_quote = FALSE;
742 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
745 memmove(destp, srcp + 1, strlen(srcp));
748 if (*destp == op && !in_quote)
750 else if (*destp == cl && !in_quote)
752 else if (*destp == quote_chr)
764 void eliminate_quote(gchar *str, gchar quote_chr)
766 register gchar *srcp, *destp;
770 while ((destp = strchr(destp, quote_chr))) {
771 if ((srcp = strchr(destp + 1, quote_chr))) {
773 while (g_ascii_isspace(*srcp)) srcp++;
774 memmove(destp, srcp, strlen(srcp) + 1);
782 void extract_quote(gchar *str, gchar quote_chr)
786 if ((str = strchr(str, quote_chr))) {
788 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
789 memmove(p - 1, p, strlen(p) + 1);
794 memmove(str, str + 1, p - str);
799 void eliminate_address_comment(gchar *str)
801 register gchar *srcp, *destp;
806 while ((destp = strchr(destp, '"'))) {
807 if ((srcp = strchr(destp + 1, '"'))) {
812 while (g_ascii_isspace(*srcp)) srcp++;
813 memmove(destp, srcp, strlen(srcp) + 1);
823 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
829 else if (*srcp == ')')
835 while (g_ascii_isspace(*srcp)) srcp++;
836 memmove(destp, srcp, strlen(srcp) + 1);
840 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
842 gboolean in_quote = FALSE;
845 if (*str == c && !in_quote)
847 if (*str == quote_chr)
855 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
857 gboolean in_quote = FALSE;
860 p = str + strlen(str) - 1;
862 if (*p == c && !in_quote)
872 void extract_address(gchar *str)
874 eliminate_address_comment(str);
875 if (strchr_with_skip_quote(str, '"', '<'))
876 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
880 void extract_list_id_str(gchar *str)
882 if (strchr_with_skip_quote(str, '"', '<'))
883 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
887 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
892 if (!str) return addr_list;
894 Xstrdup_a(work, str, return addr_list);
897 eliminate_address_comment(work);
900 while (workp && *workp) {
903 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
909 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
910 extract_parenthesis_with_skip_quote
911 (workp, '"', '<', '>');
915 addr_list = g_slist_append(addr_list, g_strdup(workp));
923 GSList *address_list_append(GSList *addr_list, const gchar *str)
925 return address_list_append_real(addr_list, str, TRUE);
928 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
930 return address_list_append_real(addr_list, str, FALSE);
933 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
937 if (!str) return msgid_list;
940 while (strp && *strp) {
941 const gchar *start, *end;
944 if ((start = strchr(strp, '<')) != NULL) {
945 end = strchr(start + 1, '>');
950 msgid = g_strndup(start + 1, end - start - 1);
953 msgid_list = g_slist_prepend(msgid_list, msgid);
963 GSList *references_list_append(GSList *msgid_list, const gchar *str)
967 list = references_list_prepend(NULL, str);
968 list = g_slist_reverse(list);
969 msgid_list = g_slist_concat(msgid_list, list);
974 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
979 if (!str) return group_list;
981 Xstrdup_a(work, str, return group_list);
985 while (workp && *workp) {
988 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
996 group_list = g_slist_append(group_list,
1005 GList *add_history(GList *list, const gchar *str)
1009 g_return_val_if_fail(str != NULL, list);
1011 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1014 list = g_list_remove(list, old->data);
1015 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1018 last = g_list_last(list);
1021 list = g_list_remove(list, last->data);
1025 list = g_list_prepend(list, g_strdup(str));
1030 void remove_return(gchar *str)
1032 register gchar *p = str;
1035 if (*p == '\n' || *p == '\r')
1036 memmove(p, p + 1, strlen(p));
1042 void remove_space(gchar *str)
1044 register gchar *p = str;
1049 while (g_ascii_isspace(*(p + spc)))
1052 memmove(p, p + spc, strlen(p + spc) + 1);
1058 void unfold_line(gchar *str)
1060 register gchar *p = str;
1064 if (*p == '\n' || *p == '\r') {
1067 while (g_ascii_isspace(*(p + spc)))
1070 memmove(p, p + spc, strlen(p + spc) + 1);
1076 void subst_char(gchar *str, gchar orig, gchar subst)
1078 register gchar *p = str;
1087 void subst_chars(gchar *str, gchar *orig, gchar subst)
1089 register gchar *p = str;
1092 if (strchr(orig, *p) != NULL)
1098 void subst_for_filename(gchar *str)
1103 subst_chars(str, "\t\r\n\\/*:", '_');
1105 subst_chars(str, "\t\r\n\\/*", '_');
1109 void subst_for_shellsafe_filename(gchar *str)
1113 subst_for_filename(str);
1114 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1117 gboolean is_header_line(const gchar *str)
1119 if (str[0] == ':') return FALSE;
1121 while (*str != '\0' && *str != ' ') {
1130 gboolean is_ascii_str(const gchar *str)
1132 const guchar *p = (const guchar *)str;
1134 while (*p != '\0') {
1135 if (*p != '\t' && *p != ' ' &&
1136 *p != '\r' && *p != '\n' &&
1137 (*p < 32 || *p >= 127))
1145 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1147 const gchar *first_pos;
1148 const gchar *last_pos;
1149 const gchar *p = str;
1150 gint quote_level = -1;
1152 /* speed up line processing by only searching to the last '>' */
1153 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1154 /* skip a line if it contains a '<' before the initial '>' */
1155 if (memchr(str, '<', first_pos - str) != NULL)
1157 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1161 while (p <= last_pos) {
1162 while (p < last_pos) {
1163 if (g_ascii_isspace(*p))
1169 if (strchr(quote_chars, *p))
1171 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1172 /* any characters are allowed except '-' and space */
1174 && !strchr(quote_chars, *p)
1175 && !g_ascii_isspace(*p)
1178 if (strchr(quote_chars, *p))
1190 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1192 const gchar *p = str, *q;
1193 gint cur_line = 0, len;
1195 while ((q = strchr(p, '\n')) != NULL) {
1197 if (len > max_chars) {
1207 if (len > max_chars) {
1216 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1218 gchar * position = NULL;
1219 gchar * tmp_pos = NULL;
1222 if (quote_chars == NULL)
1225 for (i = 0; i < strlen(quote_chars); i++) {
1226 tmp_pos = strchr (str, quote_chars[i]);
1228 || (tmp_pos != NULL && position >= tmp_pos) )
1234 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1236 gchar * position = NULL;
1237 gchar * tmp_pos = NULL;
1240 if (quote_chars == NULL)
1243 for (i = 0; i < strlen(quote_chars); i++) {
1244 tmp_pos = strrchr (str, quote_chars[i]);
1246 || (tmp_pos != NULL && position <= tmp_pos) )
1252 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1254 register guint haystack_len, needle_len;
1255 gboolean in_squote = FALSE, in_dquote = FALSE;
1257 haystack_len = strlen(haystack);
1258 needle_len = strlen(needle);
1260 if (haystack_len < needle_len || needle_len == 0)
1263 while (haystack_len >= needle_len) {
1264 if (!in_squote && !in_dquote &&
1265 !strncmp(haystack, needle, needle_len))
1266 return (gchar *)haystack;
1268 /* 'foo"bar"' -> foo"bar"
1269 "foo'bar'" -> foo'bar' */
1270 if (*haystack == '\'') {
1273 else if (!in_dquote)
1275 } else if (*haystack == '\"') {
1278 else if (!in_squote)
1289 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1292 gchar quote_chr = '"';
1294 gboolean in_quote = FALSE;
1298 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1302 if (*p == op && !in_quote)
1304 else if (*p == cl && !in_quote)
1306 else if (*p == quote_chr)
1319 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1322 GSList *string_list = NULL, *slist;
1324 const gchar *s_op, *s_cl;
1327 g_return_val_if_fail(str != NULL, NULL);
1330 max_tokens = G_MAXINT;
1332 s_op = strchr_with_skip_quote(str, '"', op);
1333 if (!s_op) return NULL;
1335 s_cl = strchr_parenthesis_close(str, op, cl);
1343 new_string = g_new(gchar, len + 1);
1344 strncpy(new_string, str, len);
1345 new_string[len] = 0;
1346 string_list = g_slist_prepend(string_list, new_string);
1350 while (*str && g_ascii_isspace(*str)) str++;
1352 string_list = g_slist_prepend(string_list,
1355 s_op = strchr_with_skip_quote(str, '"', op);
1356 if (!--max_tokens || !s_op) break;
1360 s_cl = strchr_parenthesis_close(str, op, cl);
1361 } while (--max_tokens && s_cl);
1364 str_array = g_new(gchar*, n);
1368 str_array[i--] = NULL;
1369 for (slist = string_list; slist; slist = slist->next)
1370 str_array[i--] = slist->data;
1372 g_slist_free(string_list);
1377 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1380 GSList *string_list = NULL, *slist;
1381 gchar **str_array, *s, *new_str;
1382 guint i, n = 1, len;
1384 g_return_val_if_fail(str != NULL, NULL);
1385 g_return_val_if_fail(delim != NULL, NULL);
1388 max_tokens = G_MAXINT;
1390 s = strstr_with_skip_quote(str, delim);
1392 guint delimiter_len = strlen(delim);
1396 new_str = g_strndup(str, len);
1398 if (new_str[0] == '\'' || new_str[0] == '\"') {
1399 if (new_str[len - 1] == new_str[0]) {
1400 new_str[len - 1] = '\0';
1401 memmove(new_str, new_str + 1, len - 1);
1404 string_list = g_slist_prepend(string_list, new_str);
1406 str = s + delimiter_len;
1407 s = strstr_with_skip_quote(str, delim);
1408 } while (--max_tokens && s);
1412 new_str = g_strdup(str);
1413 if (new_str[0] == '\'' || new_str[0] == '\"') {
1415 if (new_str[len - 1] == new_str[0]) {
1416 new_str[len - 1] = '\0';
1417 memmove(new_str, new_str + 1, len - 1);
1420 string_list = g_slist_prepend(string_list, new_str);
1424 str_array = g_new(gchar*, n);
1428 str_array[i--] = NULL;
1429 for (slist = string_list; slist; slist = slist->next)
1430 str_array[i--] = slist->data;
1432 g_slist_free(string_list);
1437 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1439 gchar *abbrev_group;
1441 const gchar *p = group;
1444 g_return_val_if_fail(group != NULL, NULL);
1446 last = group + strlen(group);
1447 abbrev_group = ap = g_malloc(strlen(group) + 1);
1452 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1454 while (*p != '.') p++;
1457 return abbrev_group;
1462 return abbrev_group;
1465 gchar *trim_string(const gchar *str, gint len)
1467 const gchar *p = str;
1472 if (!str) return NULL;
1473 if (strlen(str) <= len)
1474 return g_strdup(str);
1475 if (g_utf8_validate(str, -1, NULL) == FALSE)
1476 return g_strdup(str);
1478 while (*p != '\0') {
1479 mb_len = g_utf8_skip[*(guchar *)p];
1482 else if (new_len + mb_len > len)
1489 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1490 return g_strconcat(new_str, "...", NULL);
1493 GList *uri_list_extract_filenames(const gchar *uri_list)
1495 GList *result = NULL;
1497 gchar *escaped_utf8uri;
1503 while (g_ascii_isspace(*p)) p++;
1504 if (!strncmp(p, "file:", 5)) {
1507 while (*q && *q != '\n' && *q != '\r') q++;
1510 gchar *file, *locale_file = NULL;
1512 while (q > p && g_ascii_isspace(*q))
1514 Xalloca(escaped_utf8uri, q - p + 2,
1516 Xalloca(file, q - p + 2,
1519 strncpy(escaped_utf8uri, p, q - p + 1);
1520 escaped_utf8uri[q - p + 1] = '\0';
1521 decode_uri(file, escaped_utf8uri);
1523 * g_filename_from_uri() rejects escaped/locale encoded uri
1524 * string which come from Nautilus.
1526 if (g_utf8_validate(file, -1, NULL))
1528 = conv_codeset_strdup(
1531 conv_get_locale_charset_str());
1533 locale_file = g_strdup(file + 5);
1534 result = g_list_append(result, locale_file);
1538 p = strchr(p, '\n');
1545 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1548 static gint axtoi(const gchar *hexstr)
1550 gint hi, lo, result;
1553 if ('0' <= hi && hi <= '9') {
1556 if ('a' <= hi && hi <= 'f') {
1559 if ('A' <= hi && hi <= 'F') {
1564 if ('0' <= lo && lo <= '9') {
1567 if ('a' <= lo && lo <= 'f') {
1570 if ('A' <= lo && lo <= 'F') {
1573 result = lo + (16 * hi);
1577 gboolean is_uri_string(const gchar *str)
1579 while (str && *str && g_ascii_isspace(*str))
1581 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1582 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1583 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1584 g_ascii_strncasecmp(str, "www.", 4) == 0);
1587 gchar *get_uri_path(const gchar *uri)
1589 while (uri && *uri && g_ascii_isspace(*uri))
1591 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1592 return (gchar *)(uri + 7);
1593 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1594 return (gchar *)(uri + 8);
1595 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1596 return (gchar *)(uri + 6);
1598 return (gchar *)uri;
1601 gint get_uri_len(const gchar *str)
1605 if (is_uri_string(str)) {
1606 for (p = str; *p != '\0'; p++) {
1607 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1616 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1617 * plusses, and escape characters are used)
1619 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1621 gchar *dec = decoded_uri;
1622 const gchar *enc = encoded_uri;
1627 if (isxdigit((guchar)enc[0]) &&
1628 isxdigit((guchar)enc[1])) {
1634 if (with_plus && *enc == '+')
1646 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1648 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1651 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1652 gchar **subject, gchar **body, gchar **attach)
1656 const gchar *forbidden_uris[] = { ".gnupg/",
1661 Xstrdup_a(tmp_mailto, mailto, return -1);
1663 if (!strncmp(tmp_mailto, "mailto:", 7))
1666 p = strchr(tmp_mailto, '?');
1673 *to = g_strdup(tmp_mailto);
1676 gchar *field, *value;
1693 if (*value == '\0') continue;
1695 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1696 *cc = g_strdup(value);
1697 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1698 *bcc = g_strdup(value);
1699 } else if (subject && !*subject &&
1700 !g_ascii_strcasecmp(field, "subject")) {
1701 *subject = g_malloc(strlen(value) + 1);
1702 decode_uri(*subject, value);
1703 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1704 *body = g_malloc(strlen(value) + 1);
1705 decode_uri(*body, value);
1706 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1708 *attach = g_malloc(strlen(value) + 1);
1709 decode_uri(*attach, value);
1710 for (; forbidden_uris[i]; i++) {
1711 if (strstr(*attach, forbidden_uris[i])) {
1712 printf("Refusing to attach '%s', potential private data leak\n",
1727 #include <windows.h>
1728 #ifndef CSIDL_APPDATA
1729 #define CSIDL_APPDATA 0x001a
1731 #ifndef CSIDL_LOCAL_APPDATA
1732 #define CSIDL_LOCAL_APPDATA 0x001c
1734 #ifndef CSIDL_FLAG_CREATE
1735 #define CSIDL_FLAG_CREATE 0x8000
1737 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1741 w32_strerror (int w32_errno)
1743 static char strerr[256];
1744 int ec = (int)GetLastError ();
1748 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1749 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1750 strerr, DIM (strerr)-1, NULL);
1754 static __inline__ void *
1755 dlopen (const char * name, int flag)
1757 void * hd = LoadLibrary (name);
1761 static __inline__ void *
1762 dlsym (void * hd, const char * sym)
1766 void * fnc = GetProcAddress (hd, sym);
1775 static __inline__ const char *
1778 return w32_strerror (0);
1782 static __inline__ int
1794 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1796 static int initialized;
1797 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1801 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1807 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1809 handle = dlopen (dllnames[i], RTLD_LAZY);
1812 func = dlsym (handle, "SHGetFolderPathA");
1823 return func (a,b,c,d,e);
1828 /* Returns a static string with the directroy from which the module
1829 has been loaded. Returns an empty string on error. */
1830 static char *w32_get_module_dir(void)
1832 static char *moddir;
1835 char name[MAX_PATH+10];
1838 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1841 p = strrchr (name, '\\');
1847 moddir = g_strdup (name);
1851 #endif /* G_OS_WIN32 */
1853 /* Return a static string with the locale dir. */
1854 const gchar *get_locale_dir(void)
1856 static gchar *loc_dir;
1860 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1861 "\\share\\locale", NULL);
1864 loc_dir = LOCALEDIR;
1870 const gchar *get_home_dir(void)
1873 static char home_dir[MAX_PATH] = "";
1875 if (home_dir[0] == '\0') {
1876 if (w32_shgetfolderpath
1877 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1878 NULL, 0, home_dir) < 0)
1879 strcpy (home_dir, "C:\\Sylpheed");
1883 static const gchar *homeenv = NULL;
1888 if (!homeenv && g_getenv("HOME") != NULL)
1889 homeenv = g_strdup(g_getenv("HOME"));
1891 homeenv = g_get_home_dir();
1897 const gchar *get_rc_dir(void)
1899 static gchar *rc_dir = NULL;
1902 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1908 const gchar *get_mail_base_dir(void)
1911 static gchar *mail_base_dir = NULL;
1914 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917 return mail_base_dir;
1919 return get_home_dir();
1923 const gchar *get_news_cache_dir(void)
1925 static gchar *news_cache_dir = NULL;
1927 if (!news_cache_dir)
1928 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1929 NEWS_CACHE_DIR, NULL);
1931 return news_cache_dir;
1934 const gchar *get_imap_cache_dir(void)
1936 static gchar *imap_cache_dir = NULL;
1938 if (!imap_cache_dir)
1939 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1940 IMAP_CACHE_DIR, NULL);
1942 return imap_cache_dir;
1945 const gchar *get_mbox_cache_dir(void)
1947 static gchar *mbox_cache_dir = NULL;
1949 if (!mbox_cache_dir)
1950 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1951 MBOX_CACHE_DIR, NULL);
1953 return mbox_cache_dir;
1956 const gchar *get_mime_tmp_dir(void)
1958 static gchar *mime_tmp_dir = NULL;
1961 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1962 MIME_TMP_DIR, NULL);
1964 return mime_tmp_dir;
1967 const gchar *get_template_dir(void)
1969 static gchar *template_dir = NULL;
1972 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1973 TEMPLATE_DIR, NULL);
1975 return template_dir;
1978 const gchar *get_header_cache_dir(void)
1980 static gchar *header_dir = NULL;
1983 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1984 HEADER_CACHE_DIR, NULL);
1989 /* Return the default directory for Plugins. */
1990 const gchar *get_plugin_dir(void)
1993 static gchar *plugin_dir = NULL;
1996 plugin_dir = g_strconcat(w32_get_module_dir(),
1997 "\\lib\\claws-mail\\plugins\\",
2001 if (is_dir_exist(PLUGINDIR))
2004 static gchar *plugin_dir = NULL;
2006 plugin_dir = g_strconcat(get_rc_dir(),
2007 G_DIR_SEPARATOR_S, "plugins",
2008 G_DIR_SEPARATOR_S, NULL);
2014 const gchar *get_tmp_dir(void)
2016 static gchar *tmp_dir = NULL;
2019 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2025 gchar *get_tmp_file(void)
2028 static guint32 id = 0;
2030 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2031 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2036 const gchar *get_domain_name(void)
2039 static gchar *domain_name = NULL;
2045 if (uname(&uts) < 0) {
2047 domain_name = "unknown";
2049 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2050 perror("gethostbyname");
2051 domain_name = g_strdup(uts.nodename);
2053 domain_name = g_strdup(hp->h_name);
2057 debug_print("domain name = %s\n", domain_name);
2066 off_t get_file_size(const gchar *file)
2070 if (g_stat(file, &s) < 0) {
2071 FILE_OP_ERROR(file, "stat");
2078 time_t get_file_mtime(const gchar *file)
2082 if (g_stat(file, &s) < 0) {
2083 FILE_OP_ERROR(file, "stat");
2090 off_t get_file_size_as_crlf(const gchar *file)
2094 gchar buf[BUFFSIZE];
2096 if ((fp = g_fopen(file, "rb")) == NULL) {
2097 FILE_OP_ERROR(file, "fopen");
2101 while (fgets(buf, sizeof(buf), fp) != NULL) {
2103 size += strlen(buf) + 2;
2107 FILE_OP_ERROR(file, "fgets");
2116 off_t get_left_file_size(FILE *fp)
2122 if ((pos = ftell(fp)) < 0) {
2126 if (fseek(fp, 0L, SEEK_END) < 0) {
2130 if ((end = ftell(fp)) < 0) {
2135 if (fseek(fp, pos, SEEK_SET) < 0) {
2143 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2150 if (g_stat(file, &s) < 0) {
2151 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2155 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2162 /* Test on whether FILE is a relative file name. This is
2163 * straightforward for Unix but more complex for Windows. */
2164 gboolean is_relative_filename(const gchar *file)
2169 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2170 return FALSE; /* Prefixed with a hostname - this can't
2171 * be a relative name. */
2173 if ( ((*file >= 'a' && *file <= 'z')
2174 || (*file >= 'A' && *file <= 'Z'))
2176 file += 2; /* Skip drive letter. */
2178 return !(*file == '\\' || *file == '/');
2180 return !(*file == G_DIR_SEPARATOR);
2185 gboolean is_dir_exist(const gchar *dir)
2190 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2193 gboolean is_file_entry_exist(const gchar *file)
2198 return g_file_test(file, G_FILE_TEST_EXISTS);
2201 gboolean dirent_is_regular_file(struct dirent *d)
2203 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2204 if (d->d_type == DT_REG)
2206 else if (d->d_type != DT_UNKNOWN)
2210 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2213 gboolean dirent_is_directory(struct dirent *d)
2215 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2216 if (d->d_type == DT_DIR)
2218 else if (d->d_type != DT_UNKNOWN)
2222 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2225 gint change_dir(const gchar *dir)
2227 gchar *prevdir = NULL;
2230 prevdir = g_get_current_dir();
2232 if (g_chdir(dir) < 0) {
2233 FILE_OP_ERROR(dir, "chdir");
2234 if (debug_mode) g_free(prevdir);
2236 } else if (debug_mode) {
2239 cwd = g_get_current_dir();
2240 if (strcmp(prevdir, cwd) != 0)
2241 g_print("current dir: %s\n", cwd);
2249 gint make_dir(const gchar *dir)
2251 if (g_mkdir(dir, S_IRWXU) < 0) {
2252 FILE_OP_ERROR(dir, "mkdir");
2255 if (g_chmod(dir, S_IRWXU) < 0)
2256 FILE_OP_ERROR(dir, "chmod");
2261 gint make_dir_hier(const gchar *dir)
2266 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2267 parent_dir = g_strndup(dir, p - dir);
2268 if (*parent_dir != '\0') {
2269 if (!is_dir_exist(parent_dir)) {
2270 if (make_dir(parent_dir) < 0) {
2279 if (!is_dir_exist(dir)) {
2280 if (make_dir(dir) < 0)
2287 gint remove_all_files(const gchar *dir)
2290 const gchar *dir_name;
2293 prev_dir = g_get_current_dir();
2295 if (g_chdir(dir) < 0) {
2296 FILE_OP_ERROR(dir, "chdir");
2301 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2302 g_warning("failed to open directory: %s\n", dir);
2307 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2308 if (g_unlink(dir_name) < 0)
2309 FILE_OP_ERROR(dir_name, "unlink");
2314 if (g_chdir(prev_dir) < 0) {
2315 FILE_OP_ERROR(prev_dir, "chdir");
2325 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2328 const gchar *dir_name;
2332 prev_dir = g_get_current_dir();
2334 if (g_chdir(dir) < 0) {
2335 FILE_OP_ERROR(dir, "chdir");
2340 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2341 g_warning("failed to open directory: %s\n", dir);
2346 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2347 file_no = to_number(dir_name);
2348 if (file_no > 0 && first <= file_no && file_no <= last) {
2349 if (is_dir_exist(dir_name))
2351 if (g_unlink(dir_name) < 0)
2352 FILE_OP_ERROR(dir_name, "unlink");
2358 if (g_chdir(prev_dir) < 0) {
2359 FILE_OP_ERROR(prev_dir, "chdir");
2369 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2372 const gchar *dir_name;
2376 prev_dir = g_get_current_dir();
2378 if (g_chdir(dir) < 0) {
2379 FILE_OP_ERROR(dir, "chdir");
2384 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2385 FILE_OP_ERROR(dir, "opendir");
2390 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2391 file_no = to_number(dir_name);
2392 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2393 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2394 if (is_dir_exist(dir_name))
2396 if (g_unlink(dir_name) < 0)
2397 FILE_OP_ERROR(dir_name, "unlink");
2403 if (g_chdir(prev_dir) < 0) {
2404 FILE_OP_ERROR(prev_dir, "chdir");
2414 gint remove_all_numbered_files(const gchar *dir)
2416 return remove_numbered_files(dir, 0, UINT_MAX);
2419 gint remove_expired_files(const gchar *dir, guint hours)
2422 const gchar *dir_name;
2426 time_t mtime, now, expire_time;
2428 prev_dir = g_get_current_dir();
2430 if (g_chdir(dir) < 0) {
2431 FILE_OP_ERROR(dir, "chdir");
2436 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2437 g_warning("failed to open directory: %s\n", dir);
2443 expire_time = hours * 60 * 60;
2445 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2446 file_no = to_number(dir_name);
2448 if (g_stat(dir_name, &s) < 0) {
2449 FILE_OP_ERROR(dir_name, "stat");
2452 if (S_ISDIR(s.st_mode))
2454 mtime = MAX(s.st_mtime, s.st_atime);
2455 if (now - mtime > expire_time) {
2456 if (g_unlink(dir_name) < 0)
2457 FILE_OP_ERROR(dir_name, "unlink");
2464 if (g_chdir(prev_dir) < 0) {
2465 FILE_OP_ERROR(prev_dir, "chdir");
2475 gint remove_dir_recursive(const gchar *dir)
2479 const gchar *dir_name;
2482 if (g_stat(dir, &s) < 0) {
2483 FILE_OP_ERROR(dir, "stat");
2484 if (ENOENT == errno) return 0;
2488 if (!S_ISDIR(s.st_mode)) {
2489 if (g_unlink(dir) < 0) {
2490 FILE_OP_ERROR(dir, "unlink");
2497 prev_dir = g_get_current_dir();
2498 /* g_print("prev_dir = %s\n", prev_dir); */
2500 if (!path_cmp(prev_dir, dir)) {
2502 if (g_chdir("..") < 0) {
2503 FILE_OP_ERROR(dir, "chdir");
2506 prev_dir = g_get_current_dir();
2509 if (g_chdir(dir) < 0) {
2510 FILE_OP_ERROR(dir, "chdir");
2515 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2516 g_warning("failed to open directory: %s\n", dir);
2522 /* remove all files in the directory */
2523 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2524 /* g_print("removing %s\n", dir_name); */
2526 if (is_dir_exist(dir_name)) {
2527 if (remove_dir_recursive(dir_name) < 0) {
2528 g_warning("can't remove directory\n");
2532 if (g_unlink(dir_name) < 0)
2533 FILE_OP_ERROR(dir_name, "unlink");
2539 if (g_chdir(prev_dir) < 0) {
2540 FILE_OP_ERROR(prev_dir, "chdir");
2547 if (g_rmdir(dir) < 0) {
2548 FILE_OP_ERROR(dir, "rmdir");
2555 gint rename_force(const gchar *oldpath, const gchar *newpath)
2558 if (!is_file_entry_exist(oldpath)) {
2562 if (is_file_exist(newpath)) {
2563 if (g_unlink(newpath) < 0)
2564 FILE_OP_ERROR(newpath, "unlink");
2567 return g_rename(oldpath, newpath);
2571 * Append src file body to the tail of dest file.
2572 * Now keep_backup has no effects.
2574 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2576 FILE *src_fp, *dest_fp;
2580 gboolean err = FALSE;
2582 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2583 FILE_OP_ERROR(src, "fopen");
2587 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2588 FILE_OP_ERROR(dest, "fopen");
2593 if (change_file_mode_rw(dest_fp, dest) < 0) {
2594 FILE_OP_ERROR(dest, "chmod");
2595 g_warning("can't change file mode\n");
2598 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2599 if (n_read < sizeof(buf) && ferror(src_fp))
2601 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2602 g_warning("writing to %s failed.\n", dest);
2610 if (ferror(src_fp)) {
2611 FILE_OP_ERROR(src, "fread");
2615 if (fclose(dest_fp) == EOF) {
2616 FILE_OP_ERROR(dest, "fclose");
2628 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2630 FILE *src_fp, *dest_fp;
2633 gchar *dest_bak = NULL;
2634 gboolean err = FALSE;
2636 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2637 FILE_OP_ERROR(src, "fopen");
2640 if (is_file_exist(dest)) {
2641 dest_bak = g_strconcat(dest, ".bak", NULL);
2642 if (rename_force(dest, dest_bak) < 0) {
2643 FILE_OP_ERROR(dest, "rename");
2650 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2651 FILE_OP_ERROR(dest, "fopen");
2654 if (rename_force(dest_bak, dest) < 0)
2655 FILE_OP_ERROR(dest_bak, "rename");
2661 if (change_file_mode_rw(dest_fp, dest) < 0) {
2662 FILE_OP_ERROR(dest, "chmod");
2663 g_warning("can't change file mode\n");
2666 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2667 if (n_read < sizeof(buf) && ferror(src_fp))
2669 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2670 g_warning("writing to %s failed.\n", dest);
2675 if (rename_force(dest_bak, dest) < 0)
2676 FILE_OP_ERROR(dest_bak, "rename");
2683 if (ferror(src_fp)) {
2684 FILE_OP_ERROR(src, "fread");
2688 if (fclose(dest_fp) == EOF) {
2689 FILE_OP_ERROR(dest, "fclose");
2696 if (rename_force(dest_bak, dest) < 0)
2697 FILE_OP_ERROR(dest_bak, "rename");
2703 if (keep_backup == FALSE && dest_bak)
2711 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2713 if (overwrite == FALSE && is_file_exist(dest)) {
2714 g_warning("move_file(): file %s already exists.", dest);
2718 if (rename_force(src, dest) == 0) return 0;
2720 if (EXDEV != errno) {
2721 FILE_OP_ERROR(src, "rename");
2725 if (copy_file(src, dest, FALSE) < 0) return -1;
2732 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2735 gint bytes_left, to_read;
2738 if (fseek(fp, offset, SEEK_SET) < 0) {
2743 bytes_left = length;
2744 to_read = MIN(bytes_left, sizeof(buf));
2746 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2747 if (n_read < to_read && ferror(fp))
2749 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2752 bytes_left -= n_read;
2753 if (bytes_left == 0)
2755 to_read = MIN(bytes_left, sizeof(buf));
2766 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2769 gboolean err = FALSE;
2771 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2772 FILE_OP_ERROR(dest, "fopen");
2776 if (change_file_mode_rw(dest_fp, dest) < 0) {
2777 FILE_OP_ERROR(dest, "chmod");
2778 g_warning("can't change file mode\n");
2781 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2784 if (!err && fclose(dest_fp) == EOF) {
2785 FILE_OP_ERROR(dest, "fclose");
2790 g_warning("writing to %s failed.\n", dest);
2798 /* convert line endings into CRLF. If the last line doesn't end with
2799 * linebreak, add it.
2801 gchar *canonicalize_str(const gchar *str)
2807 for (p = str; *p != '\0'; ++p) {
2814 if (p == str || *(p - 1) != '\n')
2817 out = outp = g_malloc(new_len + 1);
2818 for (p = str; *p != '\0'; ++p) {
2825 if (p == str || *(p - 1) != '\n') {
2834 gint canonicalize_file(const gchar *src, const gchar *dest)
2836 FILE *src_fp, *dest_fp;
2837 gchar buf[BUFFSIZE];
2839 gboolean err = FALSE;
2840 gboolean last_linebreak = FALSE;
2842 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2843 FILE_OP_ERROR(src, "fopen");
2847 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2848 FILE_OP_ERROR(dest, "fopen");
2853 if (change_file_mode_rw(dest_fp, dest) < 0) {
2854 FILE_OP_ERROR(dest, "chmod");
2855 g_warning("can't change file mode\n");
2858 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2862 if (len == 0) break;
2863 last_linebreak = FALSE;
2865 if (buf[len - 1] != '\n') {
2866 last_linebreak = TRUE;
2867 r = fputs(buf, dest_fp);
2868 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2869 r = fputs(buf, dest_fp);
2872 r = fwrite(buf, 1, len - 1, dest_fp);
2877 r = fputs("\r\n", dest_fp);
2881 g_warning("writing to %s failed.\n", dest);
2889 if (last_linebreak == TRUE) {
2890 if (fputs("\r\n", dest_fp) == EOF)
2894 if (ferror(src_fp)) {
2895 FILE_OP_ERROR(src, "fgets");
2899 if (fclose(dest_fp) == EOF) {
2900 FILE_OP_ERROR(dest, "fclose");
2912 gint canonicalize_file_replace(const gchar *file)
2916 tmp_file = get_tmp_file();
2918 if (canonicalize_file(file, tmp_file) < 0) {
2923 if (move_file(tmp_file, file, TRUE) < 0) {
2924 g_warning("can't replace %s .\n", file);
2934 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2936 FILE *src_fp, *dest_fp;
2937 gchar buf[BUFFSIZE];
2938 gboolean err = FALSE;
2940 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2941 FILE_OP_ERROR(src, "fopen");
2945 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2946 FILE_OP_ERROR(dest, "fopen");
2951 if (change_file_mode_rw(dest_fp, dest) < 0) {
2952 FILE_OP_ERROR(dest, "chmod");
2953 g_warning("can't change file mode\n");
2956 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2958 if (fputs(buf, dest_fp) == EOF) {
2959 g_warning("writing to %s failed.\n", dest);
2967 if (ferror(src_fp)) {
2968 FILE_OP_ERROR(src, "fgets");
2972 if (fclose(dest_fp) == EOF) {
2973 FILE_OP_ERROR(dest, "fclose");
2985 gint uncanonicalize_file_replace(const gchar *file)
2989 tmp_file = get_tmp_file();
2991 if (uncanonicalize_file(file, tmp_file) < 0) {
2996 if (move_file(tmp_file, file, TRUE) < 0) {
2997 g_warning("can't replace %s .\n", file);
3007 gchar *normalize_newlines(const gchar *str)
3009 const gchar *p = str;
3012 out = outp = g_malloc(strlen(str) + 1);
3013 for (p = str; *p != '\0'; ++p) {
3015 if (*(p + 1) != '\n')
3026 gchar *get_outgoing_rfc2822_str(FILE *fp)
3028 gchar buf[BUFFSIZE];
3032 str = g_string_new(NULL);
3034 /* output header part */
3035 while (fgets(buf, sizeof(buf), fp) != NULL) {
3037 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3044 else if (next != ' ' && next != '\t') {
3048 if (fgets(buf, sizeof(buf), fp) == NULL)
3052 g_string_append(str, buf);
3053 g_string_append(str, "\r\n");
3059 /* output body part */
3060 while (fgets(buf, sizeof(buf), fp) != NULL) {
3063 g_string_append_c(str, '.');
3064 g_string_append(str, buf);
3065 g_string_append(str, "\r\n");
3069 g_string_free(str, FALSE);
3075 * Create a new boundary in a way that it is very unlikely that this
3076 * will occur in the following text. It would be easy to ensure
3077 * uniqueness if everything is either quoted-printable or base64
3078 * encoded (note that conversion is allowed), but because MIME bodies
3079 * may be nested, it may happen that the same boundary has already
3082 * boundary := 0*69<bchars> bcharsnospace
3083 * bchars := bcharsnospace / " "
3084 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3085 * "+" / "_" / "," / "-" / "." /
3086 * "/" / ":" / "=" / "?"
3088 * some special characters removed because of buggy MTAs
3091 gchar *generate_mime_boundary(const gchar *prefix)
3093 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3094 "abcdefghijklmnopqrstuvwxyz"
3099 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3100 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3103 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3107 gint change_file_mode_rw(FILE *fp, const gchar *file)
3110 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3112 return g_chmod(file, S_IRUSR|S_IWUSR);
3116 FILE *my_tmpfile(void)
3118 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3119 const gchar suffix[] = ".XXXXXX";
3120 const gchar *tmpdir;
3122 const gchar *progname;
3129 tmpdir = get_tmp_dir();
3130 tmplen = strlen(tmpdir);
3131 progname = g_get_prgname();
3132 if (progname == NULL)
3133 progname = "claws-mail";
3134 proglen = strlen(progname);
3135 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3138 memcpy(fname, tmpdir, tmplen);
3139 fname[tmplen] = G_DIR_SEPARATOR;
3140 memcpy(fname + tmplen + 1, progname, proglen);
3141 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3143 fd = mkstemp(fname);
3150 /* verify that we can write in the file after unlinking */
3151 if (write(fd, buf, 1) < 0) {
3158 fp = fdopen(fd, "w+b");
3166 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3171 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3175 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3176 dir, G_DIR_SEPARATOR);
3177 fd = mkstemp_name(template, filename);
3180 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3181 fd = mkstemp(*filename);
3183 return fdopen(fd, "w+");
3186 FILE *str_open_as_stream(const gchar *str)
3191 g_return_val_if_fail(str != NULL, NULL);
3195 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3200 if (len == 0) return fp;
3202 if (fwrite(str, 1, len, fp) != len) {
3203 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3212 gint str_write_to_file(const gchar *str, const gchar *file)
3217 g_return_val_if_fail(str != NULL, -1);
3218 g_return_val_if_fail(file != NULL, -1);
3220 if ((fp = g_fopen(file, "wb")) == NULL) {
3221 FILE_OP_ERROR(file, "fopen");
3231 if (fwrite(str, 1, len, fp) != len) {
3232 FILE_OP_ERROR(file, "fwrite");
3238 if (fclose(fp) == EOF) {
3239 FILE_OP_ERROR(file, "fclose");
3247 gchar *file_read_to_str(const gchar *file)
3252 g_return_val_if_fail(file != NULL, NULL);
3254 if ((fp = g_fopen(file, "rb")) == NULL) {
3255 FILE_OP_ERROR(file, "fopen");
3259 str = file_read_stream_to_str(fp);
3266 gchar *file_read_stream_to_str(FILE *fp)
3273 g_return_val_if_fail(fp != NULL, NULL);
3275 array = g_byte_array_new();
3277 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3278 if (n_read < sizeof(buf) && ferror(fp))
3280 g_byte_array_append(array, buf, n_read);
3284 FILE_OP_ERROR("file stream", "fread");
3285 g_byte_array_free(array, TRUE);
3290 g_byte_array_append(array, buf, 1);
3291 str = (gchar *)array->data;
3292 g_byte_array_free(array, FALSE);
3294 if (!g_utf8_validate(str, -1, NULL)) {
3295 const gchar *src_codeset, *dest_codeset;
3297 src_codeset = conv_get_locale_charset_str();
3298 dest_codeset = CS_UTF_8;
3299 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3307 static gint execute_async(gchar *const argv[])
3309 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3311 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3312 NULL, NULL, NULL, FALSE) == FALSE) {
3313 g_warning("Couldn't execute command: %s\n", argv[0]);
3320 static gint execute_sync(gchar *const argv[])
3324 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3326 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3327 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3328 g_warning("Couldn't execute command: %s\n", argv[0]);
3333 if (WIFEXITED(status))
3334 return WEXITSTATUS(status);
3342 gint execute_command_line(const gchar *cmdline, gboolean async)
3347 debug_print("execute_command_line(): executing: %s\n", cmdline);
3349 argv = strsplit_with_quote(cmdline, " ", 0);
3352 ret = execute_async(argv);
3354 ret = execute_sync(argv);
3361 gchar *get_command_output(const gchar *cmdline)
3363 gchar *child_stdout;
3366 g_return_val_if_fail(cmdline != NULL, NULL);
3368 debug_print("get_command_output(): executing: %s\n", cmdline);
3370 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3372 g_warning("Couldn't execute command: %s\n", cmdline);
3376 return child_stdout;
3379 static gint is_unchanged_uri_char(char c)
3390 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3396 for(i = 0; i < strlen(uri) ; i++) {
3397 if (is_unchanged_uri_char(uri[i])) {
3398 if (k + 2 >= bufsize)
3400 encoded_uri[k++] = uri[i];
3403 char * hexa = "0123456789ABCDEF";
3405 if (k + 4 >= bufsize)
3407 encoded_uri[k++] = '%';
3408 encoded_uri[k++] = hexa[uri[i] / 16];
3409 encoded_uri[k++] = hexa[uri[i] % 16];
3415 gint open_uri(const gchar *uri, const gchar *cmdline)
3418 gchar buf[BUFFSIZE];
3420 gchar encoded_uri[BUFFSIZE];
3421 g_return_val_if_fail(uri != NULL, -1);
3423 /* an option to choose whether to use encode_uri or not ? */
3424 encode_uri(encoded_uri, BUFFSIZE, uri);
3427 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3428 !strchr(p + 2, '%'))
3429 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3432 g_warning("Open URI command line is invalid "
3433 "(there must be only one '%%s'): %s",
3435 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3438 execute_command_line(buf, TRUE);
3440 extern osso_context_t *get_osso_context(void);
3441 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3442 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3443 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3448 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3450 gchar buf[BUFFSIZE];
3453 g_return_val_if_fail(filepath != NULL, -1);
3456 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3457 !strchr(p + 2, '%'))
3458 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3461 g_warning("Open Text Editor command line is invalid "
3462 "(there must be only one '%%s'): %s",
3464 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3467 execute_command_line(buf, TRUE);
3472 time_t remote_tzoffset_sec(const gchar *zone)
3474 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3480 time_t remoteoffset;
3482 strncpy(zone3, zone, 3);
3486 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3487 (c == '+' || c == '-')) {
3488 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3490 remoteoffset = -remoteoffset;
3491 } else if (!strncmp(zone, "UT" , 2) ||
3492 !strncmp(zone, "GMT", 2)) {
3494 } else if (strlen(zone3) == 3) {
3495 for (p = ustzstr; *p != '\0'; p += 3) {
3496 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3497 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3498 remoteoffset = iustz * 3600;
3504 } else if (strlen(zone3) == 1) {
3506 case 'Z': remoteoffset = 0; break;
3507 case 'A': remoteoffset = -1; break;
3508 case 'B': remoteoffset = -2; break;
3509 case 'C': remoteoffset = -3; break;
3510 case 'D': remoteoffset = -4; break;
3511 case 'E': remoteoffset = -5; break;
3512 case 'F': remoteoffset = -6; break;
3513 case 'G': remoteoffset = -7; break;
3514 case 'H': remoteoffset = -8; break;
3515 case 'I': remoteoffset = -9; break;
3516 case 'K': remoteoffset = -10; break; /* J is not used */
3517 case 'L': remoteoffset = -11; break;
3518 case 'M': remoteoffset = -12; break;
3519 case 'N': remoteoffset = 1; break;
3520 case 'O': remoteoffset = 2; break;
3521 case 'P': remoteoffset = 3; break;
3522 case 'Q': remoteoffset = 4; break;
3523 case 'R': remoteoffset = 5; break;
3524 case 'S': remoteoffset = 6; break;
3525 case 'T': remoteoffset = 7; break;
3526 case 'U': remoteoffset = 8; break;
3527 case 'V': remoteoffset = 9; break;
3528 case 'W': remoteoffset = 10; break;
3529 case 'X': remoteoffset = 11; break;
3530 case 'Y': remoteoffset = 12; break;
3531 default: remoteoffset = 0; break;
3533 remoteoffset = remoteoffset * 3600;
3537 return remoteoffset;
3540 time_t tzoffset_sec(time_t *now)
3546 lt = localtime(now);
3548 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3550 if (lt->tm_year < gmt.tm_year)
3552 else if (lt->tm_year > gmt.tm_year)
3554 else if (lt->tm_yday < gmt.tm_yday)
3556 else if (lt->tm_yday > gmt.tm_yday)
3559 if (off >= 24 * 60) /* should be impossible */
3560 off = 23 * 60 + 59; /* if not, insert silly value */
3561 if (off <= -24 * 60)
3562 off = -(23 * 60 + 59);
3567 /* calculate timezone offset */
3568 gchar *tzoffset(time_t *now)
3570 static gchar offset_string[6];
3576 lt = localtime(now);
3578 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3580 if (lt->tm_year < gmt.tm_year)
3582 else if (lt->tm_year > gmt.tm_year)
3584 else if (lt->tm_yday < gmt.tm_yday)
3586 else if (lt->tm_yday > gmt.tm_yday)
3594 if (off >= 24 * 60) /* should be impossible */
3595 off = 23 * 60 + 59; /* if not, insert silly value */
3597 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3599 return offset_string;
3602 void get_rfc822_date(gchar *buf, gint len)
3606 gchar day[4], mon[4];
3607 gint dd, hh, mm, ss, yyyy;
3612 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3613 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3614 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3615 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3618 /* just a wrapper to suppress the warning of gcc about %c */
3619 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3620 const struct tm *tm)
3622 return strftime(s, max, format, tm);
3625 void debug_set_mode(gboolean mode)
3630 gboolean debug_get_mode(void)
3635 void debug_print_real(const gchar *format, ...)
3638 gchar buf[BUFFSIZE];
3640 if (!debug_mode) return;
3642 va_start(args, format);
3643 g_vsnprintf(buf, sizeof(buf), format, args);
3650 const char * debug_srcname(const char *file)
3652 const char *s = strrchr (file, '/');
3657 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3659 if (subject == NULL)
3662 subject += subject_get_prefix_length(subject);
3664 return g_hash_table_lookup(subject_table, subject);
3667 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3670 if (subject == NULL || *subject == 0)
3672 subject += subject_get_prefix_length(subject);
3673 g_hash_table_insert(subject_table, subject, data);
3676 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3678 if (subject == NULL)
3681 subject += subject_get_prefix_length(subject);
3682 g_hash_table_remove(subject_table, subject);
3686 *\brief Check if a string is prefixed with known (combinations)
3687 * of prefixes. The function assumes that each prefix
3688 * is terminated by zero or exactly _one_ space.
3690 *\param str String to check for a prefixes
3692 *\return int Number of chars in the prefix that should be skipped
3693 * for a "clean" subject line. If no prefix was found, 0
3696 int subject_get_prefix_length(const gchar *subject)
3698 /*!< Array with allowable reply prefixes regexps. */
3699 static const gchar * const prefixes[] = {
3700 "Re\\:", /* "Re:" */
3701 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3702 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3703 "Aw\\:", /* "Aw:" (German) */
3704 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3705 "Res\\:", /* "Res:" (Brazilian Outlook) */
3706 "Fw\\:", /* "Fw:" Forward */
3707 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3708 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3709 "Rif\\:", /* "Rif:" (Italian Outlook) */
3710 "Sv\\:", /* "Sv" (Norwegian) */
3711 "Vs\\:", /* "Vs" (Norwegian) */
3712 "Ad\\:", /* "Ad" (Norwegian) */
3713 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3716 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3719 static regex_t regex;
3720 static gboolean init_;
3722 if (!subject) return 0;
3723 if (!*subject) return 0;
3726 GString *s = g_string_new("");
3728 for (n = 0; n < PREFIXES; n++)
3729 /* Terminate each prefix regexpression by a
3730 * "\ ?" (zero or ONE space), and OR them */
3731 g_string_append_printf(s, "(%s\\ ?)%s",
3736 g_string_prepend(s, "(");
3737 g_string_append(s, ")+"); /* match at least once */
3738 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3741 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3742 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3743 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3744 debug_print("Error compiling regexp %s\n", s->str);
3745 g_string_free(s, TRUE);
3749 g_string_free(s, TRUE);
3753 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3759 guint g_stricase_hash(gconstpointer gptr)
3761 guint hash_result = 0;
3764 for (str = gptr; str && *str; str++) {
3765 hash_result += toupper(*str);
3771 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3773 const char *str1 = gptr1;
3774 const char *str2 = gptr2;
3776 return !strcasecmp(str1, str2);
3779 gint g_int_compare(gconstpointer a, gconstpointer b)
3781 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3784 gchar *generate_msgid(gchar *buf, gint len)
3793 addr = g_strconcat("@", get_domain_name(), NULL);
3795 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3796 lt->tm_year + 1900, lt->tm_mon + 1,
3797 lt->tm_mday, lt->tm_hour,
3798 lt->tm_min, lt->tm_sec,
3799 (guint) rand(), addr);
3806 quote_cmd_argument()
3808 return a quoted string safely usable in argument of a command.
3810 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3813 gint quote_cmd_argument(gchar * result, guint size,
3823 for(p = path ; * p != '\0' ; p ++) {
3825 if (isalnum((guchar)*p) || (* p == '/')) {
3826 if (remaining > 0) {
3832 result[size - 1] = '\0';
3837 if (remaining >= 2) {
3845 result[size - 1] = '\0';
3850 if (remaining > 0) {
3854 result[size - 1] = '\0';
3868 static void g_node_map_recursive(GNode *node, gpointer data)
3870 GNodeMapData *mapdata = (GNodeMapData *) data;
3872 GNodeMapData newmapdata;
3875 newdata = mapdata->func(node->data, mapdata->data);
3876 if (newdata != NULL) {
3877 newnode = g_node_new(newdata);
3878 g_node_append(mapdata->parent, newnode);
3880 newmapdata.parent = newnode;
3881 newmapdata.func = mapdata->func;
3882 newmapdata.data = mapdata->data;
3884 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3888 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3891 GNodeMapData mapdata;
3893 g_return_val_if_fail(node != NULL, NULL);
3894 g_return_val_if_fail(func != NULL, NULL);
3896 root = g_node_new(func(node->data, data));
3898 mapdata.parent = root;
3899 mapdata.func = func;
3900 mapdata.data = data;
3902 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3907 #define HEX_TO_INT(val, hex) \
3911 if ('0' <= c && c <= '9') { \
3913 } else if ('a' <= c && c <= 'f') { \
3914 val = c - 'a' + 10; \
3915 } else if ('A' <= c && c <= 'F') { \
3916 val = c - 'A' + 10; \
3922 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3929 if (hi == -1 || lo == -1)
3932 *out = (hi << 4) + lo;
3936 #define INT_TO_HEX(hex, val) \
3939 hex = '0' + (val); \
3941 hex = 'A' + (val) - 10; \
3944 void get_hex_str(gchar *out, guchar ch)
3948 INT_TO_HEX(hex, ch >> 4);
3950 INT_TO_HEX(hex, ch & 0x0f);
3956 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3958 #define G_PRINT_REF g_print
3962 *\brief Register ref counted pointer. It is based on GBoxed, so should
3963 * work with anything that uses the GType system. The semantics
3964 * are similar to a C++ auto pointer, with the exception that
3965 * C doesn't have automatic closure (calling destructors) when
3966 * exiting a block scope.
3967 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3968 * function directly.
3970 *\return GType A GType type.
3972 GType g_auto_pointer_register(void)
3974 static GType auto_pointer_type;
3975 if (!auto_pointer_type)
3977 g_boxed_type_register_static
3978 ("G_TYPE_AUTO_POINTER",
3979 (GBoxedCopyFunc) g_auto_pointer_copy,
3980 (GBoxedFreeFunc) g_auto_pointer_free);
3981 return auto_pointer_type;
3985 *\brief Structure with g_new() allocated pointer guarded by the
3988 typedef struct AutoPointerRef {
3989 void (*free) (gpointer);
3995 *\brief The auto pointer opaque structure that references the
3996 * pointer guard block.
3998 typedef struct AutoPointer {
3999 AutoPointerRef *ref;
4000 gpointer ptr; /*!< access to protected pointer */
4004 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4008 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4009 * ... when assigning, copying and freeing storage elements
4011 * gtk_list_store_new(N_S_COLUMNS,
4012 * G_TYPE_AUTO_POINTER,
4016 * Template *precious_data = g_new0(Template, 1);
4017 * g_pointer protect = g_auto_pointer_new(precious_data);
4019 * gtk_list_store_set(container, &iter,
4023 * ... the gtk_list_store has copied the pointer and
4024 * ... incremented its reference count, we should free
4025 * ... the auto pointer (in C++ a destructor would do
4026 * ... this for us when leaving block scope)
4028 * g_auto_pointer_free(protect);
4030 * ... gtk_list_store_set() now manages the data. When
4031 * ... *explicitly* requesting a pointer from the list
4032 * ... store, don't forget you get a copy that should be
4033 * ... freed with g_auto_pointer_free() eventually.
4037 *\param pointer Pointer to be guarded.
4039 *\return GAuto * Pointer that should be used in containers with
4042 GAuto *g_auto_pointer_new(gpointer p)
4044 AutoPointerRef *ref;
4050 ref = g_new0(AutoPointerRef, 1);
4051 ptr = g_new0(AutoPointer, 1);
4061 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4067 *\brief Allocate an autopointer using the passed \a free function to
4068 * free the guarded pointer
4070 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4077 aptr = g_auto_pointer_new(p);
4078 aptr->ref->free = free_;
4082 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4084 if (auto_ptr == NULL)
4086 return ((AutoPointer *) auto_ptr)->ptr;
4090 *\brief Copies an auto pointer by. It's mostly not necessary
4091 * to call this function directly, unless you copy/assign
4092 * the guarded pointer.
4094 *\param auto_ptr Auto pointer returned by previous call to
4095 * g_auto_pointer_new_XXX()
4097 *\return gpointer An auto pointer
4099 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4102 AutoPointerRef *ref;
4105 if (auto_ptr == NULL)
4110 newp = g_new0(AutoPointer, 1);
4113 newp->ptr = ref->pointer;
4117 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4123 *\brief Free an auto pointer
4125 void g_auto_pointer_free(GAuto *auto_ptr)
4128 AutoPointerRef *ref;
4130 if (auto_ptr == NULL)
4136 if (--(ref->cnt) == 0) {
4138 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4140 ref->free(ref->pointer);
4145 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4150 void replace_returns(gchar *str)
4155 while (strstr(str, "\n")) {
4156 *strstr(str, "\n") = ' ';
4158 while (strstr(str, "\r")) {
4159 *strstr(str, "\r") = ' ';
4163 /* get_uri_part() - retrieves a URI starting from scanpos.
4164 Returns TRUE if succesful */
4165 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4166 const gchar **bp, const gchar **ep, gboolean hdr)
4169 gint parenthese_cnt = 0;
4171 g_return_val_if_fail(start != NULL, FALSE);
4172 g_return_val_if_fail(scanpos != NULL, FALSE);
4173 g_return_val_if_fail(bp != NULL, FALSE);
4174 g_return_val_if_fail(ep != NULL, FALSE);
4178 /* find end point of URI */
4179 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4180 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4181 !IS_ASCII(*(const guchar *)ep_) ||
4182 strchr("[]{}<>\"", *ep_)) {
4184 } else if (strchr("(", *ep_)) {
4186 } else if (strchr(")", *ep_)) {
4187 if (parenthese_cnt > 0)
4194 /* no punctuation at end of string */
4196 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4197 * should pass some URI type to this function and decide on that whether
4198 * to perform punctuation stripping */
4200 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4202 for (; ep_ - 1 > scanpos + 1 &&
4203 IS_REAL_PUNCT(*(ep_ - 1));
4207 #undef IS_REAL_PUNCT
4214 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4216 while (bp && *bp && g_ascii_isspace(*bp))
4218 return g_strndup(bp, ep - bp);
4221 /* valid mail address characters */
4222 #define IS_RFC822_CHAR(ch) \
4226 !g_ascii_isspace(ch) && \
4227 !strchr("(),;<>\"", (ch)))
4229 /* alphabet and number within 7bit ASCII */
4230 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4231 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4233 static GHashTable *create_domain_tab(void)
4235 static const gchar *toplvl_domains [] = {
4237 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4238 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4239 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4240 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4241 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4242 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4243 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4244 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4245 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4246 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4247 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4248 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4249 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4250 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4251 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4252 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4253 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4254 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4255 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4256 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4257 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4258 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4259 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4260 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4261 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4262 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4265 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4267 g_return_val_if_fail(htab, NULL);
4268 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4269 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4273 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4275 const gint MAX_LVL_DOM_NAME_LEN = 6;
4276 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4277 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4280 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4283 for (p = buf; p < m && first < last; *p++ = *first++)
4287 return g_hash_table_lookup(tab, buf) != NULL;
4290 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4291 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4292 const gchar **bp, const gchar **ep, gboolean hdr)
4294 /* more complex than the uri part because we need to scan back and forward starting from
4295 * the scan position. */
4296 gboolean result = FALSE;
4297 const gchar *bp_ = NULL;
4298 const gchar *ep_ = NULL;
4299 static GHashTable *dom_tab;
4300 const gchar *last_dot = NULL;
4301 const gchar *prelast_dot = NULL;
4302 const gchar *last_tld_char = NULL;
4304 /* the informative part of the email address (describing the name
4305 * of the email address owner) may contain quoted parts. the
4306 * closure stack stores the last encountered quotes. */
4307 gchar closure_stack[128];
4308 gchar *ptr = closure_stack;
4310 g_return_val_if_fail(start != NULL, FALSE);
4311 g_return_val_if_fail(scanpos != NULL, FALSE);
4312 g_return_val_if_fail(bp != NULL, FALSE);
4313 g_return_val_if_fail(ep != NULL, FALSE);
4316 const gchar *start_quote = NULL;
4317 const gchar *end_quote = NULL;
4319 /* go to the real start */
4320 if (start[0] == ',')
4322 if (start[0] == ';')
4324 while (start[0] == '\n' || start[0] == '\r')
4326 while (start[0] == ' ' || start[0] == '\t')
4331 /* check if there are quotes (to skip , in them) */
4332 if (*start == '"') {
4333 start_quote = start;
4335 end_quote = strstr(start, "\"");
4341 /* skip anything between quotes */
4342 if (start_quote && end_quote) {
4347 /* find end (either , or ; or end of line) */
4348 if (strstr(start, ",") && strstr(start, ";"))
4349 *ep = strstr(start,",") < strstr(start, ";")
4350 ? strstr(start, ",") : strstr(start, ";");
4351 else if (strstr(start, ","))
4352 *ep = strstr(start, ",");
4353 else if (strstr(start, ";"))
4354 *ep = strstr(start, ";");
4356 *ep = start+strlen(start);
4358 /* go back to real start */
4359 if (start_quote && end_quote) {
4360 start = start_quote;
4363 /* check there's still an @ in that, or search
4364 * further if possible */
4365 if (strstr(start, "@") && strstr(start, "@") < *ep)
4367 else if (*ep < start+strlen(start)) {
4370 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4378 dom_tab = create_domain_tab();
4379 g_return_val_if_fail(dom_tab, FALSE);
4381 /* scan start of address */
4382 for (bp_ = scanpos - 1;
4383 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4386 /* TODO: should start with an alnum? */
4388 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4391 if (bp_ != scanpos) {
4392 /* scan end of address */
4393 for (ep_ = scanpos + 1;
4394 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4396 prelast_dot = last_dot;
4398 if (*(last_dot + 1) == '.') {
4399 if (prelast_dot == NULL)
4401 last_dot = prelast_dot;
4406 /* TODO: really should terminate with an alnum? */
4407 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4412 if (last_dot == NULL)
4414 if (last_dot >= ep_)
4415 last_dot = prelast_dot;
4416 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4420 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4421 if (*last_tld_char == '?')
4424 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4431 if (!result) return FALSE;
4433 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4434 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4435 && IS_RFC822_CHAR(*(ep_ + 3))) {
4436 /* this informative part with an @ in it is
4437 * followed by the email address */
4440 /* go to matching '>' (or next non-rfc822 char, like \n) */
4441 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4444 /* include the bracket */
4445 if (*ep_ == '>') ep_++;
4447 /* include the leading quote */
4455 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4456 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4459 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4460 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4463 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4464 #define IN_STACK() (ptr > closure_stack)
4465 /* has underrun check */
4466 #define POP_STACK() if(IN_STACK()) --ptr
4467 /* has overrun check */
4468 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4469 /* has underrun check */
4470 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4474 /* scan for the informative part. */
4475 for (bp_ -= 2; bp_ >= start; bp_--) {
4476 /* if closure on the stack keep scanning */
4477 if (PEEK_STACK() == *bp_) {
4481 if (*bp_ == '\'' || *bp_ == '"') {
4486 /* if nothing in the closure stack, do the special conditions
4487 * the following if..else expression simply checks whether
4488 * a token is acceptable. if not acceptable, the clause
4489 * should terminate the loop with a 'break' */
4490 if (!PEEK_STACK()) {
4492 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4493 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4494 /* hyphens are allowed, but only in
4496 } else if (strchr(" \"'", *bp_)) {
4497 /* but anything not being a punctiation
4500 break; /* anything else is rejected */
4507 /* scan forward (should start with an alnum) */
4508 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4524 #undef IS_ASCII_ALNUM
4525 #undef IS_RFC822_CHAR
4527 gchar *make_email_string(const gchar *bp, const gchar *ep)
4529 /* returns a mailto: URI; mailto: is also used to detect the
4530 * uri type later on in the button_pressed signal handler */
4534 tmp = g_strndup(bp, ep - bp);
4535 result = g_strconcat("mailto:", tmp, NULL);
4541 gchar *make_http_string(const gchar *bp, const gchar *ep)
4543 /* returns an http: URI; */
4547 while (bp && *bp && g_ascii_isspace(*bp))
4549 tmp = g_strndup(bp, ep - bp);
4550 result = g_strconcat("http://", tmp, NULL);
4556 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4558 FILE *fp = fopen(path, "rb");
4559 gchar buf[BUFFSIZE];
4560 gchar *result = NULL;
4563 while (fgets(buf, sizeof (buf), fp) != NULL) {
4564 gchar **parts = g_strsplit(buf, ";", 3);
4565 gchar *trimmed = parts[0];
4566 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4568 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4569 trimmed[strlen(trimmed)-1] = '\0';
4571 if (!strcmp(trimmed, type)) {
4572 gboolean needsterminal = FALSE;
4573 if (parts[2] && strstr(parts[2], "needsterminal")) {
4574 needsterminal = TRUE;
4576 if (parts[2] && strstr(parts[2], "test=")) {
4577 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4578 gchar *testcmd = orig_testcmd;
4579 if (strstr(testcmd,";"))
4580 *(strstr(testcmd,";")) = '\0';
4581 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4583 while (testcmd[strlen(testcmd)-1] == '\n')
4584 testcmd[strlen(testcmd)-1] = '\0';
4585 while (testcmd[strlen(testcmd)-1] == '\r')
4586 testcmd[strlen(testcmd)-1] = '\0';
4587 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4588 testcmd[strlen(testcmd)-1] = '\0';
4590 if (strstr(testcmd, "%s")) {
4591 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4592 gint res = system(tmp);
4594 g_free(orig_testcmd);
4601 gint res = system(testcmd);
4602 g_free(orig_testcmd);
4612 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4614 while (trimmed[strlen(trimmed)-1] == '\n')
4615 trimmed[strlen(trimmed)-1] = '\0';
4616 while (trimmed[strlen(trimmed)-1] == '\r')
4617 trimmed[strlen(trimmed)-1] = '\0';
4618 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4619 trimmed[strlen(trimmed)-1] = '\0';
4620 result = g_strdup(trimmed);
4623 /* if there are no single quotes around %s, add them.
4624 * '.*%s.*' is ok, as in display 'png:%s'
4626 if (strstr(result, "%s")
4627 && !(strstr(result, "'") < strstr(result,"%s") &&
4628 strstr(strstr(result,"%s"), "'"))) {
4629 gchar *start = g_strdup(result);
4630 gchar *end = g_strdup(strstr(result, "%s")+2);
4632 *strstr(start, "%s") = '\0';
4633 tmp = g_strconcat(start,"'%s'",end, NULL);
4639 if (needsterminal) {
4640 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4651 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4653 gchar *result = NULL;
4657 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4658 result = mailcap_get_command_in_file(path, type, file_to_open);
4662 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4666 void mailcap_update_default(const gchar *type, const gchar *command)
4668 gchar *path = NULL, *outpath = NULL;
4669 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4670 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4671 FILE *fp = fopen(path, "rb");
4672 FILE *outfp = fopen(outpath, "wb");
4673 gchar buf[BUFFSIZE];
4681 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4682 gchar **parts = g_strsplit(buf, ";", 3);
4683 gchar *trimmed = parts[0];
4684 while (trimmed[0] == ' ')
4686 while (trimmed[strlen(trimmed)-1] == ' ')
4687 trimmed[strlen(trimmed)-1] = '\0';
4689 if (!strcmp(trimmed, type)) {
4698 fprintf(outfp, "%s; %s\n", type, command);
4704 g_rename(outpath, path);
4707 gint copy_dir(const gchar *src, const gchar *dst)
4712 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4713 g_warning("failed to open directory: %s\n", src);
4717 if (make_dir(dst) < 0)
4720 while ((name = g_dir_read_name(dir)) != NULL) {
4721 gchar *old_file, *new_file;
4722 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4723 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4724 debug_print("copying: %s -> %s\n", old_file, new_file);
4725 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4726 gint r = copy_file(old_file, new_file, TRUE);
4731 } else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4734 gchar *target = g_file_read_link(old_file, &error);
4736 r = symlink(target, new_file);
4742 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4743 gint r = copy_dir(old_file, new_file);
4754 /* crude test to see if a file is an email. */
4755 gboolean file_is_email (const gchar *filename)
4761 if (filename == NULL)
4763 if ((fp = g_fopen(filename, "rb")) == NULL)
4765 while (i < 60 && score < 3
4766 && fgets(buffer, sizeof (buffer), fp) > 0) {
4767 if (!strncmp(buffer, "From:", strlen("From:")))
4769 if (!strncmp(buffer, "To:", strlen("To:")))
4771 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4776 return (score >= 3);
4779 gboolean sc_g_list_bigger(GList *list, gint max)
4783 while (cur && i <= max+1) {
4790 gboolean sc_g_slist_bigger(GSList *list, gint max)
4794 while (cur && i <= max+1) {
4801 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4802 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4803 NULL, NULL, NULL, NULL, NULL, NULL};
4804 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4805 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4806 NULL, NULL, NULL, NULL, NULL, NULL};
4807 const gchar *s_am_up = NULL;
4808 const gchar *s_pm_up = NULL;
4809 const gchar *s_am_low = NULL;
4810 const gchar *s_pm_low = NULL;
4811 const gchar *def_loc_format = NULL;
4812 const gchar *date_loc_format = NULL;
4813 const gchar *time_loc_format = NULL;
4814 const gchar *time_am_pm = NULL;
4816 static gboolean time_names_init_done = FALSE;
4818 static void init_time_names(void)
4820 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4821 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4822 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4823 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4824 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4825 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4826 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4828 monthnames[0] = Q_("Complete month name for use by strftime|January");
4829 monthnames[1] = Q_("Complete month name for use by strftime|February");
4830 monthnames[2] = Q_("Complete month name for use by strftime|March");
4831 monthnames[3] = Q_("Complete month name for use by strftime|April");
4832 monthnames[4] = Q_("Complete month name for use by strftime|May");
4833 monthnames[5] = Q_("Complete month name for use by strftime|June");
4834 monthnames[6] = Q_("Complete month name for use by strftime|July");
4835 monthnames[7] = Q_("Complete month name for use by strftime|August");
4836 monthnames[8] = Q_("Complete month name for use by strftime|September");
4837 monthnames[9] = Q_("Complete month name for use by strftime|October");
4838 monthnames[10] = Q_("Complete month name for use by strftime|November");
4839 monthnames[11] = Q_("Complete month name for use by strftime|December");
4841 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4842 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4843 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4844 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4845 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4846 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4847 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4849 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4850 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4851 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4852 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4853 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4854 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4855 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4856 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4857 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4858 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4859 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4860 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4862 s_am_up = Q_("For use by strftime (morning)|AM");
4863 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4864 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4865 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4867 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4868 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4869 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4871 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4873 time_names_init_done = TRUE;
4877 time_t now = time(NULL), next;
4878 gchar buf_a[1024], buf_b[1024];
4880 printf("test strftime:\n");
4882 for (next = now; next < now + 86400*365*2; next++) {
4883 lt = localtime(&now);
4884 strftime(buf_a, 1024,
4885 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4886 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4887 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4888 fast_strftime(buf_b, 1024,
4889 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4890 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4891 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4892 if (strcmp(buf_a, buf_b)) {
4893 printf("diff: \n%s\n%s\n", buf_a, buf_b);
4900 #define CHECK_SIZE() { \
4901 total_done += len; \
4902 if (total_done >= buflen) { \
4903 buf[buflen-1] = '\0'; \
4908 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4910 gchar *curpos = buf;
4911 gint total_done = 0;
4912 gchar subbuf[64], subfmt[64];
4913 static time_t last_tzset = (time_t)0;
4915 if (!time_names_init_done)
4918 if (format == NULL || lt == NULL)
4921 if (last_tzset != time(NULL)) {
4923 last_tzset = time(NULL);
4926 if (*format == '%') {
4927 gint len = 0, tmp = 0;
4931 len = 1; CHECK_SIZE();
4935 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4936 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4939 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4940 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4944 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4945 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4948 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4949 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4952 fast_strftime(subbuf, 64, def_loc_format, lt);
4953 len = strlen(subbuf); CHECK_SIZE();
4954 strncpy2(curpos, subbuf, buflen - total_done);
4957 total_done += 2; CHECK_SIZE();
4958 tmp = (lt->tm_year + 1900)/100;
4959 *curpos++ = '0'+(tmp / 10);
4960 *curpos++ = '0'+(tmp % 10);
4963 total_done += 2; CHECK_SIZE();
4964 *curpos++ = '0'+(lt->tm_mday / 10);
4965 *curpos++ = '0'+(lt->tm_mday % 10);
4968 total_done += 8; CHECK_SIZE();
4969 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4970 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4972 *curpos++ = '0'+(lt->tm_mday / 10);
4973 *curpos++ = '0'+(lt->tm_mday % 10);
4975 tmp = lt->tm_year%100;
4976 *curpos++ = '0'+(tmp / 10);
4977 *curpos++ = '0'+(tmp % 10);
4980 len = 2; CHECK_SIZE();
4981 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4984 len = 10; CHECK_SIZE();
4985 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4986 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4989 total_done += 2; CHECK_SIZE();
4990 *curpos++ = '0'+(lt->tm_hour / 10);
4991 *curpos++ = '0'+(lt->tm_hour % 10);
4994 total_done += 2; CHECK_SIZE();
5000 *curpos++ = '0'+(tmp / 10);
5001 *curpos++ = '0'+(tmp % 10);
5004 len = 3; CHECK_SIZE();
5005 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5008 len = 2; CHECK_SIZE();
5009 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5012 len = 2; CHECK_SIZE();
5018 snprintf(curpos, buflen - total_done, "%2d", tmp);
5021 total_done += 2; CHECK_SIZE();
5022 tmp = lt->tm_mon + 1;
5023 *curpos++ = '0'+(tmp / 10);
5024 *curpos++ = '0'+(tmp % 10);
5027 total_done += 2; CHECK_SIZE();
5028 *curpos++ = '0'+(lt->tm_min / 10);
5029 *curpos++ = '0'+(lt->tm_min % 10);
5032 len = 1; CHECK_SIZE();
5036 if (lt->tm_hour >= 12) {
5037 len = strlen(s_pm_up); CHECK_SIZE();
5038 snprintf(curpos, buflen-total_done, s_pm_up);
5040 len = strlen(s_am_up); CHECK_SIZE();
5041 snprintf(curpos, buflen-total_done, s_am_up);
5045 if (lt->tm_hour >= 12) {
5046 len = strlen(s_pm_low); CHECK_SIZE();
5047 snprintf(curpos, buflen-total_done, s_pm_low);
5049 len = strlen(s_am_low); CHECK_SIZE();
5050 snprintf(curpos, buflen-total_done, s_am_low);
5054 fast_strftime(subbuf, 64, time_am_pm, lt);
5055 len = strlen(subbuf); CHECK_SIZE();
5056 strncpy2(curpos, subbuf, buflen - total_done);
5059 total_done += 5; CHECK_SIZE();
5060 *curpos++ = '0'+(lt->tm_hour / 10);
5061 *curpos++ = '0'+(lt->tm_hour % 10);
5063 *curpos++ = '0'+(lt->tm_min / 10);
5064 *curpos++ = '0'+(lt->tm_min % 10);
5067 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5068 len = strlen(subbuf); CHECK_SIZE();
5069 strncpy2(curpos, subbuf, buflen - total_done);
5072 total_done += 2; CHECK_SIZE();
5073 *curpos++ = '0'+(lt->tm_sec / 10);
5074 *curpos++ = '0'+(lt->tm_sec % 10);
5077 len = 1; CHECK_SIZE();
5081 total_done += 8; CHECK_SIZE();
5082 *curpos++ = '0'+(lt->tm_hour / 10);
5083 *curpos++ = '0'+(lt->tm_hour % 10);
5085 *curpos++ = '0'+(lt->tm_min / 10);
5086 *curpos++ = '0'+(lt->tm_min % 10);
5088 *curpos++ = '0'+(lt->tm_sec / 10);
5089 *curpos++ = '0'+(lt->tm_sec % 10);
5092 len = 1; CHECK_SIZE();
5093 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5096 len = 1; CHECK_SIZE();
5097 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5100 fast_strftime(subbuf, 64, date_loc_format, lt);
5101 len = strlen(subbuf); CHECK_SIZE();
5102 strncpy2(curpos, subbuf, buflen - total_done);
5105 fast_strftime(subbuf, 64, time_loc_format, lt);
5106 len = strlen(subbuf); CHECK_SIZE();
5107 strncpy2(curpos, subbuf, buflen - total_done);
5110 total_done += 2; CHECK_SIZE();
5111 tmp = lt->tm_year%100;
5112 *curpos++ = '0'+(tmp / 10);
5113 *curpos++ = '0'+(tmp % 10);
5116 len = 4; CHECK_SIZE();
5117 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5127 /* let these complicated ones be done with the libc */
5128 snprintf(subfmt, 64, "%%%c", *format);
5129 strftime(subbuf, 64, subfmt, lt);
5130 len = strlen(subbuf); CHECK_SIZE();
5131 strncpy2(curpos, subbuf, buflen - total_done);
5135 /* let these complicated modifiers be done with the libc */
5136 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5137 strftime(subbuf, 64, subfmt, lt);
5138 len = strlen(subbuf); CHECK_SIZE();
5139 strncpy2(curpos, subbuf, buflen - total_done);
5143 g_warning("format error (%c)", *format);
5150 int len = 1; CHECK_SIZE();
5151 *curpos++ = *format++;