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 "../codeconv.h"
67 static gboolean debug_mode = FALSE;
69 static GSList *tempfiles=NULL;
73 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
74 gint g_chdir(const gchar *path)
77 if (G_WIN32_HAVE_WIDECHAR_API()) {
82 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
88 retval = _wchdir(wpath);
100 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
101 if (cp_path == NULL) {
106 retval = chdir(cp_path);
119 gint g_chmod(const gchar *path, gint mode)
122 if (G_WIN32_HAVE_WIDECHAR_API()) {
127 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
133 retval = _wchmod(wpath, mode);
145 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
146 if (cp_path == NULL) {
151 retval = chmod(cp_path, mode);
160 return chmod(path, mode);
163 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
167 gint mkstemp_name(const gchar *template, gchar **name_used)
169 static gulong count=0; /* W32-_mktemp only supports up to 27
173 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
174 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
175 (S_IRUSR | S_IWUSR));
177 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
179 perror(g_strdup_printf("cant create %s",*name_used));
185 #endif /* G_OS_WIN32 */
188 gint mkstemp(const gchar *template)
191 gint res = mkstemp_name(template, &dummyname);
195 #endif /* G_OS_WIN32 */
197 void list_free_strings(GList *list)
199 list = g_list_first(list);
201 while (list != NULL) {
207 void slist_free_strings(GSList *list)
209 while (list != NULL) {
215 GSList *slist_concat_unique (GSList *first, GSList *second)
223 } else if (second == NULL)
226 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
227 if (g_slist_find(ret, tmp->data) == NULL)
228 ret = g_slist_prepend(ret, tmp->data);
233 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
238 void hash_free_strings(GHashTable *table)
240 g_hash_table_foreach(table, hash_free_strings_func, NULL);
243 static void hash_free_value_mem_func(gpointer key, gpointer value,
249 void hash_free_value_mem(GHashTable *table)
251 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
254 gint str_case_equal(gconstpointer v, gconstpointer v2)
256 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
259 guint str_case_hash(gconstpointer key)
261 const gchar *p = key;
265 h = g_ascii_tolower(h);
266 for (p += 1; *p != '\0'; p++)
267 h = (h << 5) - h + g_ascii_tolower(*p);
273 void ptr_array_free_strings(GPtrArray *array)
278 g_return_if_fail(array != NULL);
280 for (i = 0; i < array->len; i++) {
281 str = g_ptr_array_index(array, i);
286 gboolean str_find(const gchar *haystack, const gchar *needle)
288 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
291 gboolean str_case_find(const gchar *haystack, const gchar *needle)
293 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
296 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
298 return strcmp(haystack, needle) == 0;
301 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
303 return g_ascii_strcasecmp(haystack, needle) == 0;
306 gint to_number(const gchar *nstr)
308 register const gchar *p;
310 if (*nstr == '\0') return -1;
312 for (p = nstr; *p != '\0'; p++)
313 if (!g_ascii_isdigit(*p)) return -1;
318 /* convert integer into string,
319 nstr must be not lower than 11 characters length */
320 gchar *itos_buf(gchar *nstr, gint n)
322 g_snprintf(nstr, 11, "%d", n);
326 /* convert integer into string */
329 static gchar nstr[11];
331 return itos_buf(nstr, n);
334 #define divide(num,divisor,i,d) \
336 i = num >> divisor; \
337 d = num & ((1<<divisor)-1); \
338 d = (d*100) >> divisor; \
341 gchar *to_human_readable(off_t size)
343 static gchar str[14];
344 static gchar *b_format = NULL, *kb_format = NULL,
345 *mb_format = NULL, *gb_format = NULL;
346 register int t = 0, r = 0;
347 if (b_format == NULL) {
349 kb_format = _("%d.%02dKB");
350 mb_format = _("%d.%02dMB");
351 gb_format = _("%.2fGB");
354 if (size < (off_t)1024) {
355 g_snprintf(str, sizeof(str), b_format, (gint)size);
357 } else if (size >> 10 < (off_t)1024) {
358 divide(size, 10, t, r);
359 g_snprintf(str, sizeof(str), kb_format, t, r);
361 } else if (size >> 20 < (off_t)1024) {
362 divide(size, 20, t, r);
363 g_snprintf(str, sizeof(str), mb_format, t, r);
366 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
371 /* strcmp with NULL-checking */
372 gint strcmp2(const gchar *s1, const gchar *s2)
374 if (s1 == NULL || s2 == NULL)
377 return strcmp(s1, s2);
379 /* strstr with NULL-checking */
380 gchar *strstr2(const gchar *s1, const gchar *s2)
382 if (s1 == NULL || s2 == NULL)
385 return strstr(s1, s2);
388 gint path_cmp(const gchar *s1, const gchar *s2)
393 gchar *s1buf, *s2buf;
396 if (s1 == NULL || s2 == NULL) return -1;
397 if (*s1 == '\0' || *s2 == '\0') return -1;
400 s1buf = g_strdup (s1);
401 s2buf = g_strdup (s2);
402 subst_char (s1buf, '/', G_DIR_SEPARATOR);
403 subst_char (s2buf, '/', G_DIR_SEPARATOR);
406 #endif /* !G_OS_WIN32 */
411 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
412 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
414 rc = strncmp(s1, s2, MAX(len1, len2));
418 #endif /* !G_OS_WIN32 */
422 /* remove trailing return code */
423 gchar *strretchomp(gchar *str)
427 if (!*str) return str;
429 for (s = str + strlen(str) - 1;
430 s >= str && (*s == '\n' || *s == '\r');
437 /* remove trailing character */
438 gchar *strtailchomp(gchar *str, gchar tail_char)
442 if (!*str) return str;
443 if (tail_char == '\0') return str;
445 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
451 /* remove CR (carriage return) */
452 gchar *strcrchomp(gchar *str)
456 if (!*str) return str;
458 s = str + strlen(str) - 1;
459 if (*s == '\n' && s > str && *(s - 1) == '\r') {
467 void file_strip_crs(const gchar *file)
469 FILE *fp = NULL, *outfp = NULL;
471 gchar *out = get_tmp_file();
475 fp = fopen(file, "rb");
479 outfp = fopen(out, "wb");
485 while (fgets(buf, sizeof (buf), fp) != NULL) {
492 rename_force(out, file);
497 /* Similar to `strstr' but this function ignores the case of both strings. */
498 gchar *strcasestr(const gchar *haystack, const gchar *needle)
500 register size_t haystack_len, needle_len;
502 haystack_len = strlen(haystack);
503 needle_len = strlen(needle);
505 if (haystack_len < needle_len || needle_len == 0)
508 while (haystack_len >= needle_len) {
509 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
510 return (gchar *)haystack;
520 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
521 gconstpointer needle, size_t needlelen)
523 const gchar *haystack_ = (const gchar *)haystack;
524 const gchar *needle_ = (const gchar *)needle;
525 const gchar *haystack_cur = (const gchar *)haystack;
528 return memchr(haystack_, *needle_, haystacklen);
530 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
532 if (haystacklen - (haystack_cur - haystack_) < needlelen)
534 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
535 return (gpointer)haystack_cur;
543 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
544 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
546 register const gchar *s = src;
547 register gchar *d = dest;
557 /* Examine if next block is non-ASCII string */
558 gboolean is_next_nonascii(const gchar *s)
562 /* skip head space */
563 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
565 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
566 if (*(guchar *)p > 127 || *(guchar *)p < 32)
573 gint get_next_word_len(const gchar *s)
577 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
583 /* compare subjects */
584 gint subject_compare(const gchar *s1, const gchar *s2)
588 if (!s1 || !s2) return -1;
589 if (!*s1 || !*s2) return -1;
591 Xstrdup_a(str1, s1, return -1);
592 Xstrdup_a(str2, s2, return -1);
594 trim_subject_for_compare(str1);
595 trim_subject_for_compare(str2);
597 if (!*str1 || !*str2) return -1;
599 return strcmp(str1, str2);
602 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
606 if (!s1 || !s2) return -1;
608 Xstrdup_a(str1, s1, return -1);
609 Xstrdup_a(str2, s2, return -1);
611 trim_subject_for_sort(str1);
612 trim_subject_for_sort(str2);
614 return g_utf8_collate(str1, str2);
617 void trim_subject_for_compare(gchar *str)
621 eliminate_parenthesis(str, '[', ']');
622 eliminate_parenthesis(str, '(', ')');
625 srcp = str + subject_get_prefix_length(str);
627 memmove(str, srcp, strlen(srcp) + 1);
630 void trim_subject_for_sort(gchar *str)
636 srcp = str + subject_get_prefix_length(str);
638 memmove(str, srcp, strlen(srcp) + 1);
641 void trim_subject(gchar *str)
643 register gchar *srcp;
649 srcp = str + subject_get_prefix_length(str);
654 } else if (*srcp == '(') {
666 else if (*srcp == cl)
673 while (g_ascii_isspace(*srcp)) srcp++;
674 memmove(str, srcp, strlen(srcp) + 1);
677 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
679 register gchar *srcp, *destp;
684 while ((destp = strchr(destp, op))) {
690 else if (*srcp == cl)
696 while (g_ascii_isspace(*srcp)) srcp++;
697 memmove(destp, srcp, strlen(srcp) + 1);
701 void extract_parenthesis(gchar *str, gchar op, gchar cl)
703 register gchar *srcp, *destp;
708 while ((srcp = strchr(destp, op))) {
711 memmove(destp, srcp + 1, strlen(srcp));
716 else if (*destp == cl)
728 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
731 register gchar *srcp, *destp;
733 gboolean in_quote = FALSE;
737 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
740 memmove(destp, srcp + 1, strlen(srcp));
743 if (*destp == op && !in_quote)
745 else if (*destp == cl && !in_quote)
747 else if (*destp == quote_chr)
759 void eliminate_quote(gchar *str, gchar quote_chr)
761 register gchar *srcp, *destp;
765 while ((destp = strchr(destp, quote_chr))) {
766 if ((srcp = strchr(destp + 1, quote_chr))) {
768 while (g_ascii_isspace(*srcp)) srcp++;
769 memmove(destp, srcp, strlen(srcp) + 1);
777 void extract_quote(gchar *str, gchar quote_chr)
781 if ((str = strchr(str, quote_chr))) {
783 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
784 memmove(p - 1, p, strlen(p) + 1);
789 memmove(str, str + 1, p - str);
794 void eliminate_address_comment(gchar *str)
796 register gchar *srcp, *destp;
801 while ((destp = strchr(destp, '"'))) {
802 if ((srcp = strchr(destp + 1, '"'))) {
807 while (g_ascii_isspace(*srcp)) srcp++;
808 memmove(destp, srcp, strlen(srcp) + 1);
818 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
824 else if (*srcp == ')')
830 while (g_ascii_isspace(*srcp)) srcp++;
831 memmove(destp, srcp, strlen(srcp) + 1);
835 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
837 gboolean in_quote = FALSE;
840 if (*str == c && !in_quote)
842 if (*str == quote_chr)
850 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
852 gboolean in_quote = FALSE;
855 p = str + strlen(str) - 1;
857 if (*p == c && !in_quote)
867 void extract_address(gchar *str)
869 eliminate_address_comment(str);
870 if (strchr_with_skip_quote(str, '"', '<'))
871 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
875 void extract_list_id_str(gchar *str)
877 if (strchr_with_skip_quote(str, '"', '<'))
878 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
882 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
887 if (!str) return addr_list;
889 Xstrdup_a(work, str, return addr_list);
892 eliminate_address_comment(work);
895 while (workp && *workp) {
898 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
904 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
905 extract_parenthesis_with_skip_quote
906 (workp, '"', '<', '>');
910 addr_list = g_slist_append(addr_list, g_strdup(workp));
918 GSList *address_list_append(GSList *addr_list, const gchar *str)
920 return address_list_append_real(addr_list, str, TRUE);
923 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
925 return address_list_append_real(addr_list, str, FALSE);
928 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
932 if (!str) return msgid_list;
935 while (strp && *strp) {
936 const gchar *start, *end;
939 if ((start = strchr(strp, '<')) != NULL) {
940 end = strchr(start + 1, '>');
945 msgid = g_strndup(start + 1, end - start - 1);
948 msgid_list = g_slist_prepend(msgid_list, msgid);
958 GSList *references_list_append(GSList *msgid_list, const gchar *str)
962 list = references_list_prepend(NULL, str);
963 list = g_slist_reverse(list);
964 msgid_list = g_slist_concat(msgid_list, list);
969 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
974 if (!str) return group_list;
976 Xstrdup_a(work, str, return group_list);
980 while (workp && *workp) {
983 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
991 group_list = g_slist_append(group_list,
1000 GList *add_history(GList *list, const gchar *str)
1004 g_return_val_if_fail(str != NULL, list);
1006 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1009 list = g_list_remove(list, old->data);
1010 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1013 last = g_list_last(list);
1016 list = g_list_remove(list, last->data);
1020 list = g_list_prepend(list, g_strdup(str));
1025 void remove_return(gchar *str)
1027 register gchar *p = str;
1030 if (*p == '\n' || *p == '\r')
1031 memmove(p, p + 1, strlen(p));
1037 void remove_space(gchar *str)
1039 register gchar *p = str;
1044 while (g_ascii_isspace(*(p + spc)))
1047 memmove(p, p + spc, strlen(p + spc) + 1);
1053 void unfold_line(gchar *str)
1055 register gchar *p = str;
1059 if (*p == '\n' || *p == '\r') {
1062 while (g_ascii_isspace(*(p + spc)))
1065 memmove(p, p + spc, strlen(p + spc) + 1);
1071 void subst_char(gchar *str, gchar orig, gchar subst)
1073 register gchar *p = str;
1082 void subst_chars(gchar *str, gchar *orig, gchar subst)
1084 register gchar *p = str;
1087 if (strchr(orig, *p) != NULL)
1093 void subst_for_filename(gchar *str)
1098 subst_chars(str, "\t\r\n\\/*:", '_');
1100 subst_chars(str, "\t\r\n\\/*", '_');
1104 void subst_for_shellsafe_filename(gchar *str)
1108 subst_for_filename(str);
1109 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1112 gboolean is_header_line(const gchar *str)
1114 if (str[0] == ':') return FALSE;
1116 while (*str != '\0' && *str != ' ') {
1125 gboolean is_ascii_str(const gchar *str)
1127 const guchar *p = (const guchar *)str;
1129 while (*p != '\0') {
1130 if (*p != '\t' && *p != ' ' &&
1131 *p != '\r' && *p != '\n' &&
1132 (*p < 32 || *p >= 127))
1140 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1142 const gchar *first_pos;
1143 const gchar *last_pos;
1144 const gchar *p = str;
1145 gint quote_level = -1;
1147 /* speed up line processing by only searching to the last '>' */
1148 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1149 /* skip a line if it contains a '<' before the initial '>' */
1150 if (memchr(str, '<', first_pos - str) != NULL)
1152 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1156 while (p <= last_pos) {
1157 while (p < last_pos) {
1158 if (g_ascii_isspace(*p))
1164 if (strchr(quote_chars, *p))
1166 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1167 /* any characters are allowed except '-' and space */
1169 && !strchr(quote_chars, *p)
1170 && !g_ascii_isspace(*p)
1173 if (strchr(quote_chars, *p))
1185 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1187 const gchar *p = str, *q;
1188 gint cur_line = 0, len;
1190 while ((q = strchr(p, '\n')) != NULL) {
1192 if (len > max_chars) {
1202 if (len > max_chars) {
1211 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1213 gchar * position = NULL;
1214 gchar * tmp_pos = NULL;
1217 if (quote_chars == NULL)
1220 for (i = 0; i < strlen(quote_chars); i++) {
1221 tmp_pos = strchr (str, quote_chars[i]);
1223 || (tmp_pos != NULL && position >= tmp_pos) )
1229 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1231 gchar * position = NULL;
1232 gchar * tmp_pos = NULL;
1235 if (quote_chars == NULL)
1238 for (i = 0; i < strlen(quote_chars); i++) {
1239 tmp_pos = strrchr (str, quote_chars[i]);
1241 || (tmp_pos != NULL && position <= tmp_pos) )
1247 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1249 register guint haystack_len, needle_len;
1250 gboolean in_squote = FALSE, in_dquote = FALSE;
1252 haystack_len = strlen(haystack);
1253 needle_len = strlen(needle);
1255 if (haystack_len < needle_len || needle_len == 0)
1258 while (haystack_len >= needle_len) {
1259 if (!in_squote && !in_dquote &&
1260 !strncmp(haystack, needle, needle_len))
1261 return (gchar *)haystack;
1263 /* 'foo"bar"' -> foo"bar"
1264 "foo'bar'" -> foo'bar' */
1265 if (*haystack == '\'') {
1268 else if (!in_dquote)
1270 } else if (*haystack == '\"') {
1273 else if (!in_squote)
1284 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1287 gchar quote_chr = '"';
1289 gboolean in_quote = FALSE;
1293 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1297 if (*p == op && !in_quote)
1299 else if (*p == cl && !in_quote)
1301 else if (*p == quote_chr)
1314 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1317 GSList *string_list = NULL, *slist;
1319 const gchar *s_op, *s_cl;
1322 g_return_val_if_fail(str != NULL, NULL);
1325 max_tokens = G_MAXINT;
1327 s_op = strchr_with_skip_quote(str, '"', op);
1328 if (!s_op) return NULL;
1330 s_cl = strchr_parenthesis_close(str, op, cl);
1338 new_string = g_new(gchar, len + 1);
1339 strncpy(new_string, str, len);
1340 new_string[len] = 0;
1341 string_list = g_slist_prepend(string_list, new_string);
1345 while (*str && g_ascii_isspace(*str)) str++;
1347 string_list = g_slist_prepend(string_list,
1350 s_op = strchr_with_skip_quote(str, '"', op);
1351 if (!--max_tokens || !s_op) break;
1355 s_cl = strchr_parenthesis_close(str, op, cl);
1356 } while (--max_tokens && s_cl);
1359 str_array = g_new(gchar*, n);
1363 str_array[i--] = NULL;
1364 for (slist = string_list; slist; slist = slist->next)
1365 str_array[i--] = slist->data;
1367 g_slist_free(string_list);
1372 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1375 GSList *string_list = NULL, *slist;
1376 gchar **str_array, *s, *new_str;
1377 guint i, n = 1, len;
1379 g_return_val_if_fail(str != NULL, NULL);
1380 g_return_val_if_fail(delim != NULL, NULL);
1383 max_tokens = G_MAXINT;
1385 s = strstr_with_skip_quote(str, delim);
1387 guint delimiter_len = strlen(delim);
1391 new_str = g_strndup(str, len);
1393 if (new_str[0] == '\'' || new_str[0] == '\"') {
1394 if (new_str[len - 1] == new_str[0]) {
1395 new_str[len - 1] = '\0';
1396 memmove(new_str, new_str + 1, len - 1);
1399 string_list = g_slist_prepend(string_list, new_str);
1401 str = s + delimiter_len;
1402 s = strstr_with_skip_quote(str, delim);
1403 } while (--max_tokens && s);
1407 new_str = g_strdup(str);
1408 if (new_str[0] == '\'' || new_str[0] == '\"') {
1410 if (new_str[len - 1] == new_str[0]) {
1411 new_str[len - 1] = '\0';
1412 memmove(new_str, new_str + 1, len - 1);
1415 string_list = g_slist_prepend(string_list, new_str);
1419 str_array = g_new(gchar*, n);
1423 str_array[i--] = NULL;
1424 for (slist = string_list; slist; slist = slist->next)
1425 str_array[i--] = slist->data;
1427 g_slist_free(string_list);
1432 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1434 gchar *abbrev_group;
1436 const gchar *p = group;
1439 g_return_val_if_fail(group != NULL, NULL);
1441 last = group + strlen(group);
1442 abbrev_group = ap = g_malloc(strlen(group) + 1);
1447 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1449 while (*p != '.') p++;
1452 return abbrev_group;
1457 return abbrev_group;
1460 gchar *trim_string(const gchar *str, gint len)
1462 const gchar *p = str;
1467 if (!str) return NULL;
1468 if (strlen(str) <= len)
1469 return g_strdup(str);
1470 if (g_utf8_validate(str, -1, NULL) == FALSE)
1471 return g_strdup(str);
1473 while (*p != '\0') {
1474 mb_len = g_utf8_skip[*(guchar *)p];
1477 else if (new_len + mb_len > len)
1484 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1485 return g_strconcat(new_str, "...", NULL);
1488 GList *uri_list_extract_filenames(const gchar *uri_list)
1490 GList *result = NULL;
1492 gchar *escaped_utf8uri;
1498 while (g_ascii_isspace(*p)) p++;
1499 if (!strncmp(p, "file:", 5)) {
1502 while (*q && *q != '\n' && *q != '\r') q++;
1505 gchar *file, *locale_file = NULL;
1507 while (q > p && g_ascii_isspace(*q))
1509 Xalloca(escaped_utf8uri, q - p + 2,
1511 Xalloca(file, q - p + 2,
1514 strncpy(escaped_utf8uri, p, q - p + 1);
1515 escaped_utf8uri[q - p + 1] = '\0';
1516 decode_uri(file, escaped_utf8uri);
1518 * g_filename_from_uri() rejects escaped/locale encoded uri
1519 * string which come from Nautilus.
1521 if (g_utf8_validate(file, -1, NULL))
1523 = conv_codeset_strdup(
1526 conv_get_locale_charset_str());
1528 locale_file = g_strdup(file + 5);
1529 result = g_list_append(result, locale_file);
1533 p = strchr(p, '\n');
1540 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1543 static gint axtoi(const gchar *hexstr)
1545 gint hi, lo, result;
1548 if ('0' <= hi && hi <= '9') {
1551 if ('a' <= hi && hi <= 'f') {
1554 if ('A' <= hi && hi <= 'F') {
1559 if ('0' <= lo && lo <= '9') {
1562 if ('a' <= lo && lo <= 'f') {
1565 if ('A' <= lo && lo <= 'F') {
1568 result = lo + (16 * hi);
1572 gboolean is_uri_string(const gchar *str)
1574 while (str && *str && g_ascii_isspace(*str))
1576 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1577 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1578 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1579 g_ascii_strncasecmp(str, "www.", 4) == 0);
1582 gchar *get_uri_path(const gchar *uri)
1584 while (uri && *uri && g_ascii_isspace(*uri))
1586 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1587 return (gchar *)(uri + 7);
1588 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1589 return (gchar *)(uri + 8);
1590 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1591 return (gchar *)(uri + 6);
1593 return (gchar *)uri;
1596 gint get_uri_len(const gchar *str)
1600 if (is_uri_string(str)) {
1601 for (p = str; *p != '\0'; p++) {
1602 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1611 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1612 * plusses, and escape characters are used)
1614 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1616 gchar *dec = decoded_uri;
1617 const gchar *enc = encoded_uri;
1622 if (isxdigit((guchar)enc[0]) &&
1623 isxdigit((guchar)enc[1])) {
1641 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1642 gchar **subject, gchar **body, gchar **attach)
1646 const gchar *forbidden_uris[] = { ".gnupg/",
1651 Xstrdup_a(tmp_mailto, mailto, return -1);
1653 if (!strncmp(tmp_mailto, "mailto:", 7))
1656 p = strchr(tmp_mailto, '?');
1663 *to = g_strdup(tmp_mailto);
1666 gchar *field, *value;
1683 if (*value == '\0') continue;
1685 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1686 *cc = g_strdup(value);
1687 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1688 *bcc = g_strdup(value);
1689 } else if (subject && !*subject &&
1690 !g_ascii_strcasecmp(field, "subject")) {
1691 *subject = g_malloc(strlen(value) + 1);
1692 decode_uri(*subject, value);
1693 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1694 *body = g_malloc(strlen(value) + 1);
1695 decode_uri(*body, value);
1696 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1698 *attach = g_malloc(strlen(value) + 1);
1699 decode_uri(*attach, value);
1700 for (; forbidden_uris[i]; i++) {
1701 if (strstr(*attach, forbidden_uris[i])) {
1702 printf("Refusing to attach '%s', potential private data leak\n",
1717 #include <windows.h>
1718 #ifndef CSIDL_APPDATA
1719 #define CSIDL_APPDATA 0x001a
1721 #ifndef CSIDL_LOCAL_APPDATA
1722 #define CSIDL_LOCAL_APPDATA 0x001c
1724 #ifndef CSIDL_FLAG_CREATE
1725 #define CSIDL_FLAG_CREATE 0x8000
1727 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1731 w32_strerror (int w32_errno)
1733 static char strerr[256];
1734 int ec = (int)GetLastError ();
1738 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1739 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1740 strerr, DIM (strerr)-1, NULL);
1744 static __inline__ void *
1745 dlopen (const char * name, int flag)
1747 void * hd = LoadLibrary (name);
1751 static __inline__ void *
1752 dlsym (void * hd, const char * sym)
1756 void * fnc = GetProcAddress (hd, sym);
1765 static __inline__ const char *
1768 return w32_strerror (0);
1772 static __inline__ int
1784 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1786 static int initialized;
1787 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1791 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1797 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1799 handle = dlopen (dllnames[i], RTLD_LAZY);
1802 func = dlsym (handle, "SHGetFolderPathA");
1813 return func (a,b,c,d,e);
1818 /* Returns a static string with the directroy from which the module
1819 has been loaded. Returns an empty string on error. */
1820 static char *w32_get_module_dir(void)
1822 static char *moddir;
1825 char name[MAX_PATH+10];
1828 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1831 p = strrchr (name, '\\');
1837 moddir = g_strdup (name);
1841 #endif /* G_OS_WIN32 */
1843 /* Return a static string with the locale dir. */
1844 const gchar *get_locale_dir(void)
1846 static gchar *loc_dir;
1850 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1851 "\\share\\locale", NULL);
1854 loc_dir = LOCALEDIR;
1860 const gchar *get_home_dir(void)
1863 static char home_dir[MAX_PATH] = "";
1865 if (home_dir[0] == '\0') {
1866 if (w32_shgetfolderpath
1867 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1868 NULL, 0, home_dir) < 0)
1869 strcpy (home_dir, "C:\\Sylpheed");
1873 static const gchar *homeenv = NULL;
1878 if (!homeenv && g_getenv("HOME") != NULL)
1879 homeenv = g_strdup(g_getenv("HOME"));
1881 homeenv = g_get_home_dir();
1887 const gchar *get_rc_dir(void)
1889 static gchar *rc_dir = NULL;
1892 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1898 const gchar *get_mail_base_dir(void)
1901 static gchar *mail_base_dir = NULL;
1904 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1907 return mail_base_dir;
1909 return get_home_dir();
1913 const gchar *get_news_cache_dir(void)
1915 static gchar *news_cache_dir = NULL;
1917 if (!news_cache_dir)
1918 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1919 NEWS_CACHE_DIR, NULL);
1921 return news_cache_dir;
1924 const gchar *get_imap_cache_dir(void)
1926 static gchar *imap_cache_dir = NULL;
1928 if (!imap_cache_dir)
1929 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1930 IMAP_CACHE_DIR, NULL);
1932 return imap_cache_dir;
1935 const gchar *get_mbox_cache_dir(void)
1937 static gchar *mbox_cache_dir = NULL;
1939 if (!mbox_cache_dir)
1940 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1941 MBOX_CACHE_DIR, NULL);
1943 return mbox_cache_dir;
1946 const gchar *get_mime_tmp_dir(void)
1948 static gchar *mime_tmp_dir = NULL;
1951 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952 MIME_TMP_DIR, NULL);
1954 return mime_tmp_dir;
1957 const gchar *get_template_dir(void)
1959 static gchar *template_dir = NULL;
1962 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1963 TEMPLATE_DIR, NULL);
1965 return template_dir;
1968 const gchar *get_header_cache_dir(void)
1970 static gchar *header_dir = NULL;
1973 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1974 HEADER_CACHE_DIR, NULL);
1979 /* Return the default directory for Plugins. */
1980 const gchar *get_plugin_dir(void)
1983 static gchar *plugin_dir = NULL;
1986 plugin_dir = g_strconcat(w32_get_module_dir(),
1987 "\\lib\\claws-mail\\plugins\\",
1991 if (is_dir_exist(PLUGINDIR))
1994 static gchar *plugin_dir = NULL;
1996 plugin_dir = g_strconcat(get_rc_dir(),
1997 G_DIR_SEPARATOR_S, "plugins",
1998 G_DIR_SEPARATOR_S, NULL);
2004 const gchar *get_tmp_dir(void)
2006 static gchar *tmp_dir = NULL;
2009 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2015 gchar *get_tmp_file(void)
2018 static guint32 id = 0;
2020 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2021 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2026 const gchar *get_domain_name(void)
2029 static gchar *domain_name = NULL;
2035 if (uname(&uts) < 0) {
2037 domain_name = "unknown";
2039 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2040 perror("gethostbyname");
2041 domain_name = g_strdup(uts.nodename);
2043 domain_name = g_strdup(hp->h_name);
2047 debug_print("domain name = %s\n", domain_name);
2056 off_t get_file_size(const gchar *file)
2060 if (g_stat(file, &s) < 0) {
2061 FILE_OP_ERROR(file, "stat");
2068 time_t get_file_mtime(const gchar *file)
2072 if (g_stat(file, &s) < 0) {
2073 FILE_OP_ERROR(file, "stat");
2080 off_t get_file_size_as_crlf(const gchar *file)
2084 gchar buf[BUFFSIZE];
2086 if ((fp = g_fopen(file, "rb")) == NULL) {
2087 FILE_OP_ERROR(file, "fopen");
2091 while (fgets(buf, sizeof(buf), fp) != NULL) {
2093 size += strlen(buf) + 2;
2097 FILE_OP_ERROR(file, "fgets");
2106 off_t get_left_file_size(FILE *fp)
2112 if ((pos = ftell(fp)) < 0) {
2116 if (fseek(fp, 0L, SEEK_END) < 0) {
2120 if ((end = ftell(fp)) < 0) {
2125 if (fseek(fp, pos, SEEK_SET) < 0) {
2133 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2140 if (g_stat(file, &s) < 0) {
2141 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2145 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2152 /* Test on whether FILE is a relative file name. This is
2153 * straightforward for Unix but more complex for Windows. */
2154 gboolean is_relative_filename(const gchar *file)
2159 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2160 return FALSE; /* Prefixed with a hostname - this can't
2161 * be a relative name. */
2163 if ( ((*file >= 'a' && *file <= 'z')
2164 || (*file >= 'A' && *file <= 'Z'))
2166 file += 2; /* Skip drive letter. */
2168 return !(*file == '\\' || *file == '/');
2170 return !(*file == G_DIR_SEPARATOR);
2175 gboolean is_dir_exist(const gchar *dir)
2180 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2183 gboolean is_file_entry_exist(const gchar *file)
2188 return g_file_test(file, G_FILE_TEST_EXISTS);
2191 gboolean dirent_is_regular_file(struct dirent *d)
2193 #ifdef HAVE_DIRENT_D_TYPE
2194 if (d->d_type == DT_REG)
2196 else if (d->d_type != DT_UNKNOWN)
2200 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2203 gboolean dirent_is_directory(struct dirent *d)
2205 #ifdef HAVE_DIRENT_D_TYPE
2206 if (d->d_type == DT_DIR)
2208 else if (d->d_type != DT_UNKNOWN)
2212 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2215 gint change_dir(const gchar *dir)
2217 gchar *prevdir = NULL;
2220 prevdir = g_get_current_dir();
2222 if (g_chdir(dir) < 0) {
2223 FILE_OP_ERROR(dir, "chdir");
2224 if (debug_mode) g_free(prevdir);
2226 } else if (debug_mode) {
2229 cwd = g_get_current_dir();
2230 if (strcmp(prevdir, cwd) != 0)
2231 g_print("current dir: %s\n", cwd);
2239 gint make_dir(const gchar *dir)
2241 if (g_mkdir(dir, S_IRWXU) < 0) {
2242 FILE_OP_ERROR(dir, "mkdir");
2245 if (g_chmod(dir, S_IRWXU) < 0)
2246 FILE_OP_ERROR(dir, "chmod");
2251 gint make_dir_hier(const gchar *dir)
2256 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2257 parent_dir = g_strndup(dir, p - dir);
2258 if (*parent_dir != '\0') {
2259 if (!is_dir_exist(parent_dir)) {
2260 if (make_dir(parent_dir) < 0) {
2269 if (!is_dir_exist(dir)) {
2270 if (make_dir(dir) < 0)
2277 gint remove_all_files(const gchar *dir)
2280 const gchar *dir_name;
2283 prev_dir = g_get_current_dir();
2285 if (g_chdir(dir) < 0) {
2286 FILE_OP_ERROR(dir, "chdir");
2291 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2292 g_warning("failed to open directory: %s\n", dir);
2297 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2298 if (g_unlink(dir_name) < 0)
2299 FILE_OP_ERROR(dir_name, "unlink");
2304 if (g_chdir(prev_dir) < 0) {
2305 FILE_OP_ERROR(prev_dir, "chdir");
2315 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2318 const gchar *dir_name;
2322 prev_dir = g_get_current_dir();
2324 if (g_chdir(dir) < 0) {
2325 FILE_OP_ERROR(dir, "chdir");
2330 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2331 g_warning("failed to open directory: %s\n", dir);
2336 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2337 file_no = to_number(dir_name);
2338 if (file_no > 0 && first <= file_no && file_no <= last) {
2339 if (is_dir_exist(dir_name))
2341 if (g_unlink(dir_name) < 0)
2342 FILE_OP_ERROR(dir_name, "unlink");
2348 if (g_chdir(prev_dir) < 0) {
2349 FILE_OP_ERROR(prev_dir, "chdir");
2359 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2362 const gchar *dir_name;
2366 prev_dir = g_get_current_dir();
2368 if (g_chdir(dir) < 0) {
2369 FILE_OP_ERROR(dir, "chdir");
2374 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2375 FILE_OP_ERROR(dir, "opendir");
2380 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2381 file_no = to_number(dir_name);
2382 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2383 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2384 if (is_dir_exist(dir_name))
2386 if (g_unlink(dir_name) < 0)
2387 FILE_OP_ERROR(dir_name, "unlink");
2393 if (g_chdir(prev_dir) < 0) {
2394 FILE_OP_ERROR(prev_dir, "chdir");
2404 gint remove_all_numbered_files(const gchar *dir)
2406 return remove_numbered_files(dir, 0, UINT_MAX);
2409 gint remove_expired_files(const gchar *dir, guint hours)
2412 const gchar *dir_name;
2416 time_t mtime, now, expire_time;
2418 prev_dir = g_get_current_dir();
2420 if (g_chdir(dir) < 0) {
2421 FILE_OP_ERROR(dir, "chdir");
2426 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2427 g_warning("failed to open directory: %s\n", dir);
2433 expire_time = hours * 60 * 60;
2435 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2436 file_no = to_number(dir_name);
2438 if (g_stat(dir_name, &s) < 0) {
2439 FILE_OP_ERROR(dir_name, "stat");
2442 if (S_ISDIR(s.st_mode))
2444 mtime = MAX(s.st_mtime, s.st_atime);
2445 if (now - mtime > expire_time) {
2446 if (g_unlink(dir_name) < 0)
2447 FILE_OP_ERROR(dir_name, "unlink");
2454 if (g_chdir(prev_dir) < 0) {
2455 FILE_OP_ERROR(prev_dir, "chdir");
2465 gint remove_dir_recursive(const gchar *dir)
2469 const gchar *dir_name;
2472 if (g_stat(dir, &s) < 0) {
2473 FILE_OP_ERROR(dir, "stat");
2474 if (ENOENT == errno) return 0;
2478 if (!S_ISDIR(s.st_mode)) {
2479 if (g_unlink(dir) < 0) {
2480 FILE_OP_ERROR(dir, "unlink");
2487 prev_dir = g_get_current_dir();
2488 /* g_print("prev_dir = %s\n", prev_dir); */
2490 if (!path_cmp(prev_dir, dir)) {
2492 if (g_chdir("..") < 0) {
2493 FILE_OP_ERROR(dir, "chdir");
2496 prev_dir = g_get_current_dir();
2499 if (g_chdir(dir) < 0) {
2500 FILE_OP_ERROR(dir, "chdir");
2505 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2506 g_warning("failed to open directory: %s\n", dir);
2512 /* remove all files in the directory */
2513 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2514 /* g_print("removing %s\n", dir_name); */
2516 if (is_dir_exist(dir_name)) {
2517 if (remove_dir_recursive(dir_name) < 0) {
2518 g_warning("can't remove directory\n");
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");
2537 if (g_rmdir(dir) < 0) {
2538 FILE_OP_ERROR(dir, "rmdir");
2545 gint rename_force(const gchar *oldpath, const gchar *newpath)
2548 if (!is_file_entry_exist(oldpath)) {
2552 if (is_file_exist(newpath)) {
2553 if (g_unlink(newpath) < 0)
2554 FILE_OP_ERROR(newpath, "unlink");
2557 return g_rename(oldpath, newpath);
2561 * Append src file body to the tail of dest file.
2562 * Now keep_backup has no effects.
2564 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2566 FILE *src_fp, *dest_fp;
2570 gboolean err = FALSE;
2572 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2573 FILE_OP_ERROR(src, "fopen");
2577 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2578 FILE_OP_ERROR(dest, "fopen");
2583 if (change_file_mode_rw(dest_fp, dest) < 0) {
2584 FILE_OP_ERROR(dest, "chmod");
2585 g_warning("can't change file mode\n");
2588 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2589 if (n_read < sizeof(buf) && ferror(src_fp))
2591 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2592 g_warning("writing to %s failed.\n", dest);
2600 if (ferror(src_fp)) {
2601 FILE_OP_ERROR(src, "fread");
2605 if (fclose(dest_fp) == EOF) {
2606 FILE_OP_ERROR(dest, "fclose");
2618 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2620 FILE *src_fp, *dest_fp;
2623 gchar *dest_bak = NULL;
2624 gboolean err = FALSE;
2626 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2627 FILE_OP_ERROR(src, "fopen");
2630 if (is_file_exist(dest)) {
2631 dest_bak = g_strconcat(dest, ".bak", NULL);
2632 if (rename_force(dest, dest_bak) < 0) {
2633 FILE_OP_ERROR(dest, "rename");
2640 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2641 FILE_OP_ERROR(dest, "fopen");
2644 if (rename_force(dest_bak, dest) < 0)
2645 FILE_OP_ERROR(dest_bak, "rename");
2651 if (change_file_mode_rw(dest_fp, dest) < 0) {
2652 FILE_OP_ERROR(dest, "chmod");
2653 g_warning("can't change file mode\n");
2656 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2657 if (n_read < sizeof(buf) && ferror(src_fp))
2659 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2660 g_warning("writing to %s failed.\n", dest);
2665 if (rename_force(dest_bak, dest) < 0)
2666 FILE_OP_ERROR(dest_bak, "rename");
2673 if (ferror(src_fp)) {
2674 FILE_OP_ERROR(src, "fread");
2678 if (fclose(dest_fp) == EOF) {
2679 FILE_OP_ERROR(dest, "fclose");
2686 if (rename_force(dest_bak, dest) < 0)
2687 FILE_OP_ERROR(dest_bak, "rename");
2693 if (keep_backup == FALSE && dest_bak)
2701 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2703 if (overwrite == FALSE && is_file_exist(dest)) {
2704 g_warning("move_file(): file %s already exists.", dest);
2708 if (rename_force(src, dest) == 0) return 0;
2710 if (EXDEV != errno) {
2711 FILE_OP_ERROR(src, "rename");
2715 if (copy_file(src, dest, FALSE) < 0) return -1;
2722 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2725 gint bytes_left, to_read;
2728 if (fseek(fp, offset, SEEK_SET) < 0) {
2733 bytes_left = length;
2734 to_read = MIN(bytes_left, sizeof(buf));
2736 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2737 if (n_read < to_read && ferror(fp))
2739 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2742 bytes_left -= n_read;
2743 if (bytes_left == 0)
2745 to_read = MIN(bytes_left, sizeof(buf));
2756 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2759 gboolean err = FALSE;
2761 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2762 FILE_OP_ERROR(dest, "fopen");
2766 if (change_file_mode_rw(dest_fp, dest) < 0) {
2767 FILE_OP_ERROR(dest, "chmod");
2768 g_warning("can't change file mode\n");
2771 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2774 if (!err && fclose(dest_fp) == EOF) {
2775 FILE_OP_ERROR(dest, "fclose");
2780 g_warning("writing to %s failed.\n", dest);
2788 /* convert line endings into CRLF. If the last line doesn't end with
2789 * linebreak, add it.
2791 gchar *canonicalize_str(const gchar *str)
2797 for (p = str; *p != '\0'; ++p) {
2804 if (p == str || *(p - 1) != '\n')
2807 out = outp = g_malloc(new_len + 1);
2808 for (p = str; *p != '\0'; ++p) {
2815 if (p == str || *(p - 1) != '\n') {
2824 gint canonicalize_file(const gchar *src, const gchar *dest)
2826 FILE *src_fp, *dest_fp;
2827 gchar buf[BUFFSIZE];
2829 gboolean err = FALSE;
2830 gboolean last_linebreak = FALSE;
2832 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2833 FILE_OP_ERROR(src, "fopen");
2837 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2838 FILE_OP_ERROR(dest, "fopen");
2843 if (change_file_mode_rw(dest_fp, dest) < 0) {
2844 FILE_OP_ERROR(dest, "chmod");
2845 g_warning("can't change file mode\n");
2848 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2852 if (len == 0) break;
2853 last_linebreak = FALSE;
2855 if (buf[len - 1] != '\n') {
2856 last_linebreak = TRUE;
2857 r = fputs(buf, dest_fp);
2858 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2859 r = fputs(buf, dest_fp);
2862 r = fwrite(buf, 1, len - 1, dest_fp);
2867 r = fputs("\r\n", dest_fp);
2871 g_warning("writing to %s failed.\n", dest);
2879 if (last_linebreak == TRUE) {
2880 if (fputs("\r\n", dest_fp) == EOF)
2884 if (ferror(src_fp)) {
2885 FILE_OP_ERROR(src, "fgets");
2889 if (fclose(dest_fp) == EOF) {
2890 FILE_OP_ERROR(dest, "fclose");
2902 gint canonicalize_file_replace(const gchar *file)
2906 tmp_file = get_tmp_file();
2908 if (canonicalize_file(file, tmp_file) < 0) {
2913 if (move_file(tmp_file, file, TRUE) < 0) {
2914 g_warning("can't replace %s .\n", file);
2924 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2926 FILE *src_fp, *dest_fp;
2927 gchar buf[BUFFSIZE];
2928 gboolean err = FALSE;
2930 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2931 FILE_OP_ERROR(src, "fopen");
2935 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2936 FILE_OP_ERROR(dest, "fopen");
2941 if (change_file_mode_rw(dest_fp, dest) < 0) {
2942 FILE_OP_ERROR(dest, "chmod");
2943 g_warning("can't change file mode\n");
2946 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2948 if (fputs(buf, dest_fp) == EOF) {
2949 g_warning("writing to %s failed.\n", dest);
2957 if (ferror(src_fp)) {
2958 FILE_OP_ERROR(src, "fgets");
2962 if (fclose(dest_fp) == EOF) {
2963 FILE_OP_ERROR(dest, "fclose");
2975 gint uncanonicalize_file_replace(const gchar *file)
2979 tmp_file = get_tmp_file();
2981 if (uncanonicalize_file(file, tmp_file) < 0) {
2986 if (move_file(tmp_file, file, TRUE) < 0) {
2987 g_warning("can't replace %s .\n", file);
2997 gchar *normalize_newlines(const gchar *str)
2999 const gchar *p = str;
3002 out = outp = g_malloc(strlen(str) + 1);
3003 for (p = str; *p != '\0'; ++p) {
3005 if (*(p + 1) != '\n')
3016 gchar *get_outgoing_rfc2822_str(FILE *fp)
3018 gchar buf[BUFFSIZE];
3022 str = g_string_new(NULL);
3024 /* output header part */
3025 while (fgets(buf, sizeof(buf), fp) != NULL) {
3027 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3034 else if (next != ' ' && next != '\t') {
3038 if (fgets(buf, sizeof(buf), fp) == NULL)
3042 g_string_append(str, buf);
3043 g_string_append(str, "\r\n");
3049 /* output body part */
3050 while (fgets(buf, sizeof(buf), fp) != NULL) {
3053 g_string_append_c(str, '.');
3054 g_string_append(str, buf);
3055 g_string_append(str, "\r\n");
3059 g_string_free(str, FALSE);
3065 * Create a new boundary in a way that it is very unlikely that this
3066 * will occur in the following text. It would be easy to ensure
3067 * uniqueness if everything is either quoted-printable or base64
3068 * encoded (note that conversion is allowed), but because MIME bodies
3069 * may be nested, it may happen that the same boundary has already
3072 * boundary := 0*69<bchars> bcharsnospace
3073 * bchars := bcharsnospace / " "
3074 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3075 * "+" / "_" / "," / "-" / "." /
3076 * "/" / ":" / "=" / "?"
3078 * some special characters removed because of buggy MTAs
3081 gchar *generate_mime_boundary(const gchar *prefix)
3083 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3084 "abcdefghijklmnopqrstuvwxyz"
3089 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3090 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3093 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3097 gint change_file_mode_rw(FILE *fp, const gchar *file)
3100 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3102 return g_chmod(file, S_IRUSR|S_IWUSR);
3106 FILE *my_tmpfile(void)
3108 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3109 const gchar suffix[] = ".XXXXXX";
3110 const gchar *tmpdir;
3112 const gchar *progname;
3119 tmpdir = get_tmp_dir();
3120 tmplen = strlen(tmpdir);
3121 progname = g_get_prgname();
3122 if (progname == NULL)
3123 progname = "claws-mail";
3124 proglen = strlen(progname);
3125 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3128 memcpy(fname, tmpdir, tmplen);
3129 fname[tmplen] = G_DIR_SEPARATOR;
3130 memcpy(fname + tmplen + 1, progname, proglen);
3131 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3133 fd = mkstemp(fname);
3140 /* verify that we can write in the file after unlinking */
3141 if (write(fd, buf, 1) < 0) {
3148 fp = fdopen(fd, "w+b");
3156 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3161 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3165 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3166 dir, G_DIR_SEPARATOR);
3167 fd = mkstemp_name(template, filename);
3170 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3171 fd = mkstemp(*filename);
3173 return fdopen(fd, "w+");
3176 FILE *str_open_as_stream(const gchar *str)
3181 g_return_val_if_fail(str != NULL, NULL);
3185 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3190 if (len == 0) return fp;
3192 if (fwrite(str, 1, len, fp) != len) {
3193 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3202 gint str_write_to_file(const gchar *str, const gchar *file)
3207 g_return_val_if_fail(str != NULL, -1);
3208 g_return_val_if_fail(file != NULL, -1);
3210 if ((fp = g_fopen(file, "wb")) == NULL) {
3211 FILE_OP_ERROR(file, "fopen");
3221 if (fwrite(str, 1, len, fp) != len) {
3222 FILE_OP_ERROR(file, "fwrite");
3228 if (fclose(fp) == EOF) {
3229 FILE_OP_ERROR(file, "fclose");
3237 gchar *file_read_to_str(const gchar *file)
3242 g_return_val_if_fail(file != NULL, NULL);
3244 if ((fp = g_fopen(file, "rb")) == NULL) {
3245 FILE_OP_ERROR(file, "fopen");
3249 str = file_read_stream_to_str(fp);
3256 gchar *file_read_stream_to_str(FILE *fp)
3263 g_return_val_if_fail(fp != NULL, NULL);
3265 array = g_byte_array_new();
3267 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3268 if (n_read < sizeof(buf) && ferror(fp))
3270 g_byte_array_append(array, buf, n_read);
3274 FILE_OP_ERROR("file stream", "fread");
3275 g_byte_array_free(array, TRUE);
3280 g_byte_array_append(array, buf, 1);
3281 str = (gchar *)array->data;
3282 g_byte_array_free(array, FALSE);
3284 if (!g_utf8_validate(str, -1, NULL)) {
3285 const gchar *src_codeset, *dest_codeset;
3287 src_codeset = conv_get_locale_charset_str();
3288 dest_codeset = CS_UTF_8;
3289 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3297 gint execute_async(gchar *const argv[])
3299 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3301 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3302 NULL, NULL, NULL, FALSE) == FALSE) {
3303 g_warning("Couldn't execute command: %s\n", argv[0]);
3310 gint execute_sync(gchar *const argv[])
3314 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3316 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3317 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3318 g_warning("Couldn't execute command: %s\n", argv[0]);
3323 if (WIFEXITED(status))
3324 return WEXITSTATUS(status);
3332 gint execute_command_line(const gchar *cmdline, gboolean async)
3337 debug_print("execute_command_line(): executing: %s\n", cmdline);
3339 argv = strsplit_with_quote(cmdline, " ", 0);
3342 ret = execute_async(argv);
3344 ret = execute_sync(argv);
3351 gchar *get_command_output(const gchar *cmdline)
3353 gchar *child_stdout;
3356 g_return_val_if_fail(cmdline != NULL, NULL);
3358 debug_print("get_command_output(): executing: %s\n", cmdline);
3360 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3362 g_warning("Couldn't execute command: %s\n", cmdline);
3366 return child_stdout;
3369 static gint is_unchanged_uri_char(char c)
3381 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3387 for(i = 0; i < strlen(uri) ; i++) {
3388 if (is_unchanged_uri_char(uri[i])) {
3389 if (k + 2 >= bufsize)
3391 encoded_uri[k++] = uri[i];
3394 char * hexa = "0123456789ABCDEF";
3396 if (k + 4 >= bufsize)
3398 encoded_uri[k++] = '%';
3399 encoded_uri[k++] = hexa[uri[i] / 16];
3400 encoded_uri[k++] = hexa[uri[i] % 16];
3406 gint open_uri(const gchar *uri, const gchar *cmdline)
3408 gchar buf[BUFFSIZE];
3410 gchar encoded_uri[BUFFSIZE];
3412 g_return_val_if_fail(uri != NULL, -1);
3414 /* an option to choose whether to use encode_uri or not ? */
3415 encode_uri(encoded_uri, BUFFSIZE, uri);
3418 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3419 !strchr(p + 2, '%'))
3420 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3423 g_warning("Open URI command line is invalid "
3424 "(there must be only one '%%s'): %s",
3426 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3429 execute_command_line(buf, TRUE);
3434 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3436 gchar buf[BUFFSIZE];
3439 g_return_val_if_fail(filepath != NULL, -1);
3442 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3443 !strchr(p + 2, '%'))
3444 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3447 g_warning("Open Text Editor command line is invalid "
3448 "(there must be only one '%%s'): %s",
3450 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3453 execute_command_line(buf, TRUE);
3458 time_t remote_tzoffset_sec(const gchar *zone)
3460 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3466 time_t remoteoffset;
3468 strncpy(zone3, zone, 3);
3472 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3473 (c == '+' || c == '-')) {
3474 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3476 remoteoffset = -remoteoffset;
3477 } else if (!strncmp(zone, "UT" , 2) ||
3478 !strncmp(zone, "GMT", 2)) {
3480 } else if (strlen(zone3) == 3) {
3481 for (p = ustzstr; *p != '\0'; p += 3) {
3482 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3483 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3484 remoteoffset = iustz * 3600;
3490 } else if (strlen(zone3) == 1) {
3492 case 'Z': remoteoffset = 0; break;
3493 case 'A': remoteoffset = -1; break;
3494 case 'B': remoteoffset = -2; break;
3495 case 'C': remoteoffset = -3; break;
3496 case 'D': remoteoffset = -4; break;
3497 case 'E': remoteoffset = -5; break;
3498 case 'F': remoteoffset = -6; break;
3499 case 'G': remoteoffset = -7; break;
3500 case 'H': remoteoffset = -8; break;
3501 case 'I': remoteoffset = -9; break;
3502 case 'K': remoteoffset = -10; break; /* J is not used */
3503 case 'L': remoteoffset = -11; break;
3504 case 'M': remoteoffset = -12; break;
3505 case 'N': remoteoffset = 1; break;
3506 case 'O': remoteoffset = 2; break;
3507 case 'P': remoteoffset = 3; break;
3508 case 'Q': remoteoffset = 4; break;
3509 case 'R': remoteoffset = 5; break;
3510 case 'S': remoteoffset = 6; break;
3511 case 'T': remoteoffset = 7; break;
3512 case 'U': remoteoffset = 8; break;
3513 case 'V': remoteoffset = 9; break;
3514 case 'W': remoteoffset = 10; break;
3515 case 'X': remoteoffset = 11; break;
3516 case 'Y': remoteoffset = 12; break;
3517 default: remoteoffset = 0; break;
3519 remoteoffset = remoteoffset * 3600;
3523 return remoteoffset;
3526 time_t tzoffset_sec(time_t *now)
3532 lt = localtime(now);
3534 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3536 if (lt->tm_year < gmt.tm_year)
3538 else if (lt->tm_year > gmt.tm_year)
3540 else if (lt->tm_yday < gmt.tm_yday)
3542 else if (lt->tm_yday > gmt.tm_yday)
3545 if (off >= 24 * 60) /* should be impossible */
3546 off = 23 * 60 + 59; /* if not, insert silly value */
3547 if (off <= -24 * 60)
3548 off = -(23 * 60 + 59);
3553 /* calculate timezone offset */
3554 gchar *tzoffset(time_t *now)
3556 static gchar offset_string[6];
3562 lt = localtime(now);
3564 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3566 if (lt->tm_year < gmt.tm_year)
3568 else if (lt->tm_year > gmt.tm_year)
3570 else if (lt->tm_yday < gmt.tm_yday)
3572 else if (lt->tm_yday > gmt.tm_yday)
3580 if (off >= 24 * 60) /* should be impossible */
3581 off = 23 * 60 + 59; /* if not, insert silly value */
3583 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3585 return offset_string;
3588 void get_rfc822_date(gchar *buf, gint len)
3592 gchar day[4], mon[4];
3593 gint dd, hh, mm, ss, yyyy;
3598 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3599 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3600 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3601 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3604 /* just a wrapper to suppress the warning of gcc about %c */
3605 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3606 const struct tm *tm)
3608 return strftime(s, max, format, tm);
3611 void debug_set_mode(gboolean mode)
3616 gboolean debug_get_mode(void)
3621 void debug_print_real(const gchar *format, ...)
3624 gchar buf[BUFFSIZE];
3626 if (!debug_mode) return;
3628 va_start(args, format);
3629 g_vsnprintf(buf, sizeof(buf), format, args);
3636 const char * debug_srcname(const char *file)
3638 const char *s = strrchr (file, '/');
3643 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3645 if (subject == NULL)
3648 subject += subject_get_prefix_length(subject);
3650 return g_hash_table_lookup(subject_table, subject);
3653 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3656 if (subject == NULL || *subject == 0)
3658 subject += subject_get_prefix_length(subject);
3659 g_hash_table_insert(subject_table, subject, data);
3662 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3664 if (subject == NULL)
3667 subject += subject_get_prefix_length(subject);
3668 g_hash_table_remove(subject_table, subject);
3672 *\brief Check if a string is prefixed with known (combinations)
3673 * of prefixes. The function assumes that each prefix
3674 * is terminated by zero or exactly _one_ space.
3676 *\param str String to check for a prefixes
3678 *\return int Number of chars in the prefix that should be skipped
3679 * for a "clean" subject line. If no prefix was found, 0
3682 int subject_get_prefix_length(const gchar *subject)
3684 /*!< Array with allowable reply prefixes regexps. */
3685 static const gchar * const prefixes[] = {
3686 "Re\\:", /* "Re:" */
3687 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3688 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3689 "Aw\\:", /* "Aw:" (German) */
3690 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3691 "Res\\:", /* "Res:" (Brazilian Outlook) */
3692 "Fw\\:", /* "Fw:" Forward */
3693 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3694 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3695 "Rif\\:", /* "Rif:" (Italian Outlook) */
3696 "Sv\\:", /* "Sv" (Norwegian) */
3697 "Vs\\:", /* "Vs" (Norwegian) */
3698 "Ad\\:", /* "Ad" (Norwegian) */
3699 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3702 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3705 static regex_t regex;
3706 static gboolean init_;
3708 if (!subject) return 0;
3709 if (!*subject) return 0;
3712 GString *s = g_string_new("");
3714 for (n = 0; n < PREFIXES; n++)
3715 /* Terminate each prefix regexpression by a
3716 * "\ ?" (zero or ONE space), and OR them */
3717 g_string_append_printf(s, "(%s\\ ?)%s",
3722 g_string_prepend(s, "(");
3723 g_string_append(s, ")+"); /* match at least once */
3724 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3727 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3728 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3729 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3730 debug_print("Error compiling regexp %s\n", s->str);
3731 g_string_free(s, TRUE);
3735 g_string_free(s, TRUE);
3739 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3745 guint g_stricase_hash(gconstpointer gptr)
3747 guint hash_result = 0;
3750 for (str = gptr; str && *str; str++) {
3751 hash_result += toupper(*str);
3757 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3759 const char *str1 = gptr1;
3760 const char *str2 = gptr2;
3762 return !strcasecmp(str1, str2);
3765 gint g_int_compare(gconstpointer a, gconstpointer b)
3767 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3770 gchar *generate_msgid(gchar *buf, gint len)
3779 addr = g_strconcat("@", get_domain_name(), NULL);
3781 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3782 lt->tm_year + 1900, lt->tm_mon + 1,
3783 lt->tm_mday, lt->tm_hour,
3784 lt->tm_min, lt->tm_sec,
3785 (guint) rand(), addr);
3792 quote_cmd_argument()
3794 return a quoted string safely usable in argument of a command.
3796 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3799 gint quote_cmd_argument(gchar * result, guint size,
3809 for(p = path ; * p != '\0' ; p ++) {
3811 if (isalnum((guchar)*p) || (* p == '/')) {
3812 if (remaining > 0) {
3818 result[size - 1] = '\0';
3823 if (remaining >= 2) {
3831 result[size - 1] = '\0';
3836 if (remaining > 0) {
3840 result[size - 1] = '\0';
3854 static void g_node_map_recursive(GNode *node, gpointer data)
3856 GNodeMapData *mapdata = (GNodeMapData *) data;
3858 GNodeMapData newmapdata;
3861 newdata = mapdata->func(node->data, mapdata->data);
3862 if (newdata != NULL) {
3863 newnode = g_node_new(newdata);
3864 g_node_append(mapdata->parent, newnode);
3866 newmapdata.parent = newnode;
3867 newmapdata.func = mapdata->func;
3868 newmapdata.data = mapdata->data;
3870 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3874 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3877 GNodeMapData mapdata;
3879 g_return_val_if_fail(node != NULL, NULL);
3880 g_return_val_if_fail(func != NULL, NULL);
3882 root = g_node_new(func(node->data, data));
3884 mapdata.parent = root;
3885 mapdata.func = func;
3886 mapdata.data = data;
3888 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3893 #define HEX_TO_INT(val, hex) \
3897 if ('0' <= c && c <= '9') { \
3899 } else if ('a' <= c && c <= 'f') { \
3900 val = c - 'a' + 10; \
3901 } else if ('A' <= c && c <= 'F') { \
3902 val = c - 'A' + 10; \
3908 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3915 if (hi == -1 || lo == -1)
3918 *out = (hi << 4) + lo;
3922 #define INT_TO_HEX(hex, val) \
3925 hex = '0' + (val); \
3927 hex = 'A' + (val) - 10; \
3930 void get_hex_str(gchar *out, guchar ch)
3934 INT_TO_HEX(hex, ch >> 4);
3936 INT_TO_HEX(hex, ch & 0x0f);
3942 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3944 #define G_PRINT_REF g_print
3948 *\brief Register ref counted pointer. It is based on GBoxed, so should
3949 * work with anything that uses the GType system. The semantics
3950 * are similar to a C++ auto pointer, with the exception that
3951 * C doesn't have automatic closure (calling destructors) when
3952 * exiting a block scope.
3953 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3954 * function directly.
3956 *\return GType A GType type.
3958 GType g_auto_pointer_register(void)
3960 static GType auto_pointer_type;
3961 if (!auto_pointer_type)
3963 g_boxed_type_register_static
3964 ("G_TYPE_AUTO_POINTER",
3965 (GBoxedCopyFunc) g_auto_pointer_copy,
3966 (GBoxedFreeFunc) g_auto_pointer_free);
3967 return auto_pointer_type;
3971 *\brief Structure with g_new() allocated pointer guarded by the
3974 typedef struct AutoPointerRef {
3975 void (*free) (gpointer);
3981 *\brief The auto pointer opaque structure that references the
3982 * pointer guard block.
3984 typedef struct AutoPointer {
3985 AutoPointerRef *ref;
3986 gpointer ptr; /*!< access to protected pointer */
3990 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3994 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3995 * ... when assigning, copying and freeing storage elements
3997 * gtk_list_store_new(N_S_COLUMNS,
3998 * G_TYPE_AUTO_POINTER,
4002 * Template *precious_data = g_new0(Template, 1);
4003 * g_pointer protect = g_auto_pointer_new(precious_data);
4005 * gtk_list_store_set(container, &iter,
4009 * ... the gtk_list_store has copied the pointer and
4010 * ... incremented its reference count, we should free
4011 * ... the auto pointer (in C++ a destructor would do
4012 * ... this for us when leaving block scope)
4014 * g_auto_pointer_free(protect);
4016 * ... gtk_list_store_set() now manages the data. When
4017 * ... *explicitly* requesting a pointer from the list
4018 * ... store, don't forget you get a copy that should be
4019 * ... freed with g_auto_pointer_free() eventually.
4023 *\param pointer Pointer to be guarded.
4025 *\return GAuto * Pointer that should be used in containers with
4028 GAuto *g_auto_pointer_new(gpointer p)
4030 AutoPointerRef *ref;
4036 ref = g_new0(AutoPointerRef, 1);
4037 ptr = g_new0(AutoPointer, 1);
4047 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4053 *\brief Allocate an autopointer using the passed \a free function to
4054 * free the guarded pointer
4056 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4063 aptr = g_auto_pointer_new(p);
4064 aptr->ref->free = free_;
4068 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4070 if (auto_ptr == NULL)
4072 return ((AutoPointer *) auto_ptr)->ptr;
4076 *\brief Copies an auto pointer by. It's mostly not necessary
4077 * to call this function directly, unless you copy/assign
4078 * the guarded pointer.
4080 *\param auto_ptr Auto pointer returned by previous call to
4081 * g_auto_pointer_new_XXX()
4083 *\return gpointer An auto pointer
4085 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4088 AutoPointerRef *ref;
4091 if (auto_ptr == NULL)
4096 newp = g_new0(AutoPointer, 1);
4099 newp->ptr = ref->pointer;
4103 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4109 *\brief Free an auto pointer
4111 void g_auto_pointer_free(GAuto *auto_ptr)
4114 AutoPointerRef *ref;
4116 if (auto_ptr == NULL)
4122 if (--(ref->cnt) == 0) {
4124 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4126 ref->free(ref->pointer);
4131 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4136 void replace_returns(gchar *str)
4141 while (strstr(str, "\n")) {
4142 *strstr(str, "\n") = ' ';
4144 while (strstr(str, "\r")) {
4145 *strstr(str, "\r") = ' ';
4149 /* get_uri_part() - retrieves a URI starting from scanpos.
4150 Returns TRUE if succesful */
4151 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4152 const gchar **bp, const gchar **ep, gboolean hdr)
4155 gint parenthese_cnt = 0;
4157 g_return_val_if_fail(start != NULL, FALSE);
4158 g_return_val_if_fail(scanpos != NULL, FALSE);
4159 g_return_val_if_fail(bp != NULL, FALSE);
4160 g_return_val_if_fail(ep != NULL, FALSE);
4164 /* find end point of URI */
4165 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4166 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4167 !IS_ASCII(*(const guchar *)ep_) ||
4168 strchr("[]{}<>\"", *ep_)) {
4170 } else if (strchr("(", *ep_)) {
4172 } else if (strchr(")", *ep_)) {
4173 if (parenthese_cnt > 0)
4180 /* no punctuation at end of string */
4182 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4183 * should pass some URI type to this function and decide on that whether
4184 * to perform punctuation stripping */
4186 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4188 for (; ep_ - 1 > scanpos + 1 &&
4189 IS_REAL_PUNCT(*(ep_ - 1));
4193 #undef IS_REAL_PUNCT
4200 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4202 while (bp && *bp && g_ascii_isspace(*bp))
4204 return g_strndup(bp, ep - bp);
4207 /* valid mail address characters */
4208 #define IS_RFC822_CHAR(ch) \
4212 !g_ascii_isspace(ch) && \
4213 !strchr("(),;<>\"", (ch)))
4215 /* alphabet and number within 7bit ASCII */
4216 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4217 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4219 static GHashTable *create_domain_tab(void)
4221 static const gchar *toplvl_domains [] = {
4223 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4224 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4225 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4226 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4227 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4228 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4229 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4230 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4231 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4232 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4233 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4234 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4235 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4236 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4237 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4238 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4239 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4240 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4241 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4242 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4243 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4244 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4245 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4246 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4247 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4248 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4251 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4253 g_return_val_if_fail(htab, NULL);
4254 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4255 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4259 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4261 const gint MAX_LVL_DOM_NAME_LEN = 6;
4262 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4263 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4266 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4269 for (p = buf; p < m && first < last; *p++ = *first++)
4273 return g_hash_table_lookup(tab, buf) != NULL;
4276 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4277 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4278 const gchar **bp, const gchar **ep, gboolean hdr)
4280 /* more complex than the uri part because we need to scan back and forward starting from
4281 * the scan position. */
4282 gboolean result = FALSE;
4283 const gchar *bp_ = NULL;
4284 const gchar *ep_ = NULL;
4285 static GHashTable *dom_tab;
4286 const gchar *last_dot = NULL;
4287 const gchar *prelast_dot = NULL;
4288 const gchar *last_tld_char = NULL;
4290 /* the informative part of the email address (describing the name
4291 * of the email address owner) may contain quoted parts. the
4292 * closure stack stores the last encountered quotes. */
4293 gchar closure_stack[128];
4294 gchar *ptr = closure_stack;
4296 g_return_val_if_fail(start != NULL, FALSE);
4297 g_return_val_if_fail(scanpos != NULL, FALSE);
4298 g_return_val_if_fail(bp != NULL, FALSE);
4299 g_return_val_if_fail(ep != NULL, FALSE);
4302 const gchar *start_quote = NULL;
4303 const gchar *end_quote = NULL;
4305 /* go to the real start */
4306 if (start[0] == ',')
4308 if (start[0] == ';')
4310 while (start[0] == '\n' || start[0] == '\r')
4312 while (start[0] == ' ' || start[0] == '\t')
4317 /* check if there are quotes (to skip , in them) */
4318 if (*start == '"') {
4319 start_quote = start;
4321 end_quote = strstr(start, "\"");
4327 /* skip anything between quotes */
4328 if (start_quote && end_quote) {
4333 /* find end (either , or ; or end of line) */
4334 if (strstr(start, ",") && strstr(start, ";"))
4335 *ep = strstr(start,",") < strstr(start, ";")
4336 ? strstr(start, ",") : strstr(start, ";");
4337 else if (strstr(start, ","))
4338 *ep = strstr(start, ",");
4339 else if (strstr(start, ";"))
4340 *ep = strstr(start, ";");
4342 *ep = start+strlen(start);
4344 /* go back to real start */
4345 if (start_quote && end_quote) {
4346 start = start_quote;
4349 /* check there's still an @ in that, or search
4350 * further if possible */
4351 if (strstr(start, "@") && strstr(start, "@") < *ep)
4353 else if (*ep < start+strlen(start)) {
4356 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4364 dom_tab = create_domain_tab();
4365 g_return_val_if_fail(dom_tab, FALSE);
4367 /* scan start of address */
4368 for (bp_ = scanpos - 1;
4369 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4372 /* TODO: should start with an alnum? */
4374 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4377 if (bp_ != scanpos) {
4378 /* scan end of address */
4379 for (ep_ = scanpos + 1;
4380 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4382 prelast_dot = last_dot;
4384 if (*(last_dot + 1) == '.') {
4385 if (prelast_dot == NULL)
4387 last_dot = prelast_dot;
4392 /* TODO: really should terminate with an alnum? */
4393 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4398 if (last_dot == NULL)
4400 if (last_dot >= ep_)
4401 last_dot = prelast_dot;
4402 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4406 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4407 if (*last_tld_char == '?')
4410 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4417 if (!result) return FALSE;
4419 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4420 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4421 && IS_RFC822_CHAR(*(ep_ + 3))) {
4422 /* this informative part with an @ in it is
4423 * followed by the email address */
4426 /* go to matching '>' (or next non-rfc822 char, like \n) */
4427 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4430 /* include the bracket */
4431 if (*ep_ == '>') ep_++;
4433 /* include the leading quote */
4441 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4442 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4445 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4446 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4449 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4450 #define IN_STACK() (ptr > closure_stack)
4451 /* has underrun check */
4452 #define POP_STACK() if(IN_STACK()) --ptr
4453 /* has overrun check */
4454 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4455 /* has underrun check */
4456 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4460 /* scan for the informative part. */
4461 for (bp_ -= 2; bp_ >= start; bp_--) {
4462 /* if closure on the stack keep scanning */
4463 if (PEEK_STACK() == *bp_) {
4467 if (*bp_ == '\'' || *bp_ == '"') {
4472 /* if nothing in the closure stack, do the special conditions
4473 * the following if..else expression simply checks whether
4474 * a token is acceptable. if not acceptable, the clause
4475 * should terminate the loop with a 'break' */
4476 if (!PEEK_STACK()) {
4478 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4479 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4480 /* hyphens are allowed, but only in
4482 } else if (strchr(" \"'", *bp_)) {
4483 /* but anything not being a punctiation
4486 break; /* anything else is rejected */
4493 /* scan forward (should start with an alnum) */
4494 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4510 #undef IS_ASCII_ALNUM
4511 #undef IS_RFC822_CHAR
4513 gchar *make_email_string(const gchar *bp, const gchar *ep)
4515 /* returns a mailto: URI; mailto: is also used to detect the
4516 * uri type later on in the button_pressed signal handler */
4520 tmp = g_strndup(bp, ep - bp);
4521 result = g_strconcat("mailto:", tmp, NULL);
4527 gchar *make_http_string(const gchar *bp, const gchar *ep)
4529 /* returns an http: URI; */
4533 while (bp && *bp && g_ascii_isspace(*bp))
4535 tmp = g_strndup(bp, ep - bp);
4536 result = g_strconcat("http://", tmp, NULL);
4542 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4544 FILE *fp = fopen(path, "rb");
4545 gchar buf[BUFFSIZE];
4546 gchar *result = NULL;
4549 while (fgets(buf, sizeof (buf), fp) != NULL) {
4550 gchar **parts = g_strsplit(buf, ";", 3);
4551 gchar *trimmed = parts[0];
4552 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4554 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4555 trimmed[strlen(trimmed)-1] = '\0';
4557 if (!strcmp(trimmed, type)) {
4558 gboolean needsterminal = FALSE;
4559 if (parts[2] && strstr(parts[2], "needsterminal")) {
4560 needsterminal = TRUE;
4562 if (parts[2] && strstr(parts[2], "test=")) {
4563 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4564 gchar *testcmd = orig_testcmd;
4565 if (strstr(testcmd,";"))
4566 *(strstr(testcmd,";")) = '\0';
4567 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4569 while (testcmd[strlen(testcmd)-1] == '\n')
4570 testcmd[strlen(testcmd)-1] = '\0';
4571 while (testcmd[strlen(testcmd)-1] == '\r')
4572 testcmd[strlen(testcmd)-1] = '\0';
4573 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4574 testcmd[strlen(testcmd)-1] = '\0';
4576 if (strstr(testcmd, "%s")) {
4577 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4578 gint res = system(tmp);
4580 g_free(orig_testcmd);
4587 gint res = system(testcmd);
4588 g_free(orig_testcmd);
4598 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4600 while (trimmed[strlen(trimmed)-1] == '\n')
4601 trimmed[strlen(trimmed)-1] = '\0';
4602 while (trimmed[strlen(trimmed)-1] == '\r')
4603 trimmed[strlen(trimmed)-1] = '\0';
4604 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4605 trimmed[strlen(trimmed)-1] = '\0';
4606 result = g_strdup(trimmed);
4609 /* if there are no single quotes around %s, add them.
4610 * '.*%s.*' is ok, as in display 'png:%s'
4612 if (strstr(result, "%s")
4613 && !(strstr(result, "'") < strstr(result,"%s") &&
4614 strstr(strstr(result,"%s"), "'"))) {
4615 gchar *start = g_strdup(result);
4616 gchar *end = g_strdup(strstr(result, "%s")+2);
4618 *strstr(start, "%s") = '\0';
4619 tmp = g_strconcat(start,"'%s'",end, NULL);
4625 if (needsterminal) {
4626 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4637 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4639 gchar *result = NULL;
4643 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4644 result = mailcap_get_command_in_file(path, type, file_to_open);
4648 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4652 void mailcap_update_default(const gchar *type, const gchar *command)
4654 gchar *path = NULL, *outpath = NULL;
4655 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4656 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4657 FILE *fp = fopen(path, "rb");
4658 FILE *outfp = fopen(outpath, "wb");
4659 gchar buf[BUFFSIZE];
4667 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4668 gchar **parts = g_strsplit(buf, ";", 3);
4669 gchar *trimmed = parts[0];
4670 while (trimmed[0] == ' ')
4672 while (trimmed[strlen(trimmed)-1] == ' ')
4673 trimmed[strlen(trimmed)-1] = '\0';
4675 if (!strcmp(trimmed, type)) {
4684 fprintf(outfp, "%s; %s\n", type, command);
4690 g_rename(outpath, path);
4693 gint copy_dir(const gchar *src, const gchar *dst)
4698 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4699 g_warning("failed to open directory: %s\n", src);
4703 if (make_dir(dst) < 0)
4706 while ((name = g_dir_read_name(dir)) != NULL) {
4707 gchar *old_file, *new_file;
4708 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4709 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4710 debug_print("copying: %s -> %s\n", old_file, new_file);
4711 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4712 gint r = copy_file(old_file, new_file, TRUE);
4717 } else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4720 gchar *target = g_file_read_link(old_file, &error);
4722 r = symlink(target, new_file);
4728 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4729 gint r = copy_dir(old_file, new_file);
4740 /* crude test to see if a file is an email. */
4741 gboolean file_is_email (const gchar *filename)
4747 if (filename == NULL)
4749 if ((fp = g_fopen(filename, "rb")) == NULL)
4751 while (i < 60 && score < 3
4752 && fgets(buffer, sizeof (buffer), fp) > 0) {
4753 if (!strncmp(buffer, "From:", strlen("From:")))
4755 if (!strncmp(buffer, "To:", strlen("To:")))
4757 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4762 return (score >= 3);
4765 gboolean sc_g_list_bigger(GList *list, gint max)
4769 while (cur && i <= max+1) {
4776 gboolean sc_g_slist_bigger(GSList *list, gint max)
4780 while (cur && i <= max+1) {
4787 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4788 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4789 NULL, NULL, NULL, NULL, NULL, NULL};
4790 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4791 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4792 NULL, NULL, NULL, NULL, NULL, NULL};
4793 const gchar *s_am_up = NULL;
4794 const gchar *s_pm_up = NULL;
4795 const gchar *s_am_low = NULL;
4796 const gchar *s_pm_low = NULL;
4797 const gchar *def_loc_format = NULL;
4798 const gchar *date_loc_format = NULL;
4799 const gchar *time_loc_format = NULL;
4800 const gchar *time_am_pm = NULL;
4802 static gboolean time_names_init_done = FALSE;
4804 static void init_time_names(void)
4806 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4807 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4808 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4809 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4810 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4811 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4812 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4814 monthnames[0] = Q_("Complete month name for use by strftime|January");
4815 monthnames[1] = Q_("Complete month name for use by strftime|February");
4816 monthnames[2] = Q_("Complete month name for use by strftime|March");
4817 monthnames[3] = Q_("Complete month name for use by strftime|April");
4818 monthnames[4] = Q_("Complete month name for use by strftime|May");
4819 monthnames[5] = Q_("Complete month name for use by strftime|June");
4820 monthnames[6] = Q_("Complete month name for use by strftime|July");
4821 monthnames[7] = Q_("Complete month name for use by strftime|August");
4822 monthnames[8] = Q_("Complete month name for use by strftime|September");
4823 monthnames[9] = Q_("Complete month name for use by strftime|October");
4824 monthnames[10] = Q_("Complete month name for use by strftime|November");
4825 monthnames[11] = Q_("Complete month name for use by strftime|December");
4827 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4828 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4829 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4830 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4831 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4832 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4833 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4835 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4836 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4837 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4838 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4839 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4840 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4841 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4842 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4843 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4844 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4845 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4846 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4848 s_am_up = Q_("For use by strftime (morning)|AM");
4849 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4850 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4851 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4853 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4854 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4855 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4857 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4859 time_names_init_done = TRUE;
4863 time_t now = time(NULL), next;
4864 gchar buf_a[1024], buf_b[1024];
4866 printf("test strftime:\n");
4868 for (next = now; next < now + 86400*365*2; next++) {
4869 lt = localtime(&now);
4870 strftime(buf_a, 1024,
4871 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4872 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4873 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4874 fast_strftime(buf_b, 1024,
4875 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4876 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4877 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4878 if (strcmp(buf_a, buf_b)) {
4879 printf("diff: \n%s\n%s\n", buf_a, buf_b);
4886 #define CHECK_SIZE() { \
4887 total_done += len; \
4888 if (total_done >= buflen) { \
4889 buf[buflen-1] = '\0'; \
4894 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4896 gchar *curpos = buf;
4897 gint total_done = 0;
4898 gchar subbuf[64], subfmt[64];
4899 static time_t last_tzset = (time_t)0;
4901 if (!time_names_init_done)
4904 if (format == NULL || lt == NULL)
4907 if (last_tzset != time(NULL)) {
4909 last_tzset = time(NULL);
4912 if (*format == '%') {
4913 gint len = 0, tmp = 0;
4917 len = 1; CHECK_SIZE();
4921 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4922 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4925 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4926 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4930 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4931 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4934 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4935 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4938 fast_strftime(subbuf, 64, def_loc_format, lt);
4939 len = strlen(subbuf); CHECK_SIZE();
4940 strncpy2(curpos, subbuf, buflen - total_done);
4943 total_done += 2; CHECK_SIZE();
4944 tmp = (lt->tm_year + 1900)/100;
4945 *curpos++ = '0'+(tmp / 10);
4946 *curpos++ = '0'+(tmp % 10);
4949 total_done += 2; CHECK_SIZE();
4950 *curpos++ = '0'+(lt->tm_mday / 10);
4951 *curpos++ = '0'+(lt->tm_mday % 10);
4954 total_done += 8; CHECK_SIZE();
4955 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4956 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4958 *curpos++ = '0'+(lt->tm_mday / 10);
4959 *curpos++ = '0'+(lt->tm_mday % 10);
4961 tmp = lt->tm_year%100;
4962 *curpos++ = '0'+(tmp / 10);
4963 *curpos++ = '0'+(tmp % 10);
4966 len = 2; CHECK_SIZE();
4967 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4970 len = 10; CHECK_SIZE();
4971 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4972 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4975 total_done += 2; CHECK_SIZE();
4976 *curpos++ = '0'+(lt->tm_hour / 10);
4977 *curpos++ = '0'+(lt->tm_hour % 10);
4980 total_done += 2; CHECK_SIZE();
4986 *curpos++ = '0'+(tmp / 10);
4987 *curpos++ = '0'+(tmp % 10);
4990 len = 3; CHECK_SIZE();
4991 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4994 len = 2; CHECK_SIZE();
4995 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4998 len = 2; CHECK_SIZE();
5004 snprintf(curpos, buflen - total_done, "%2d", tmp);
5007 total_done += 2; CHECK_SIZE();
5008 tmp = lt->tm_mon + 1;
5009 *curpos++ = '0'+(tmp / 10);
5010 *curpos++ = '0'+(tmp % 10);
5013 total_done += 2; CHECK_SIZE();
5014 *curpos++ = '0'+(lt->tm_min / 10);
5015 *curpos++ = '0'+(lt->tm_min % 10);
5018 len = 1; CHECK_SIZE();
5022 if (lt->tm_hour >= 12) {
5023 len = strlen(s_pm_up); CHECK_SIZE();
5024 snprintf(curpos, buflen-total_done, s_pm_up);
5026 len = strlen(s_am_up); CHECK_SIZE();
5027 snprintf(curpos, buflen-total_done, s_am_up);
5031 if (lt->tm_hour >= 12) {
5032 len = strlen(s_pm_low); CHECK_SIZE();
5033 snprintf(curpos, buflen-total_done, s_pm_low);
5035 len = strlen(s_am_low); CHECK_SIZE();
5036 snprintf(curpos, buflen-total_done, s_am_low);
5040 fast_strftime(subbuf, 64, time_am_pm, lt);
5041 len = strlen(subbuf); CHECK_SIZE();
5042 strncpy2(curpos, subbuf, buflen - total_done);
5045 total_done += 5; CHECK_SIZE();
5046 *curpos++ = '0'+(lt->tm_hour / 10);
5047 *curpos++ = '0'+(lt->tm_hour % 10);
5049 *curpos++ = '0'+(lt->tm_min / 10);
5050 *curpos++ = '0'+(lt->tm_min % 10);
5053 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5054 len = strlen(subbuf); CHECK_SIZE();
5055 strncpy2(curpos, subbuf, buflen - total_done);
5058 total_done += 2; CHECK_SIZE();
5059 *curpos++ = '0'+(lt->tm_sec / 10);
5060 *curpos++ = '0'+(lt->tm_sec % 10);
5063 len = 1; CHECK_SIZE();
5067 total_done += 8; CHECK_SIZE();
5068 *curpos++ = '0'+(lt->tm_hour / 10);
5069 *curpos++ = '0'+(lt->tm_hour % 10);
5071 *curpos++ = '0'+(lt->tm_min / 10);
5072 *curpos++ = '0'+(lt->tm_min % 10);
5074 *curpos++ = '0'+(lt->tm_sec / 10);
5075 *curpos++ = '0'+(lt->tm_sec % 10);
5078 len = 1; CHECK_SIZE();
5079 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5082 len = 1; CHECK_SIZE();
5083 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5086 fast_strftime(subbuf, 64, date_loc_format, lt);
5087 len = strlen(subbuf); CHECK_SIZE();
5088 strncpy2(curpos, subbuf, buflen - total_done);
5091 fast_strftime(subbuf, 64, time_loc_format, lt);
5092 len = strlen(subbuf); CHECK_SIZE();
5093 strncpy2(curpos, subbuf, buflen - total_done);
5096 total_done += 2; CHECK_SIZE();
5097 tmp = lt->tm_year%100;
5098 *curpos++ = '0'+(tmp / 10);
5099 *curpos++ = '0'+(tmp % 10);
5102 len = 4; CHECK_SIZE();
5103 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5113 /* let these complicated ones be done with the libc */
5114 snprintf(subfmt, 64, "%%%c", *format);
5115 strftime(subbuf, 64, subfmt, lt);
5116 len = strlen(subbuf); CHECK_SIZE();
5117 strncpy2(curpos, subbuf, buflen - total_done);
5121 /* let these complicated modifiers be done with the libc */
5122 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5123 strftime(subbuf, 64, subfmt, lt);
5124 len = strlen(subbuf); CHECK_SIZE();
5125 strncpy2(curpos, subbuf, buflen - total_done);
5129 g_warning("format error (%c)", *format);
5136 int len = 1; CHECK_SIZE();
5137 *curpos++ = *format++;