2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
46 #include <sys/types.h>
48 # include <sys/wait.h>
55 #include <sys/utsname.h>
66 #include "../codeconv.h"
70 static gboolean debug_mode = FALSE;
72 static GSList *tempfiles=NULL;
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
80 if (G_WIN32_HAVE_WIDECHAR_API()) {
85 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
91 retval = _wchdir(wpath);
103 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104 if (cp_path == NULL) {
109 retval = chdir(cp_path);
122 gint g_chmod(const gchar *path, gint mode)
125 if (G_WIN32_HAVE_WIDECHAR_API()) {
130 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
136 retval = _wchmod(wpath, mode);
148 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149 if (cp_path == NULL) {
154 retval = chmod(cp_path, mode);
163 return chmod(path, mode);
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
170 gint mkstemp_name(const gchar *template, gchar **name_used)
172 static gulong count=0; /* W32-_mktemp only supports up to 27
176 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
178 (S_IRUSR | S_IWUSR));
180 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
182 perror(g_strdup_printf("cant create %s",*name_used));
188 #endif /* G_OS_WIN32 */
191 gint mkstemp(const gchar *template)
194 gint res = mkstemp_name(template, &dummyname);
198 #endif /* G_OS_WIN32 */
200 void list_free_strings(GList *list)
202 list = g_list_first(list);
204 while (list != NULL) {
210 void slist_free_strings(GSList *list)
212 while (list != NULL) {
218 GSList *slist_concat_unique (GSList *first, GSList *second)
226 } else if (second == NULL)
229 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230 if (g_slist_find(ret, tmp->data) == NULL)
231 ret = g_slist_prepend(ret, tmp->data);
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
241 void hash_free_strings(GHashTable *table)
243 g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
252 void hash_free_value_mem(GHashTable *table)
254 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
259 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
262 guint str_case_hash(gconstpointer key)
264 const gchar *p = key;
268 h = g_ascii_tolower(h);
269 for (p += 1; *p != '\0'; p++)
270 h = (h << 5) - h + g_ascii_tolower(*p);
276 void ptr_array_free_strings(GPtrArray *array)
281 g_return_if_fail(array != NULL);
283 for (i = 0; i < array->len; i++) {
284 str = g_ptr_array_index(array, i);
289 gboolean str_find(const gchar *haystack, const gchar *needle)
291 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
296 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
301 return strcmp(haystack, needle) == 0;
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
306 return g_ascii_strcasecmp(haystack, needle) == 0;
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 #define divide(num,divisor,i,d) \
343 if (d > 1000) d /= 1000; \
344 else if (d > 100) d /= 100; \
345 else if (d > 10) d /= 10; \
348 gchar *to_human_readable(off_t size)
350 static gchar str[14];
351 static gchar *b_format = NULL, *kb_format = NULL,
352 *mb_format = NULL, *gb_format = NULL;
353 register int t = 0, r = 0;
354 if (b_format == NULL) {
356 kb_format = _("%d.%dKB");
357 mb_format = _("%.2fMB");
358 gb_format = _("%.2fGB");
362 g_snprintf(str, sizeof(str), b_format, (gint)size);
364 } else if (size >> 10 < 1024) {
365 divide(size, (1 << 10), t, r);
366 g_snprintf(str, sizeof(str), kb_format, t, r);
368 } else if (size >> 20 < 1024) {
369 g_snprintf(str, sizeof(str), mb_format, (gfloat)size / (1 << 20));
372 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
377 /* strcmp with NULL-checking */
378 gint strcmp2(const gchar *s1, const gchar *s2)
380 if (s1 == NULL || s2 == NULL)
383 return strcmp(s1, s2);
385 /* strstr with NULL-checking */
386 gchar *strstr2(const gchar *s1, const gchar *s2)
388 if (s1 == NULL || s2 == NULL)
391 return strstr(s1, s2);
394 gint path_cmp(const gchar *s1, const gchar *s2)
399 gchar *s1buf, *s2buf;
402 if (s1 == NULL || s2 == NULL) return -1;
403 if (*s1 == '\0' || *s2 == '\0') return -1;
406 s1buf = g_strdup (s1);
407 s2buf = g_strdup (s2);
408 subst_char (s1buf, '/', G_DIR_SEPARATOR);
409 subst_char (s2buf, '/', G_DIR_SEPARATOR);
412 #endif /* !G_OS_WIN32 */
417 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
418 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
420 rc = strncmp(s1, s2, MAX(len1, len2));
424 #endif /* !G_OS_WIN32 */
428 /* remove trailing return code */
429 gchar *strretchomp(gchar *str)
433 if (!*str) return str;
435 for (s = str + strlen(str) - 1;
436 s >= str && (*s == '\n' || *s == '\r');
443 /* remove trailing character */
444 gchar *strtailchomp(gchar *str, gchar tail_char)
448 if (!*str) return str;
449 if (tail_char == '\0') return str;
451 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
457 /* remove CR (carriage return) */
458 gchar *strcrchomp(gchar *str)
462 if (!*str) return str;
464 s = str + strlen(str) - 1;
465 if (*s == '\n' && s > str && *(s - 1) == '\r') {
473 void file_strip_crs(const gchar *file)
475 FILE *fp = NULL, *outfp = NULL;
477 gchar *out = get_tmp_file();
481 fp = fopen(file, "rb");
485 outfp = fopen(out, "wb");
491 while (fgets(buf, sizeof (buf), fp) != NULL) {
498 rename_force(out, file);
503 /* Similar to `strstr' but this function ignores the case of both strings. */
504 gchar *strcasestr(const gchar *haystack, const gchar *needle)
506 register size_t haystack_len, needle_len;
508 haystack_len = strlen(haystack);
509 needle_len = strlen(needle);
511 if (haystack_len < needle_len || needle_len == 0)
514 while (haystack_len >= needle_len) {
515 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
516 return (gchar *)haystack;
526 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
527 gconstpointer needle, size_t needlelen)
529 const gchar *haystack_ = (const gchar *)haystack;
530 const gchar *needle_ = (const gchar *)needle;
531 const gchar *haystack_cur = (const gchar *)haystack;
534 return memchr(haystack_, *needle_, haystacklen);
536 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
538 if (haystacklen - (haystack_cur - haystack_) < needlelen)
540 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
541 return (gpointer)haystack_cur;
549 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
550 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
552 register const gchar *s = src;
553 register gchar *d = dest;
563 /* Examine if next block is non-ASCII string */
564 gboolean is_next_nonascii(const gchar *s)
568 /* skip head space */
569 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
571 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
572 if (*(guchar *)p > 127 || *(guchar *)p < 32)
579 gint get_next_word_len(const gchar *s)
583 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
589 /* compare subjects */
590 gint subject_compare(const gchar *s1, const gchar *s2)
594 if (!s1 || !s2) return -1;
595 if (!*s1 || !*s2) return -1;
597 Xstrdup_a(str1, s1, return -1);
598 Xstrdup_a(str2, s2, return -1);
600 trim_subject_for_compare(str1);
601 trim_subject_for_compare(str2);
603 if (!*str1 || !*str2) return -1;
605 return strcmp(str1, str2);
608 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
612 if (!s1 || !s2) return -1;
614 Xstrdup_a(str1, s1, return -1);
615 Xstrdup_a(str2, s2, return -1);
617 trim_subject_for_sort(str1);
618 trim_subject_for_sort(str2);
620 return g_utf8_collate(str1, str2);
623 void trim_subject_for_compare(gchar *str)
627 eliminate_parenthesis(str, '[', ']');
628 eliminate_parenthesis(str, '(', ')');
631 srcp = str + subject_get_prefix_length(str);
633 memmove(str, srcp, strlen(srcp) + 1);
636 void trim_subject_for_sort(gchar *str)
642 srcp = str + subject_get_prefix_length(str);
644 memmove(str, srcp, strlen(srcp) + 1);
647 void trim_subject(gchar *str)
649 register gchar *srcp;
655 srcp = str + subject_get_prefix_length(str);
660 } else if (*srcp == '(') {
672 else if (*srcp == cl)
679 while (g_ascii_isspace(*srcp)) srcp++;
680 memmove(str, srcp, strlen(srcp) + 1);
683 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
685 register gchar *srcp, *destp;
690 while ((destp = strchr(destp, op))) {
696 else if (*srcp == cl)
702 while (g_ascii_isspace(*srcp)) srcp++;
703 memmove(destp, srcp, strlen(srcp) + 1);
707 void extract_parenthesis(gchar *str, gchar op, gchar cl)
709 register gchar *srcp, *destp;
714 while ((srcp = strchr(destp, op))) {
717 memmove(destp, srcp + 1, strlen(srcp));
722 else if (*destp == cl)
734 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
737 register gchar *srcp, *destp;
739 gboolean in_quote = FALSE;
743 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
746 memmove(destp, srcp + 1, strlen(srcp));
749 if (*destp == op && !in_quote)
751 else if (*destp == cl && !in_quote)
753 else if (*destp == quote_chr)
765 void eliminate_quote(gchar *str, gchar quote_chr)
767 register gchar *srcp, *destp;
771 while ((destp = strchr(destp, quote_chr))) {
772 if ((srcp = strchr(destp + 1, quote_chr))) {
774 while (g_ascii_isspace(*srcp)) srcp++;
775 memmove(destp, srcp, strlen(srcp) + 1);
783 void extract_quote(gchar *str, gchar quote_chr)
787 if ((str = strchr(str, quote_chr))) {
789 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
790 memmove(p - 1, p, strlen(p) + 1);
795 memmove(str, str + 1, p - str);
800 void eliminate_address_comment(gchar *str)
802 register gchar *srcp, *destp;
807 while ((destp = strchr(destp, '"'))) {
808 if ((srcp = strchr(destp + 1, '"'))) {
813 while (g_ascii_isspace(*srcp)) srcp++;
814 memmove(destp, srcp, strlen(srcp) + 1);
824 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
830 else if (*srcp == ')')
836 while (g_ascii_isspace(*srcp)) srcp++;
837 memmove(destp, srcp, strlen(srcp) + 1);
841 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
843 gboolean in_quote = FALSE;
846 if (*str == c && !in_quote)
848 if (*str == quote_chr)
856 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
858 gboolean in_quote = FALSE;
861 p = str + strlen(str) - 1;
863 if (*p == c && !in_quote)
873 void extract_address(gchar *str)
875 eliminate_address_comment(str);
876 if (strchr_with_skip_quote(str, '"', '<'))
877 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
881 void extract_list_id_str(gchar *str)
883 if (strchr_with_skip_quote(str, '"', '<'))
884 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
888 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
893 if (!str) return addr_list;
895 Xstrdup_a(work, str, return addr_list);
898 eliminate_address_comment(work);
901 while (workp && *workp) {
904 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
910 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
911 extract_parenthesis_with_skip_quote
912 (workp, '"', '<', '>');
916 addr_list = g_slist_append(addr_list, g_strdup(workp));
924 GSList *address_list_append(GSList *addr_list, const gchar *str)
926 return address_list_append_real(addr_list, str, TRUE);
929 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
931 return address_list_append_real(addr_list, str, FALSE);
934 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
938 if (!str) return msgid_list;
941 while (strp && *strp) {
942 const gchar *start, *end;
945 if ((start = strchr(strp, '<')) != NULL) {
946 end = strchr(start + 1, '>');
951 msgid = g_strndup(start + 1, end - start - 1);
954 msgid_list = g_slist_prepend(msgid_list, msgid);
964 GSList *references_list_append(GSList *msgid_list, const gchar *str)
968 list = references_list_prepend(NULL, str);
969 list = g_slist_reverse(list);
970 msgid_list = g_slist_concat(msgid_list, list);
975 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
980 if (!str) return group_list;
982 Xstrdup_a(work, str, return group_list);
986 while (workp && *workp) {
989 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
997 group_list = g_slist_append(group_list,
1006 GList *add_history(GList *list, const gchar *str)
1010 g_return_val_if_fail(str != NULL, list);
1012 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1015 list = g_list_remove(list, old->data);
1016 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1019 last = g_list_last(list);
1022 list = g_list_remove(list, last->data);
1026 list = g_list_prepend(list, g_strdup(str));
1031 void remove_return(gchar *str)
1033 register gchar *p = str;
1036 if (*p == '\n' || *p == '\r')
1037 memmove(p, p + 1, strlen(p));
1043 void remove_space(gchar *str)
1045 register gchar *p = str;
1050 while (g_ascii_isspace(*(p + spc)))
1053 memmove(p, p + spc, strlen(p + spc) + 1);
1059 void unfold_line(gchar *str)
1061 register gchar *p = str;
1065 if (*p == '\n' || *p == '\r') {
1068 while (g_ascii_isspace(*(p + spc)))
1071 memmove(p, p + spc, strlen(p + spc) + 1);
1077 void subst_char(gchar *str, gchar orig, gchar subst)
1079 register gchar *p = str;
1088 void subst_chars(gchar *str, gchar *orig, gchar subst)
1090 register gchar *p = str;
1093 if (strchr(orig, *p) != NULL)
1099 void subst_for_filename(gchar *str)
1104 subst_chars(str, "\t\r\n\\/*:", '_');
1106 subst_chars(str, "\t\r\n\\/*", '_');
1110 void subst_for_shellsafe_filename(gchar *str)
1114 subst_for_filename(str);
1115 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1118 gboolean is_header_line(const gchar *str)
1120 if (str[0] == ':') return FALSE;
1122 while (*str != '\0' && *str != ' ') {
1131 gboolean is_ascii_str(const gchar *str)
1133 const guchar *p = (const guchar *)str;
1135 while (*p != '\0') {
1136 if (*p != '\t' && *p != ' ' &&
1137 *p != '\r' && *p != '\n' &&
1138 (*p < 32 || *p >= 127))
1146 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1148 const gchar *first_pos;
1149 const gchar *last_pos;
1150 const gchar *p = str;
1151 gint quote_level = -1;
1153 /* speed up line processing by only searching to the last '>' */
1154 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1155 /* skip a line if it contains a '<' before the initial '>' */
1156 if (memchr(str, '<', first_pos - str) != NULL)
1158 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1162 while (p <= last_pos) {
1163 while (p < last_pos) {
1164 if (g_ascii_isspace(*p))
1170 if (strchr(quote_chars, *p))
1172 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1173 /* any characters are allowed except '-' and space */
1175 && !strchr(quote_chars, *p)
1176 && !g_ascii_isspace(*p)
1179 if (strchr(quote_chars, *p))
1191 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1193 const gchar *p = str, *q;
1194 gint cur_line = 0, len;
1196 while ((q = strchr(p, '\n')) != NULL) {
1198 if (len > max_chars) {
1208 if (len > max_chars) {
1217 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1219 gchar * position = NULL;
1220 gchar * tmp_pos = NULL;
1223 if (quote_chars == NULL)
1226 for (i = 0; i < strlen(quote_chars); i++) {
1227 tmp_pos = strchr (str, quote_chars[i]);
1229 || (tmp_pos != NULL && position >= tmp_pos) )
1235 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1237 gchar * position = NULL;
1238 gchar * tmp_pos = NULL;
1241 if (quote_chars == NULL)
1244 for (i = 0; i < strlen(quote_chars); i++) {
1245 tmp_pos = strrchr (str, quote_chars[i]);
1247 || (tmp_pos != NULL && position <= tmp_pos) )
1253 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1255 register guint haystack_len, needle_len;
1256 gboolean in_squote = FALSE, in_dquote = FALSE;
1258 haystack_len = strlen(haystack);
1259 needle_len = strlen(needle);
1261 if (haystack_len < needle_len || needle_len == 0)
1264 while (haystack_len >= needle_len) {
1265 if (!in_squote && !in_dquote &&
1266 !strncmp(haystack, needle, needle_len))
1267 return (gchar *)haystack;
1269 /* 'foo"bar"' -> foo"bar"
1270 "foo'bar'" -> foo'bar' */
1271 if (*haystack == '\'') {
1274 else if (!in_dquote)
1276 } else if (*haystack == '\"') {
1279 else if (!in_squote)
1290 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1293 gchar quote_chr = '"';
1295 gboolean in_quote = FALSE;
1299 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1303 if (*p == op && !in_quote)
1305 else if (*p == cl && !in_quote)
1307 else if (*p == quote_chr)
1320 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1323 GSList *string_list = NULL, *slist;
1325 const gchar *s_op, *s_cl;
1328 g_return_val_if_fail(str != NULL, NULL);
1331 max_tokens = G_MAXINT;
1333 s_op = strchr_with_skip_quote(str, '"', op);
1334 if (!s_op) return NULL;
1336 s_cl = strchr_parenthesis_close(str, op, cl);
1344 new_string = g_new(gchar, len + 1);
1345 strncpy(new_string, str, len);
1346 new_string[len] = 0;
1347 string_list = g_slist_prepend(string_list, new_string);
1351 while (*str && g_ascii_isspace(*str)) str++;
1353 string_list = g_slist_prepend(string_list,
1356 s_op = strchr_with_skip_quote(str, '"', op);
1357 if (!--max_tokens || !s_op) break;
1361 s_cl = strchr_parenthesis_close(str, op, cl);
1362 } while (--max_tokens && s_cl);
1365 str_array = g_new(gchar*, n);
1369 str_array[i--] = NULL;
1370 for (slist = string_list; slist; slist = slist->next)
1371 str_array[i--] = slist->data;
1373 g_slist_free(string_list);
1378 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1381 GSList *string_list = NULL, *slist;
1382 gchar **str_array, *s, *new_str;
1383 guint i, n = 1, len;
1385 g_return_val_if_fail(str != NULL, NULL);
1386 g_return_val_if_fail(delim != NULL, NULL);
1389 max_tokens = G_MAXINT;
1391 s = strstr_with_skip_quote(str, delim);
1393 guint delimiter_len = strlen(delim);
1397 new_str = g_strndup(str, len);
1399 if (new_str[0] == '\'' || new_str[0] == '\"') {
1400 if (new_str[len - 1] == new_str[0]) {
1401 new_str[len - 1] = '\0';
1402 memmove(new_str, new_str + 1, len - 1);
1405 string_list = g_slist_prepend(string_list, new_str);
1407 str = s + delimiter_len;
1408 s = strstr_with_skip_quote(str, delim);
1409 } while (--max_tokens && s);
1413 new_str = g_strdup(str);
1414 if (new_str[0] == '\'' || new_str[0] == '\"') {
1416 if (new_str[len - 1] == new_str[0]) {
1417 new_str[len - 1] = '\0';
1418 memmove(new_str, new_str + 1, len - 1);
1421 string_list = g_slist_prepend(string_list, new_str);
1425 str_array = g_new(gchar*, n);
1429 str_array[i--] = NULL;
1430 for (slist = string_list; slist; slist = slist->next)
1431 str_array[i--] = slist->data;
1433 g_slist_free(string_list);
1438 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1440 gchar *abbrev_group;
1442 const gchar *p = group;
1445 g_return_val_if_fail(group != NULL, NULL);
1447 last = group + strlen(group);
1448 abbrev_group = ap = g_malloc(strlen(group) + 1);
1453 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1455 while (*p != '.') p++;
1458 return abbrev_group;
1463 return abbrev_group;
1466 gchar *trim_string(const gchar *str, gint len)
1468 const gchar *p = str;
1473 if (!str) return NULL;
1474 if (strlen(str) <= len)
1475 return g_strdup(str);
1476 if (g_utf8_validate(str, -1, NULL) == FALSE)
1477 return g_strdup(str);
1479 while (*p != '\0') {
1480 mb_len = g_utf8_skip[*(guchar *)p];
1483 else if (new_len + mb_len > len)
1490 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1491 return g_strconcat(new_str, "...", NULL);
1494 GList *uri_list_extract_filenames(const gchar *uri_list)
1496 GList *result = NULL;
1498 gchar *escaped_utf8uri;
1504 while (g_ascii_isspace(*p)) p++;
1505 if (!strncmp(p, "file:", 5)) {
1508 while (*q && *q != '\n' && *q != '\r') q++;
1511 gchar *file, *locale_file = NULL;
1513 while (q > p && g_ascii_isspace(*q))
1515 Xalloca(escaped_utf8uri, q - p + 2,
1517 Xalloca(file, q - p + 2,
1520 strncpy(escaped_utf8uri, p, q - p + 1);
1521 escaped_utf8uri[q - p + 1] = '\0';
1522 decode_uri(file, escaped_utf8uri);
1524 * g_filename_from_uri() rejects escaped/locale encoded uri
1525 * string which come from Nautilus.
1527 if (g_utf8_validate(file, -1, NULL))
1529 = conv_codeset_strdup(
1532 conv_get_locale_charset_str());
1534 locale_file = g_strdup(file + 5);
1535 result = g_list_append(result, locale_file);
1539 p = strchr(p, '\n');
1546 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1549 static gint axtoi(const gchar *hexstr)
1551 gint hi, lo, result;
1554 if ('0' <= hi && hi <= '9') {
1557 if ('a' <= hi && hi <= 'f') {
1560 if ('A' <= hi && hi <= 'F') {
1565 if ('0' <= lo && lo <= '9') {
1568 if ('a' <= lo && lo <= 'f') {
1571 if ('A' <= lo && lo <= 'F') {
1574 result = lo + (16 * hi);
1578 gboolean is_uri_string(const gchar *str)
1580 while (str && *str && g_ascii_isspace(*str))
1582 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1583 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1584 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1585 g_ascii_strncasecmp(str, "www.", 4) == 0);
1588 gchar *get_uri_path(const gchar *uri)
1590 while (uri && *uri && g_ascii_isspace(*uri))
1592 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1593 return (gchar *)(uri + 7);
1594 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1595 return (gchar *)(uri + 8);
1596 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1597 return (gchar *)(uri + 6);
1599 return (gchar *)uri;
1602 gint get_uri_len(const gchar *str)
1606 if (is_uri_string(str)) {
1607 for (p = str; *p != '\0'; p++) {
1608 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1617 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1618 * plusses, and escape characters are used)
1620 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1622 gchar *dec = decoded_uri;
1623 const gchar *enc = encoded_uri;
1628 if (isxdigit((guchar)enc[0]) &&
1629 isxdigit((guchar)enc[1])) {
1647 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1648 gchar **subject, gchar **body)
1653 Xstrdup_a(tmp_mailto, mailto, return -1);
1655 if (!strncmp(tmp_mailto, "mailto:", 7))
1658 p = strchr(tmp_mailto, '?');
1665 *to = g_strdup(tmp_mailto);
1668 gchar *field, *value;
1685 if (*value == '\0') continue;
1687 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1688 *cc = g_strdup(value);
1689 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1690 *bcc = g_strdup(value);
1691 } else if (subject && !*subject &&
1692 !g_ascii_strcasecmp(field, "subject")) {
1693 *subject = g_malloc(strlen(value) + 1);
1694 decode_uri(*subject, value);
1695 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1696 *body = g_malloc(strlen(value) + 1);
1697 decode_uri(*body, value);
1706 #include <windows.h>
1707 #ifndef CSIDL_APPDATA
1708 #define CSIDL_APPDATA 0x001a
1710 #ifndef CSIDL_LOCAL_APPDATA
1711 #define CSIDL_LOCAL_APPDATA 0x001c
1713 #ifndef CSIDL_FLAG_CREATE
1714 #define CSIDL_FLAG_CREATE 0x8000
1716 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1720 w32_strerror (int w32_errno)
1722 static char strerr[256];
1723 int ec = (int)GetLastError ();
1727 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1728 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1729 strerr, DIM (strerr)-1, NULL);
1733 static __inline__ void *
1734 dlopen (const char * name, int flag)
1736 void * hd = LoadLibrary (name);
1740 static __inline__ void *
1741 dlsym (void * hd, const char * sym)
1745 void * fnc = GetProcAddress (hd, sym);
1754 static __inline__ const char *
1757 return w32_strerror (0);
1761 static __inline__ int
1773 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1775 static int initialized;
1776 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1780 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1786 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1788 handle = dlopen (dllnames[i], RTLD_LAZY);
1791 func = dlsym (handle, "SHGetFolderPathA");
1802 return func (a,b,c,d,e);
1807 /* Returns a static string with the directroy from which the module
1808 has been loaded. Returns an empty string on error. */
1809 static char *w32_get_module_dir(void)
1811 static char *moddir;
1814 char name[MAX_PATH+10];
1817 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1820 p = strrchr (name, '\\');
1826 moddir = g_strdup (name);
1830 #endif /* G_OS_WIN32 */
1832 /* Return a static string with the locale dir. */
1833 const gchar *get_locale_dir(void)
1835 static gchar *loc_dir;
1839 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1840 "\\share\\locale", NULL);
1843 loc_dir = LOCALEDIR;
1849 const gchar *get_home_dir(void)
1852 static char home_dir[MAX_PATH] = "";
1854 if (home_dir[0] == '\0') {
1855 if (w32_shgetfolderpath
1856 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1857 NULL, 0, home_dir) < 0)
1858 strcpy (home_dir, "C:\\Sylpheed");
1862 static const gchar *homeenv = NULL;
1867 if (!homeenv && g_getenv("HOME") != NULL)
1868 homeenv = g_strdup(g_getenv("HOME"));
1870 homeenv = g_get_home_dir();
1876 const gchar *get_rc_dir(void)
1878 static gchar *rc_dir = NULL;
1881 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1887 const gchar *get_mail_base_dir(void)
1890 static gchar *mail_base_dir = NULL;
1893 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1896 return mail_base_dir;
1898 return get_home_dir();
1902 const gchar *get_news_cache_dir(void)
1904 static gchar *news_cache_dir = NULL;
1906 if (!news_cache_dir)
1907 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1908 NEWS_CACHE_DIR, NULL);
1910 return news_cache_dir;
1913 const gchar *get_imap_cache_dir(void)
1915 static gchar *imap_cache_dir = NULL;
1917 if (!imap_cache_dir)
1918 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1919 IMAP_CACHE_DIR, NULL);
1921 return imap_cache_dir;
1924 const gchar *get_mbox_cache_dir(void)
1926 static gchar *mbox_cache_dir = NULL;
1928 if (!mbox_cache_dir)
1929 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1930 MBOX_CACHE_DIR, NULL);
1932 return mbox_cache_dir;
1935 const gchar *get_mime_tmp_dir(void)
1937 static gchar *mime_tmp_dir = NULL;
1940 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1941 MIME_TMP_DIR, NULL);
1943 return mime_tmp_dir;
1946 const gchar *get_template_dir(void)
1948 static gchar *template_dir = NULL;
1951 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952 TEMPLATE_DIR, NULL);
1954 return template_dir;
1957 const gchar *get_header_cache_dir(void)
1959 static gchar *header_dir = NULL;
1962 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1963 HEADER_CACHE_DIR, NULL);
1968 /* Return the default directory for Plugins. */
1969 const gchar *get_plugin_dir(void)
1972 static gchar *plugin_dir = NULL;
1975 plugin_dir = g_strconcat(w32_get_module_dir(),
1976 "\\lib\\sylpheed-claws\\plugins\\",
1980 if (is_dir_exist(PLUGINDIR))
1983 static gchar *plugin_dir = NULL;
1985 plugin_dir = g_strconcat(get_rc_dir(),
1986 G_DIR_SEPARATOR_S, "plugins",
1987 G_DIR_SEPARATOR_S, NULL);
1993 const gchar *get_tmp_dir(void)
1995 static gchar *tmp_dir = NULL;
1998 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2004 gchar *get_tmp_file(void)
2007 static guint32 id = 0;
2009 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2010 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2015 const gchar *get_domain_name(void)
2018 static gchar *domain_name = NULL;
2024 if (uname(&uts) < 0) {
2026 domain_name = "unknown";
2028 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2029 perror("gethostbyname");
2030 domain_name = g_strdup(uts.nodename);
2032 domain_name = g_strdup(hp->h_name);
2036 debug_print("domain name = %s\n", domain_name);
2045 off_t get_file_size(const gchar *file)
2049 if (g_stat(file, &s) < 0) {
2050 FILE_OP_ERROR(file, "stat");
2057 off_t get_file_size_as_crlf(const gchar *file)
2061 gchar buf[BUFFSIZE];
2063 if ((fp = g_fopen(file, "rb")) == NULL) {
2064 FILE_OP_ERROR(file, "fopen");
2068 while (fgets(buf, sizeof(buf), fp) != NULL) {
2070 size += strlen(buf) + 2;
2074 FILE_OP_ERROR(file, "fgets");
2083 off_t get_left_file_size(FILE *fp)
2089 if ((pos = ftell(fp)) < 0) {
2093 if (fseek(fp, 0L, SEEK_END) < 0) {
2097 if ((end = ftell(fp)) < 0) {
2102 if (fseek(fp, pos, SEEK_SET) < 0) {
2110 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2117 if (g_stat(file, &s) < 0) {
2118 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2122 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2129 /* Test on whether FILE is a relative file name. This is
2130 * straightforward for Unix but more complex for Windows. */
2131 gboolean is_relative_filename(const gchar *file)
2136 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2137 return FALSE; /* Prefixed with a hostname - this can't
2138 * be a relative name. */
2140 if ( ((*file >= 'a' && *file <= 'z')
2141 || (*file >= 'A' && *file <= 'Z'))
2143 file += 2; /* Skip drive letter. */
2145 return !(*file == '\\' || *file == '/');
2147 return !(*file == G_DIR_SEPARATOR);
2152 gboolean is_dir_exist(const gchar *dir)
2157 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2160 gboolean is_file_entry_exist(const gchar *file)
2165 return g_file_test(file, G_FILE_TEST_EXISTS);
2168 gboolean dirent_is_regular_file(struct dirent *d)
2170 #ifdef HAVE_DIRENT_D_TYPE
2171 if (d->d_type == DT_REG)
2173 else if (d->d_type != DT_UNKNOWN)
2177 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2180 gboolean dirent_is_directory(struct dirent *d)
2182 #ifdef HAVE_DIRENT_D_TYPE
2183 if (d->d_type == DT_DIR)
2185 else if (d->d_type != DT_UNKNOWN)
2189 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2192 gint change_dir(const gchar *dir)
2194 gchar *prevdir = NULL;
2197 prevdir = g_get_current_dir();
2199 if (g_chdir(dir) < 0) {
2200 FILE_OP_ERROR(dir, "chdir");
2201 if (debug_mode) g_free(prevdir);
2203 } else if (debug_mode) {
2206 cwd = g_get_current_dir();
2207 if (strcmp(prevdir, cwd) != 0)
2208 g_print("current dir: %s\n", cwd);
2216 gint make_dir(const gchar *dir)
2218 if (g_mkdir(dir, S_IRWXU) < 0) {
2219 FILE_OP_ERROR(dir, "mkdir");
2222 if (g_chmod(dir, S_IRWXU) < 0)
2223 FILE_OP_ERROR(dir, "chmod");
2228 gint make_dir_hier(const gchar *dir)
2233 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2234 parent_dir = g_strndup(dir, p - dir);
2235 if (*parent_dir != '\0') {
2236 if (!is_dir_exist(parent_dir)) {
2237 if (make_dir(parent_dir) < 0) {
2246 if (!is_dir_exist(dir)) {
2247 if (make_dir(dir) < 0)
2254 gint remove_all_files(const gchar *dir)
2257 const gchar *dir_name;
2260 prev_dir = g_get_current_dir();
2262 if (g_chdir(dir) < 0) {
2263 FILE_OP_ERROR(dir, "chdir");
2268 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2269 g_warning("failed to open directory: %s\n", dir);
2274 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2275 if (g_unlink(dir_name) < 0)
2276 FILE_OP_ERROR(dir_name, "unlink");
2281 if (g_chdir(prev_dir) < 0) {
2282 FILE_OP_ERROR(prev_dir, "chdir");
2292 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2295 const gchar *dir_name;
2299 prev_dir = g_get_current_dir();
2301 if (g_chdir(dir) < 0) {
2302 FILE_OP_ERROR(dir, "chdir");
2307 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2308 g_warning("failed to open directory: %s\n", dir);
2313 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2314 file_no = to_number(dir_name);
2315 if (file_no > 0 && first <= file_no && file_no <= last) {
2316 if (is_dir_exist(dir_name))
2318 if (g_unlink(dir_name) < 0)
2319 FILE_OP_ERROR(dir_name, "unlink");
2325 if (g_chdir(prev_dir) < 0) {
2326 FILE_OP_ERROR(prev_dir, "chdir");
2336 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2339 const gchar *dir_name;
2343 prev_dir = g_get_current_dir();
2345 if (g_chdir(dir) < 0) {
2346 FILE_OP_ERROR(dir, "chdir");
2351 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2352 FILE_OP_ERROR(dir, "opendir");
2357 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2358 file_no = to_number(dir_name);
2359 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2360 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2361 if (is_dir_exist(dir_name))
2363 if (g_unlink(dir_name) < 0)
2364 FILE_OP_ERROR(dir_name, "unlink");
2370 if (g_chdir(prev_dir) < 0) {
2371 FILE_OP_ERROR(prev_dir, "chdir");
2381 gint remove_all_numbered_files(const gchar *dir)
2383 return remove_numbered_files(dir, 0, UINT_MAX);
2386 gint remove_expired_files(const gchar *dir, guint hours)
2389 const gchar *dir_name;
2393 time_t mtime, now, expire_time;
2395 prev_dir = g_get_current_dir();
2397 if (g_chdir(dir) < 0) {
2398 FILE_OP_ERROR(dir, "chdir");
2403 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2404 g_warning("failed to open directory: %s\n", dir);
2410 expire_time = hours * 60 * 60;
2412 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2413 file_no = to_number(dir_name);
2415 if (g_stat(dir_name, &s) < 0) {
2416 FILE_OP_ERROR(dir_name, "stat");
2419 if (S_ISDIR(s.st_mode))
2421 mtime = MAX(s.st_mtime, s.st_atime);
2422 if (now - mtime > expire_time) {
2423 if (g_unlink(dir_name) < 0)
2424 FILE_OP_ERROR(dir_name, "unlink");
2431 if (g_chdir(prev_dir) < 0) {
2432 FILE_OP_ERROR(prev_dir, "chdir");
2442 gint remove_dir_recursive(const gchar *dir)
2446 const gchar *dir_name;
2449 if (g_stat(dir, &s) < 0) {
2450 FILE_OP_ERROR(dir, "stat");
2451 if (ENOENT == errno) return 0;
2455 if (!S_ISDIR(s.st_mode)) {
2456 if (g_unlink(dir) < 0) {
2457 FILE_OP_ERROR(dir, "unlink");
2464 prev_dir = g_get_current_dir();
2465 /* g_print("prev_dir = %s\n", prev_dir); */
2467 if (!path_cmp(prev_dir, dir)) {
2469 if (g_chdir("..") < 0) {
2470 FILE_OP_ERROR(dir, "chdir");
2473 prev_dir = g_get_current_dir();
2476 if (g_chdir(dir) < 0) {
2477 FILE_OP_ERROR(dir, "chdir");
2482 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2483 g_warning("failed to open directory: %s\n", dir);
2489 /* remove all files in the directory */
2490 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2491 /* g_print("removing %s\n", dir_name); */
2493 if (is_dir_exist(dir_name)) {
2494 if (remove_dir_recursive(dir_name) < 0) {
2495 g_warning("can't remove directory\n");
2499 if (g_unlink(dir_name) < 0)
2500 FILE_OP_ERROR(dir_name, "unlink");
2506 if (g_chdir(prev_dir) < 0) {
2507 FILE_OP_ERROR(prev_dir, "chdir");
2514 if (g_rmdir(dir) < 0) {
2515 FILE_OP_ERROR(dir, "rmdir");
2522 gint rename_force(const gchar *oldpath, const gchar *newpath)
2525 if (!is_file_entry_exist(oldpath)) {
2529 if (is_file_exist(newpath)) {
2530 if (g_unlink(newpath) < 0)
2531 FILE_OP_ERROR(newpath, "unlink");
2534 return g_rename(oldpath, newpath);
2538 * Append src file body to the tail of dest file.
2539 * Now keep_backup has no effects.
2541 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2543 FILE *src_fp, *dest_fp;
2547 gboolean err = FALSE;
2549 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2550 FILE_OP_ERROR(src, "fopen");
2554 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2555 FILE_OP_ERROR(dest, "fopen");
2560 if (change_file_mode_rw(dest_fp, dest) < 0) {
2561 FILE_OP_ERROR(dest, "chmod");
2562 g_warning("can't change file mode\n");
2565 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2566 if (n_read < sizeof(buf) && ferror(src_fp))
2568 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2569 g_warning("writing to %s failed.\n", dest);
2577 if (ferror(src_fp)) {
2578 FILE_OP_ERROR(src, "fread");
2582 if (fclose(dest_fp) == EOF) {
2583 FILE_OP_ERROR(dest, "fclose");
2595 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2597 FILE *src_fp, *dest_fp;
2600 gchar *dest_bak = NULL;
2601 gboolean err = FALSE;
2603 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2604 FILE_OP_ERROR(src, "fopen");
2607 if (is_file_exist(dest)) {
2608 dest_bak = g_strconcat(dest, ".bak", NULL);
2609 if (rename_force(dest, dest_bak) < 0) {
2610 FILE_OP_ERROR(dest, "rename");
2617 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2618 FILE_OP_ERROR(dest, "fopen");
2621 if (rename_force(dest_bak, dest) < 0)
2622 FILE_OP_ERROR(dest_bak, "rename");
2628 if (change_file_mode_rw(dest_fp, dest) < 0) {
2629 FILE_OP_ERROR(dest, "chmod");
2630 g_warning("can't change file mode\n");
2633 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2634 if (n_read < sizeof(buf) && ferror(src_fp))
2636 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2637 g_warning("writing to %s failed.\n", dest);
2642 if (rename_force(dest_bak, dest) < 0)
2643 FILE_OP_ERROR(dest_bak, "rename");
2650 if (ferror(src_fp)) {
2651 FILE_OP_ERROR(src, "fread");
2655 if (fclose(dest_fp) == EOF) {
2656 FILE_OP_ERROR(dest, "fclose");
2663 if (rename_force(dest_bak, dest) < 0)
2664 FILE_OP_ERROR(dest_bak, "rename");
2670 if (keep_backup == FALSE && dest_bak)
2678 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2680 if (overwrite == FALSE && is_file_exist(dest)) {
2681 g_warning("move_file(): file %s already exists.", dest);
2685 if (rename_force(src, dest) == 0) return 0;
2687 if (EXDEV != errno) {
2688 FILE_OP_ERROR(src, "rename");
2692 if (copy_file(src, dest, FALSE) < 0) return -1;
2699 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2702 gint bytes_left, to_read;
2705 if (fseek(fp, offset, SEEK_SET) < 0) {
2710 bytes_left = length;
2711 to_read = MIN(bytes_left, sizeof(buf));
2713 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2714 if (n_read < to_read && ferror(fp))
2716 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2719 bytes_left -= n_read;
2720 if (bytes_left == 0)
2722 to_read = MIN(bytes_left, sizeof(buf));
2733 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2736 gboolean err = FALSE;
2738 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2739 FILE_OP_ERROR(dest, "fopen");
2743 if (change_file_mode_rw(dest_fp, dest) < 0) {
2744 FILE_OP_ERROR(dest, "chmod");
2745 g_warning("can't change file mode\n");
2748 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2751 if (!err && fclose(dest_fp) == EOF) {
2752 FILE_OP_ERROR(dest, "fclose");
2757 g_warning("writing to %s failed.\n", dest);
2765 /* convert line endings into CRLF. If the last line doesn't end with
2766 * linebreak, add it.
2768 gchar *canonicalize_str(const gchar *str)
2774 for (p = str; *p != '\0'; ++p) {
2781 if (p == str || *(p - 1) != '\n')
2784 out = outp = g_malloc(new_len + 1);
2785 for (p = str; *p != '\0'; ++p) {
2792 if (p == str || *(p - 1) != '\n') {
2801 gint canonicalize_file(const gchar *src, const gchar *dest)
2803 FILE *src_fp, *dest_fp;
2804 gchar buf[BUFFSIZE];
2806 gboolean err = FALSE;
2807 gboolean last_linebreak = FALSE;
2809 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2810 FILE_OP_ERROR(src, "fopen");
2814 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2815 FILE_OP_ERROR(dest, "fopen");
2820 if (change_file_mode_rw(dest_fp, dest) < 0) {
2821 FILE_OP_ERROR(dest, "chmod");
2822 g_warning("can't change file mode\n");
2825 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2829 if (len == 0) break;
2830 last_linebreak = FALSE;
2832 if (buf[len - 1] != '\n') {
2833 last_linebreak = TRUE;
2834 r = fputs(buf, dest_fp);
2835 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2836 r = fputs(buf, dest_fp);
2839 r = fwrite(buf, 1, len - 1, dest_fp);
2844 r = fputs("\r\n", dest_fp);
2848 g_warning("writing to %s failed.\n", dest);
2856 if (last_linebreak == TRUE) {
2857 if (fputs("\r\n", dest_fp) == EOF)
2861 if (ferror(src_fp)) {
2862 FILE_OP_ERROR(src, "fgets");
2866 if (fclose(dest_fp) == EOF) {
2867 FILE_OP_ERROR(dest, "fclose");
2879 gint canonicalize_file_replace(const gchar *file)
2883 tmp_file = get_tmp_file();
2885 if (canonicalize_file(file, tmp_file) < 0) {
2890 if (move_file(tmp_file, file, TRUE) < 0) {
2891 g_warning("can't replace %s .\n", file);
2901 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2903 FILE *src_fp, *dest_fp;
2904 gchar buf[BUFFSIZE];
2905 gboolean err = FALSE;
2907 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2908 FILE_OP_ERROR(src, "fopen");
2912 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2913 FILE_OP_ERROR(dest, "fopen");
2918 if (change_file_mode_rw(dest_fp, dest) < 0) {
2919 FILE_OP_ERROR(dest, "chmod");
2920 g_warning("can't change file mode\n");
2923 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2925 if (fputs(buf, dest_fp) == EOF) {
2926 g_warning("writing to %s failed.\n", dest);
2934 if (ferror(src_fp)) {
2935 FILE_OP_ERROR(src, "fgets");
2939 if (fclose(dest_fp) == EOF) {
2940 FILE_OP_ERROR(dest, "fclose");
2952 gint uncanonicalize_file_replace(const gchar *file)
2956 tmp_file = get_tmp_file();
2958 if (uncanonicalize_file(file, tmp_file) < 0) {
2963 if (move_file(tmp_file, file, TRUE) < 0) {
2964 g_warning("can't replace %s .\n", file);
2974 gchar *normalize_newlines(const gchar *str)
2976 const gchar *p = str;
2979 out = outp = g_malloc(strlen(str) + 1);
2980 for (p = str; *p != '\0'; ++p) {
2982 if (*(p + 1) != '\n')
2993 gchar *get_outgoing_rfc2822_str(FILE *fp)
2995 gchar buf[BUFFSIZE];
2999 str = g_string_new(NULL);
3001 /* output header part */
3002 while (fgets(buf, sizeof(buf), fp) != NULL) {
3004 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3011 else if (next != ' ' && next != '\t') {
3015 if (fgets(buf, sizeof(buf), fp) == NULL)
3019 g_string_append(str, buf);
3020 g_string_append(str, "\r\n");
3026 /* output body part */
3027 while (fgets(buf, sizeof(buf), fp) != NULL) {
3030 g_string_append_c(str, '.');
3031 g_string_append(str, buf);
3032 g_string_append(str, "\r\n");
3036 g_string_free(str, FALSE);
3042 * Create a new boundary in a way that it is very unlikely that this
3043 * will occur in the following text. It would be easy to ensure
3044 * uniqueness if everything is either quoted-printable or base64
3045 * encoded (note that conversion is allowed), but because MIME bodies
3046 * may be nested, it may happen that the same boundary has already
3049 * boundary := 0*69<bchars> bcharsnospace
3050 * bchars := bcharsnospace / " "
3051 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3052 * "+" / "_" / "," / "-" / "." /
3053 * "/" / ":" / "=" / "?"
3055 * some special characters removed because of buggy MTAs
3058 gchar *generate_mime_boundary(const gchar *prefix)
3060 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3061 "abcdefghijklmnopqrstuvwxyz"
3066 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3067 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3070 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3074 gint change_file_mode_rw(FILE *fp, const gchar *file)
3077 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3079 return g_chmod(file, S_IRUSR|S_IWUSR);
3083 FILE *my_tmpfile(void)
3085 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3086 const gchar suffix[] = ".XXXXXX";
3087 const gchar *tmpdir;
3089 const gchar *progname;
3096 tmpdir = get_tmp_dir();
3097 tmplen = strlen(tmpdir);
3098 progname = g_get_prgname();
3099 if (progname == NULL)
3100 progname = "sylpheed-claws";
3101 proglen = strlen(progname);
3102 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3105 memcpy(fname, tmpdir, tmplen);
3106 fname[tmplen] = G_DIR_SEPARATOR;
3107 memcpy(fname + tmplen + 1, progname, proglen);
3108 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3110 fd = mkstemp(fname);
3117 /* verify that we can write in the file after unlinking */
3118 if (write(fd, buf, 1) < 0) {
3125 fp = fdopen(fd, "w+b");
3133 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3138 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3142 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3143 dir, G_DIR_SEPARATOR);
3144 fd = mkstemp_name(template, filename);
3147 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3148 fd = mkstemp(*filename);
3150 return fdopen(fd, "w+");
3153 FILE *str_open_as_stream(const gchar *str)
3158 g_return_val_if_fail(str != NULL, NULL);
3162 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3167 if (len == 0) return fp;
3169 if (fwrite(str, 1, len, fp) != len) {
3170 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3179 gint str_write_to_file(const gchar *str, const gchar *file)
3184 g_return_val_if_fail(str != NULL, -1);
3185 g_return_val_if_fail(file != NULL, -1);
3187 if ((fp = g_fopen(file, "wb")) == NULL) {
3188 FILE_OP_ERROR(file, "fopen");
3198 if (fwrite(str, 1, len, fp) != len) {
3199 FILE_OP_ERROR(file, "fwrite");
3205 if (fclose(fp) == EOF) {
3206 FILE_OP_ERROR(file, "fclose");
3214 gchar *file_read_to_str(const gchar *file)
3219 g_return_val_if_fail(file != NULL, NULL);
3221 if ((fp = g_fopen(file, "rb")) == NULL) {
3222 FILE_OP_ERROR(file, "fopen");
3226 str = file_read_stream_to_str(fp);
3233 gchar *file_read_stream_to_str(FILE *fp)
3240 g_return_val_if_fail(fp != NULL, NULL);
3242 array = g_byte_array_new();
3244 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3245 if (n_read < sizeof(buf) && ferror(fp))
3247 g_byte_array_append(array, buf, n_read);
3251 FILE_OP_ERROR("file stream", "fread");
3252 g_byte_array_free(array, TRUE);
3257 g_byte_array_append(array, buf, 1);
3258 str = (gchar *)array->data;
3259 g_byte_array_free(array, FALSE);
3261 if (!g_utf8_validate(str, -1, NULL)) {
3262 const gchar *src_codeset, *dest_codeset;
3264 src_codeset = conv_get_locale_charset_str();
3265 dest_codeset = CS_UTF_8;
3266 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3274 gint execute_async(gchar *const argv[])
3276 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3278 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3279 NULL, NULL, NULL, FALSE) == FALSE) {
3280 g_warning("Couldn't execute command: %s\n", argv[0]);
3287 gint execute_sync(gchar *const argv[])
3291 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3293 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3294 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3295 g_warning("Couldn't execute command: %s\n", argv[0]);
3300 if (WIFEXITED(status))
3301 return WEXITSTATUS(status);
3309 gint execute_command_line(const gchar *cmdline, gboolean async)
3314 debug_print("execute_command_line(): executing: %s\n", cmdline);
3316 argv = strsplit_with_quote(cmdline, " ", 0);
3319 ret = execute_async(argv);
3321 ret = execute_sync(argv);
3328 gchar *get_command_output(const gchar *cmdline)
3330 gchar *child_stdout;
3333 g_return_val_if_fail(cmdline != NULL, NULL);
3335 debug_print("get_command_output(): executing: %s\n", cmdline);
3337 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3339 g_warning("Couldn't execute command: %s\n", cmdline);
3343 return child_stdout;
3346 static gint is_unchanged_uri_char(char c)
3358 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3364 for(i = 0; i < strlen(uri) ; i++) {
3365 if (is_unchanged_uri_char(uri[i])) {
3366 if (k + 2 >= bufsize)
3368 encoded_uri[k++] = uri[i];
3371 char * hexa = "0123456789ABCDEF";
3373 if (k + 4 >= bufsize)
3375 encoded_uri[k++] = '%';
3376 encoded_uri[k++] = hexa[uri[i] / 16];
3377 encoded_uri[k++] = hexa[uri[i] % 16];
3383 gint open_uri(const gchar *uri, const gchar *cmdline)
3385 gchar buf[BUFFSIZE];
3387 gchar encoded_uri[BUFFSIZE];
3389 g_return_val_if_fail(uri != NULL, -1);
3391 /* an option to choose whether to use encode_uri or not ? */
3392 encode_uri(encoded_uri, BUFFSIZE, uri);
3395 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3396 !strchr(p + 2, '%'))
3397 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3400 g_warning("Open URI command line is invalid "
3401 "(there must be only one '%%s'): %s",
3403 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3406 execute_command_line(buf, TRUE);
3411 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3413 gchar buf[BUFFSIZE];
3416 g_return_val_if_fail(filepath != NULL, -1);
3419 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3420 !strchr(p + 2, '%'))
3421 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3424 g_warning("Open Text Editor command line is invalid "
3425 "(there must be only one '%%s'): %s",
3427 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3430 execute_command_line(buf, TRUE);
3435 time_t remote_tzoffset_sec(const gchar *zone)
3437 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3443 time_t remoteoffset;
3445 strncpy(zone3, zone, 3);
3449 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3450 (c == '+' || c == '-')) {
3451 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3453 remoteoffset = -remoteoffset;
3454 } else if (!strncmp(zone, "UT" , 2) ||
3455 !strncmp(zone, "GMT", 2)) {
3457 } else if (strlen(zone3) == 3) {
3458 for (p = ustzstr; *p != '\0'; p += 3) {
3459 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3460 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3461 remoteoffset = iustz * 3600;
3467 } else if (strlen(zone3) == 1) {
3469 case 'Z': remoteoffset = 0; break;
3470 case 'A': remoteoffset = -1; break;
3471 case 'B': remoteoffset = -2; break;
3472 case 'C': remoteoffset = -3; break;
3473 case 'D': remoteoffset = -4; break;
3474 case 'E': remoteoffset = -5; break;
3475 case 'F': remoteoffset = -6; break;
3476 case 'G': remoteoffset = -7; break;
3477 case 'H': remoteoffset = -8; break;
3478 case 'I': remoteoffset = -9; break;
3479 case 'K': remoteoffset = -10; break; /* J is not used */
3480 case 'L': remoteoffset = -11; break;
3481 case 'M': remoteoffset = -12; break;
3482 case 'N': remoteoffset = 1; break;
3483 case 'O': remoteoffset = 2; break;
3484 case 'P': remoteoffset = 3; break;
3485 case 'Q': remoteoffset = 4; break;
3486 case 'R': remoteoffset = 5; break;
3487 case 'S': remoteoffset = 6; break;
3488 case 'T': remoteoffset = 7; break;
3489 case 'U': remoteoffset = 8; break;
3490 case 'V': remoteoffset = 9; break;
3491 case 'W': remoteoffset = 10; break;
3492 case 'X': remoteoffset = 11; break;
3493 case 'Y': remoteoffset = 12; break;
3494 default: remoteoffset = 0; break;
3496 remoteoffset = remoteoffset * 3600;
3500 return remoteoffset;
3503 time_t tzoffset_sec(time_t *now)
3509 lt = localtime(now);
3511 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3513 if (lt->tm_year < gmt.tm_year)
3515 else if (lt->tm_year > gmt.tm_year)
3517 else if (lt->tm_yday < gmt.tm_yday)
3519 else if (lt->tm_yday > gmt.tm_yday)
3522 if (off >= 24 * 60) /* should be impossible */
3523 off = 23 * 60 + 59; /* if not, insert silly value */
3524 if (off <= -24 * 60)
3525 off = -(23 * 60 + 59);
3530 /* calculate timezone offset */
3531 gchar *tzoffset(time_t *now)
3533 static gchar offset_string[6];
3539 lt = localtime(now);
3541 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3543 if (lt->tm_year < gmt.tm_year)
3545 else if (lt->tm_year > gmt.tm_year)
3547 else if (lt->tm_yday < gmt.tm_yday)
3549 else if (lt->tm_yday > gmt.tm_yday)
3557 if (off >= 24 * 60) /* should be impossible */
3558 off = 23 * 60 + 59; /* if not, insert silly value */
3560 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3562 return offset_string;
3565 void get_rfc822_date(gchar *buf, gint len)
3569 gchar day[4], mon[4];
3570 gint dd, hh, mm, ss, yyyy;
3575 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3576 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3577 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3578 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3581 /* just a wrapper to suppress the warning of gcc about %c */
3582 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3583 const struct tm *tm)
3585 return strftime(s, max, format, tm);
3588 void debug_set_mode(gboolean mode)
3593 gboolean debug_get_mode(void)
3598 void debug_print_real(const gchar *format, ...)
3601 gchar buf[BUFFSIZE];
3603 if (!debug_mode) return;
3605 va_start(args, format);
3606 g_vsnprintf(buf, sizeof(buf), format, args);
3613 const char * debug_srcname(const char *file)
3615 const char *s = strrchr (file, '/');
3620 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3622 if (subject == NULL)
3625 subject += subject_get_prefix_length(subject);
3627 return g_hash_table_lookup(subject_table, subject);
3630 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3633 if (subject == NULL || *subject == 0)
3635 subject += subject_get_prefix_length(subject);
3636 g_hash_table_insert(subject_table, subject, data);
3639 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3641 if (subject == NULL)
3644 subject += subject_get_prefix_length(subject);
3645 g_hash_table_remove(subject_table, subject);
3649 *\brief Check if a string is prefixed with known (combinations)
3650 * of prefixes. The function assumes that each prefix
3651 * is terminated by zero or exactly _one_ space.
3653 *\param str String to check for a prefixes
3655 *\return int Number of chars in the prefix that should be skipped
3656 * for a "clean" subject line. If no prefix was found, 0
3659 int subject_get_prefix_length(const gchar *subject)
3661 /*!< Array with allowable reply prefixes regexps. */
3662 static const gchar * const prefixes[] = {
3663 "Re\\:", /* "Re:" */
3664 "RE\\:", /* "RE:" (outlook) */
3665 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3666 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3667 "Aw\\:", /* "Aw:" (German) */
3668 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3669 "Res\\:", /* "Res:" (Brazilian Outlook) */
3670 "Fw\\:", /* "Fw:" Forward */
3671 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3672 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3673 "Rif\\:", /* "Rif:" (Italian Outlook) */
3674 "SV\\:", /* "SV" (Norwegian) */
3675 "Sv\\:", /* "Sv" (Norwegian) */
3676 "VS\\:", /* "VS" (Norwegian) */
3677 "Vs\\:", /* "Vs" (Norwegian) */
3678 "AD\\:", /* "AD" (Norwegian) */
3679 "Ad\\:", /* "Ad" (Norwegian) */
3680 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3683 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3686 static regex_t regex;
3687 static gboolean init_;
3689 if (!subject) return 0;
3690 if (!*subject) return 0;
3693 GString *s = g_string_new("");
3695 for (n = 0; n < PREFIXES; n++)
3696 /* Terminate each prefix regexpression by a
3697 * "\ ?" (zero or ONE space), and OR them */
3698 g_string_append_printf(s, "(%s\\ ?)%s",
3703 g_string_prepend(s, "(");
3704 g_string_append(s, ")+"); /* match at least once */
3705 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3708 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3709 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3710 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3711 debug_print("Error compiling regexp %s\n", s->str);
3712 g_string_free(s, TRUE);
3716 g_string_free(s, TRUE);
3720 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3726 guint g_stricase_hash(gconstpointer gptr)
3728 guint hash_result = 0;
3731 for (str = gptr; str && *str; str++) {
3732 hash_result += toupper(*str);
3738 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3740 const char *str1 = gptr1;
3741 const char *str2 = gptr2;
3743 return !strcasecmp(str1, str2);
3746 gint g_int_compare(gconstpointer a, gconstpointer b)
3748 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3751 gchar *generate_msgid(gchar *buf, gint len)
3760 addr = g_strconcat("@", get_domain_name(), NULL);
3762 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3763 lt->tm_year + 1900, lt->tm_mon + 1,
3764 lt->tm_mday, lt->tm_hour,
3765 lt->tm_min, lt->tm_sec,
3766 (guint) rand(), addr);
3773 quote_cmd_argument()
3775 return a quoted string safely usable in argument of a command.
3777 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3780 gint quote_cmd_argument(gchar * result, guint size,
3790 for(p = path ; * p != '\0' ; p ++) {
3792 if (isalnum((guchar)*p) || (* p == '/')) {
3793 if (remaining > 0) {
3799 result[size - 1] = '\0';
3804 if (remaining >= 2) {
3812 result[size - 1] = '\0';
3817 if (remaining > 0) {
3821 result[size - 1] = '\0';
3835 static void g_node_map_recursive(GNode *node, gpointer data)
3837 GNodeMapData *mapdata = (GNodeMapData *) data;
3839 GNodeMapData newmapdata;
3842 newdata = mapdata->func(node->data, mapdata->data);
3843 if (newdata != NULL) {
3844 newnode = g_node_new(newdata);
3845 g_node_append(mapdata->parent, newnode);
3847 newmapdata.parent = newnode;
3848 newmapdata.func = mapdata->func;
3849 newmapdata.data = mapdata->data;
3851 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3855 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3858 GNodeMapData mapdata;
3860 g_return_val_if_fail(node != NULL, NULL);
3861 g_return_val_if_fail(func != NULL, NULL);
3863 root = g_node_new(func(node->data, data));
3865 mapdata.parent = root;
3866 mapdata.func = func;
3867 mapdata.data = data;
3869 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3874 #define HEX_TO_INT(val, hex) \
3878 if ('0' <= c && c <= '9') { \
3880 } else if ('a' <= c && c <= 'f') { \
3881 val = c - 'a' + 10; \
3882 } else if ('A' <= c && c <= 'F') { \
3883 val = c - 'A' + 10; \
3889 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3896 if (hi == -1 || lo == -1)
3899 *out = (hi << 4) + lo;
3903 #define INT_TO_HEX(hex, val) \
3906 hex = '0' + (val); \
3908 hex = 'A' + (val) - 10; \
3911 void get_hex_str(gchar *out, guchar ch)
3915 INT_TO_HEX(hex, ch >> 4);
3917 INT_TO_HEX(hex, ch & 0x0f);
3923 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3925 #define G_PRINT_REF g_print
3929 *\brief Register ref counted pointer. It is based on GBoxed, so should
3930 * work with anything that uses the GType system. The semantics
3931 * are similar to a C++ auto pointer, with the exception that
3932 * C doesn't have automatic closure (calling destructors) when
3933 * exiting a block scope.
3934 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3935 * function directly.
3937 *\return GType A GType type.
3939 GType g_auto_pointer_register(void)
3941 static GType auto_pointer_type;
3942 if (!auto_pointer_type)
3944 g_boxed_type_register_static
3945 ("G_TYPE_AUTO_POINTER",
3946 (GBoxedCopyFunc) g_auto_pointer_copy,
3947 (GBoxedFreeFunc) g_auto_pointer_free);
3948 return auto_pointer_type;
3952 *\brief Structure with g_new() allocated pointer guarded by the
3955 typedef struct AutoPointerRef {
3956 void (*free) (gpointer);
3962 *\brief The auto pointer opaque structure that references the
3963 * pointer guard block.
3965 typedef struct AutoPointer {
3966 AutoPointerRef *ref;
3967 gpointer ptr; /*!< access to protected pointer */
3971 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3975 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3976 * ... when assigning, copying and freeing storage elements
3978 * gtk_list_store_new(N_S_COLUMNS,
3979 * G_TYPE_AUTO_POINTER,
3983 * Template *precious_data = g_new0(Template, 1);
3984 * g_pointer protect = g_auto_pointer_new(precious_data);
3986 * gtk_list_store_set(container, &iter,
3990 * ... the gtk_list_store has copied the pointer and
3991 * ... incremented its reference count, we should free
3992 * ... the auto pointer (in C++ a destructor would do
3993 * ... this for us when leaving block scope)
3995 * g_auto_pointer_free(protect);
3997 * ... gtk_list_store_set() now manages the data. When
3998 * ... *explicitly* requesting a pointer from the list
3999 * ... store, don't forget you get a copy that should be
4000 * ... freed with g_auto_pointer_free() eventually.
4004 *\param pointer Pointer to be guarded.
4006 *\return GAuto * Pointer that should be used in containers with
4009 GAuto *g_auto_pointer_new(gpointer p)
4011 AutoPointerRef *ref;
4017 ref = g_new0(AutoPointerRef, 1);
4018 ptr = g_new0(AutoPointer, 1);
4028 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4034 *\brief Allocate an autopointer using the passed \a free function to
4035 * free the guarded pointer
4037 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4044 aptr = g_auto_pointer_new(p);
4045 aptr->ref->free = free_;
4049 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4051 if (auto_ptr == NULL)
4053 return ((AutoPointer *) auto_ptr)->ptr;
4057 *\brief Copies an auto pointer by. It's mostly not necessary
4058 * to call this function directly, unless you copy/assign
4059 * the guarded pointer.
4061 *\param auto_ptr Auto pointer returned by previous call to
4062 * g_auto_pointer_new_XXX()
4064 *\return gpointer An auto pointer
4066 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4069 AutoPointerRef *ref;
4072 if (auto_ptr == NULL)
4077 newp = g_new0(AutoPointer, 1);
4080 newp->ptr = ref->pointer;
4084 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4090 *\brief Free an auto pointer
4092 void g_auto_pointer_free(GAuto *auto_ptr)
4095 AutoPointerRef *ref;
4097 if (auto_ptr == NULL)
4103 if (--(ref->cnt) == 0) {
4105 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4107 ref->free(ref->pointer);
4112 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4117 void replace_returns(gchar *str)
4122 while (strstr(str, "\n")) {
4123 *strstr(str, "\n") = ' ';
4125 while (strstr(str, "\r")) {
4126 *strstr(str, "\r") = ' ';
4130 /* get_uri_part() - retrieves a URI starting from scanpos.
4131 Returns TRUE if succesful */
4132 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4133 const gchar **bp, const gchar **ep, gboolean hdr)
4136 gint parenthese_cnt = 0;
4138 g_return_val_if_fail(start != NULL, FALSE);
4139 g_return_val_if_fail(scanpos != NULL, FALSE);
4140 g_return_val_if_fail(bp != NULL, FALSE);
4141 g_return_val_if_fail(ep != NULL, FALSE);
4145 /* find end point of URI */
4146 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4147 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4148 !IS_ASCII(*(const guchar *)ep_) ||
4149 strchr("[]{}<>\"", *ep_)) {
4151 } else if (strchr("(", *ep_)) {
4153 } else if (strchr(")", *ep_)) {
4154 if (parenthese_cnt > 0)
4161 /* no punctuation at end of string */
4163 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4164 * should pass some URI type to this function and decide on that whether
4165 * to perform punctuation stripping */
4167 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4169 for (; ep_ - 1 > scanpos + 1 &&
4170 IS_REAL_PUNCT(*(ep_ - 1));
4174 #undef IS_REAL_PUNCT
4181 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4183 while (bp && *bp && g_ascii_isspace(*bp))
4185 return g_strndup(bp, ep - bp);
4188 /* valid mail address characters */
4189 #define IS_RFC822_CHAR(ch) \
4193 !g_ascii_isspace(ch) && \
4194 !strchr("(),;<>\"", (ch)))
4196 /* alphabet and number within 7bit ASCII */
4197 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4198 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4200 static GHashTable *create_domain_tab(void)
4202 static const gchar *toplvl_domains [] = {
4204 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4205 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4206 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4207 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4208 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4209 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4210 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4211 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4212 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4213 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4214 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4215 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4216 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4217 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4218 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4219 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4220 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4221 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4222 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4223 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4224 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4225 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4226 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4227 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4228 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4229 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4232 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4234 g_return_val_if_fail(htab, NULL);
4235 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4236 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4240 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4242 const gint MAX_LVL_DOM_NAME_LEN = 6;
4243 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4244 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4247 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4250 for (p = buf; p < m && first < last; *p++ = *first++)
4254 return g_hash_table_lookup(tab, buf) != NULL;
4257 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4258 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4259 const gchar **bp, const gchar **ep, gboolean hdr)
4261 /* more complex than the uri part because we need to scan back and forward starting from
4262 * the scan position. */
4263 gboolean result = FALSE;
4264 const gchar *bp_ = NULL;
4265 const gchar *ep_ = NULL;
4266 static GHashTable *dom_tab;
4267 const gchar *last_dot = NULL;
4268 const gchar *prelast_dot = NULL;
4269 const gchar *last_tld_char = NULL;
4271 /* the informative part of the email address (describing the name
4272 * of the email address owner) may contain quoted parts. the
4273 * closure stack stores the last encountered quotes. */
4274 gchar closure_stack[128];
4275 gchar *ptr = closure_stack;
4277 g_return_val_if_fail(start != NULL, FALSE);
4278 g_return_val_if_fail(scanpos != NULL, FALSE);
4279 g_return_val_if_fail(bp != NULL, FALSE);
4280 g_return_val_if_fail(ep != NULL, FALSE);
4283 const gchar *start_quote = NULL;
4284 const gchar *end_quote = NULL;
4286 /* go to the real start */
4287 if (start[0] == ',')
4289 if (start[0] == ';')
4291 while (start[0] == '\n' || start[0] == '\r')
4293 while (start[0] == ' ' || start[0] == '\t')
4298 /* check if there are quotes (to skip , in them) */
4299 if (*start == '"') {
4300 start_quote = start;
4302 end_quote = strstr(start, "\"");
4308 /* skip anything between quotes */
4309 if (start_quote && end_quote) {
4314 /* find end (either , or ; or end of line) */
4315 if (strstr(start, ",") && strstr(start, ";"))
4316 *ep = strstr(start,",") < strstr(start, ";")
4317 ? strstr(start, ",") : strstr(start, ";");
4318 else if (strstr(start, ","))
4319 *ep = strstr(start, ",");
4320 else if (strstr(start, ";"))
4321 *ep = strstr(start, ";");
4323 *ep = start+strlen(start);
4325 /* go back to real start */
4326 if (start_quote && end_quote) {
4327 start = start_quote;
4330 /* check there's still an @ in that, or search
4331 * further if possible */
4332 if (strstr(start, "@") && strstr(start, "@") < *ep)
4334 else if (*ep < start+strlen(start)) {
4337 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4345 dom_tab = create_domain_tab();
4346 g_return_val_if_fail(dom_tab, FALSE);
4348 /* scan start of address */
4349 for (bp_ = scanpos - 1;
4350 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4353 /* TODO: should start with an alnum? */
4355 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4358 if (bp_ != scanpos) {
4359 /* scan end of address */
4360 for (ep_ = scanpos + 1;
4361 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4363 prelast_dot = last_dot;
4365 if (*(last_dot + 1) == '.') {
4366 if (prelast_dot == NULL)
4368 last_dot = prelast_dot;
4373 /* TODO: really should terminate with an alnum? */
4374 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4379 if (last_dot == NULL)
4381 if (last_dot >= ep_)
4382 last_dot = prelast_dot;
4383 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4387 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4388 if (*last_tld_char == '?')
4391 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4398 if (!result) return FALSE;
4400 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4401 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4402 && IS_RFC822_CHAR(*(ep_ + 3))) {
4403 /* this informative part with an @ in it is
4404 * followed by the email address */
4407 /* go to matching '>' (or next non-rfc822 char, like \n) */
4408 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4411 /* include the bracket */
4412 if (*ep_ == '>') ep_++;
4414 /* include the leading quote */
4422 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4423 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4426 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4427 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4430 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4431 #define IN_STACK() (ptr > closure_stack)
4432 /* has underrun check */
4433 #define POP_STACK() if(IN_STACK()) --ptr
4434 /* has overrun check */
4435 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4436 /* has underrun check */
4437 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4441 /* scan for the informative part. */
4442 for (bp_ -= 2; bp_ >= start; bp_--) {
4443 /* if closure on the stack keep scanning */
4444 if (PEEK_STACK() == *bp_) {
4448 if (*bp_ == '\'' || *bp_ == '"') {
4453 /* if nothing in the closure stack, do the special conditions
4454 * the following if..else expression simply checks whether
4455 * a token is acceptable. if not acceptable, the clause
4456 * should terminate the loop with a 'break' */
4457 if (!PEEK_STACK()) {
4459 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4460 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4461 /* hyphens are allowed, but only in
4463 } else if (strchr(" \"'", *bp_)) {
4464 /* but anything not being a punctiation
4467 break; /* anything else is rejected */
4474 /* scan forward (should start with an alnum) */
4475 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4491 #undef IS_ASCII_ALNUM
4492 #undef IS_RFC822_CHAR
4494 gchar *make_email_string(const gchar *bp, const gchar *ep)
4496 /* returns a mailto: URI; mailto: is also used to detect the
4497 * uri type later on in the button_pressed signal handler */
4501 tmp = g_strndup(bp, ep - bp);
4502 result = g_strconcat("mailto:", tmp, NULL);
4508 gchar *make_http_string(const gchar *bp, const gchar *ep)
4510 /* returns an http: URI; */
4514 while (bp && *bp && g_ascii_isspace(*bp))
4516 tmp = g_strndup(bp, ep - bp);
4517 result = g_strconcat("http://", tmp, NULL);
4523 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4525 FILE *fp = fopen(path, "rb");
4526 gchar buf[BUFFSIZE];
4527 gchar *result = NULL;
4530 while (fgets(buf, sizeof (buf), fp) != NULL) {
4531 gchar **parts = g_strsplit(buf, ";", 3);
4532 gchar *trimmed = parts[0];
4533 while (trimmed[0] == ' ')
4535 while (trimmed[strlen(trimmed)-1] == ' ')
4536 trimmed[strlen(trimmed)-1] = '\0';
4538 if (!strcmp(trimmed, type)) {
4539 gboolean needsterminal = FALSE;
4540 if (parts[2] && strstr(parts[2], "needsterminal")) {
4541 needsterminal = TRUE;
4543 if (parts[2] && strstr(parts[2], "test=")) {
4544 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4545 gchar *testcmd = orig_testcmd;
4546 if (strstr(testcmd,";"))
4547 *(strstr(testcmd,";")) = '\0';
4548 while (testcmd[0] == ' ')
4550 while (testcmd[strlen(testcmd)-1] == '\n')
4551 testcmd[strlen(testcmd)-1] = '\0';
4552 while (testcmd[strlen(testcmd)-1] == '\r')
4553 testcmd[strlen(testcmd)-1] = '\0';
4554 while (testcmd[strlen(testcmd)-1] == ' ')
4555 testcmd[strlen(testcmd)-1] = '\0';
4557 if (strstr(testcmd, "%s")) {
4558 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4559 gint res = system(tmp);
4561 g_free(orig_testcmd);
4568 gint res = system(testcmd);
4569 g_free(orig_testcmd);
4579 while (trimmed[0] == ' ')
4581 while (trimmed[strlen(trimmed)-1] == '\n')
4582 trimmed[strlen(trimmed)-1] = '\0';
4583 while (trimmed[strlen(trimmed)-1] == '\r')
4584 trimmed[strlen(trimmed)-1] = '\0';
4585 while (trimmed[strlen(trimmed)-1] == ' ')
4586 trimmed[strlen(trimmed)-1] = '\0';
4587 result = g_strdup(trimmed);
4590 /* if there are no single quotes around %s, add them.
4591 * '.*%s.*' is ok, as in display 'png:%s'
4593 if (strstr(result, "%s")
4594 && !(strstr(result, "'") < strstr(result,"%s") &&
4595 strstr(strstr(result,"%s"), "'"))) {
4596 gchar *start = g_strdup(result);
4597 gchar *end = g_strdup(strstr(result, "%s")+2);
4599 *strstr(start, "%s") = '\0';
4600 tmp = g_strconcat(start,"'%s'",end, NULL);
4606 if (needsterminal) {
4607 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4618 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4620 gchar *result = NULL;
4624 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4625 result = mailcap_get_command_in_file(path, type, file_to_open);
4629 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4633 void mailcap_update_default(const gchar *type, const gchar *command)
4635 gchar *path = NULL, *outpath = NULL;
4636 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4637 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4638 FILE *fp = fopen(path, "rb");
4639 FILE *outfp = fopen(outpath, "wb");
4640 gchar buf[BUFFSIZE];
4653 while (fgets(buf, sizeof (buf), fp) != NULL) {
4654 gchar **parts = g_strsplit(buf, ";", 3);
4655 gchar *trimmed = parts[0];
4656 while (trimmed[0] == ' ')
4658 while (trimmed[strlen(trimmed)-1] == ' ')
4659 trimmed[strlen(trimmed)-1] = '\0';
4661 if (!strcmp(trimmed, type)) {
4670 fprintf(outfp, "%s; %s\n", type, command);
4673 g_rename(outpath, path);
4676 gint copy_dir(const gchar *src, const gchar *dst)
4681 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4682 g_warning("failed to open directory: %s\n", src);
4686 if (make_dir(dst) < 0)
4689 while ((name = g_dir_read_name(dir)) != NULL) {
4690 gchar *old_file, *new_file;
4691 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4692 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4693 debug_print("copying: %s -> %s\n", old_file, new_file);
4694 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4695 gint r = copy_file(old_file, new_file, TRUE);
4698 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4699 gint r = copy_dir(old_file, new_file);
4707 /* crude test to see if a file is an email. */
4708 gboolean file_is_email (const gchar *filename)
4714 if (filename == NULL)
4716 if ((fp = g_fopen(filename, "rb")) == NULL)
4718 while (i < 60 && score < 3
4719 && fgets(buffer, sizeof (buffer), fp) > 0) {
4720 if (!strncmp(buffer, "From:", strlen("From:")))
4722 if (!strncmp(buffer, "To:", strlen("To:")))
4724 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4729 return (score >= 3);
4732 gboolean sc_g_list_bigger(GList *list, gint max)
4736 while (cur && i <= max+1) {
4743 gboolean sc_g_slist_bigger(GSList *list, gint max)
4747 while (cur && i <= max+1) {