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 3 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, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
66 # include <tablet-browser-interface.h>
68 # include <osso-browser-interface.h>
74 #include "../codeconv.h"
78 static gboolean debug_mode = FALSE;
80 static GSList *tempfiles=NULL;
83 /* Return true if we are running as root. This function should beused
84 instead of getuid () == 0. */
85 gboolean superuser_p (void)
88 return w32_is_administrator ();
96 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
97 gint g_chdir(const gchar *path)
100 if (G_WIN32_HAVE_WIDECHAR_API()) {
105 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
111 retval = _wchdir(wpath);
123 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
124 if (cp_path == NULL) {
129 retval = chdir(cp_path);
142 gint g_chmod(const gchar *path, gint mode)
145 if (G_WIN32_HAVE_WIDECHAR_API()) {
150 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
156 retval = _wchmod(wpath, mode);
168 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
169 if (cp_path == NULL) {
174 retval = chmod(cp_path, mode);
183 return chmod(path, mode);
186 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
190 gint mkstemp_name(const gchar *template, gchar **name_used)
192 static gulong count=0; /* W32-_mktemp only supports up to 27
196 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
197 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
198 (S_IRUSR | S_IWUSR));
200 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
202 perror(g_strdup_printf("cant create %s",*name_used));
208 #endif /* G_OS_WIN32 */
211 gint mkstemp(const gchar *template)
214 gint res = mkstemp_name(template, &dummyname);
218 #endif /* G_OS_WIN32 */
220 void list_free_strings(GList *list)
222 list = g_list_first(list);
224 while (list != NULL) {
230 void slist_free_strings(GSList *list)
232 while (list != NULL) {
238 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
243 void hash_free_strings(GHashTable *table)
245 g_hash_table_foreach(table, hash_free_strings_func, NULL);
248 gint str_case_equal(gconstpointer v, gconstpointer v2)
250 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
253 guint str_case_hash(gconstpointer key)
255 const gchar *p = key;
259 h = g_ascii_tolower(h);
260 for (p += 1; *p != '\0'; p++)
261 h = (h << 5) - h + g_ascii_tolower(*p);
267 void ptr_array_free_strings(GPtrArray *array)
272 g_return_if_fail(array != NULL);
274 for (i = 0; i < array->len; i++) {
275 str = g_ptr_array_index(array, i);
280 gboolean str_find(const gchar *haystack, const gchar *needle)
282 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
285 gboolean str_case_find(const gchar *haystack, const gchar *needle)
287 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
290 gint to_number(const gchar *nstr)
292 register const gchar *p;
294 if (*nstr == '\0') return -1;
296 for (p = nstr; *p != '\0'; p++)
297 if (!g_ascii_isdigit(*p)) return -1;
302 /* convert integer into string,
303 nstr must be not lower than 11 characters length */
304 gchar *itos_buf(gchar *nstr, gint n)
306 g_snprintf(nstr, 11, "%d", n);
310 /* convert integer into string */
313 static gchar nstr[11];
315 return itos_buf(nstr, n);
318 #define divide(num,divisor,i,d) \
320 i = num >> divisor; \
321 d = num & ((1<<divisor)-1); \
322 d = (d*100) >> divisor; \
325 gchar *to_human_readable(off_t size)
327 static gchar str[14];
328 static gchar *b_format = NULL, *kb_format = NULL,
329 *mb_format = NULL, *gb_format = NULL;
330 register int t = 0, r = 0;
331 if (b_format == NULL) {
333 kb_format = _("%d.%02dKB");
334 mb_format = _("%d.%02dMB");
335 gb_format = _("%.2fGB");
338 if (size < (off_t)1024) {
339 g_snprintf(str, sizeof(str), b_format, (gint)size);
341 } else if (size >> 10 < (off_t)1024) {
342 divide(size, 10, t, r);
343 g_snprintf(str, sizeof(str), kb_format, t, r);
345 } else if (size >> 20 < (off_t)1024) {
346 divide(size, 20, t, r);
347 g_snprintf(str, sizeof(str), mb_format, t, r);
350 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
355 /* strcmp with NULL-checking */
356 gint strcmp2(const gchar *s1, const gchar *s2)
358 if (s1 == NULL || s2 == NULL)
361 return strcmp(s1, s2);
363 /* strstr with NULL-checking */
364 gchar *strstr2(const gchar *s1, const gchar *s2)
366 if (s1 == NULL || s2 == NULL)
369 return strstr(s1, s2);
372 gint path_cmp(const gchar *s1, const gchar *s2)
377 gchar *s1buf, *s2buf;
380 if (s1 == NULL || s2 == NULL) return -1;
381 if (*s1 == '\0' || *s2 == '\0') return -1;
384 s1buf = g_strdup (s1);
385 s2buf = g_strdup (s2);
386 subst_char (s1buf, '/', G_DIR_SEPARATOR);
387 subst_char (s2buf, '/', G_DIR_SEPARATOR);
390 #endif /* !G_OS_WIN32 */
395 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
396 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
398 rc = strncmp(s1, s2, MAX(len1, len2));
402 #endif /* !G_OS_WIN32 */
406 /* remove trailing return code */
407 gchar *strretchomp(gchar *str)
411 if (!*str) return str;
413 for (s = str + strlen(str) - 1;
414 s >= str && (*s == '\n' || *s == '\r');
421 /* remove trailing character */
422 gchar *strtailchomp(gchar *str, gchar tail_char)
426 if (!*str) return str;
427 if (tail_char == '\0') return str;
429 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
435 /* remove CR (carriage return) */
436 gchar *strcrchomp(gchar *str)
440 if (!*str) return str;
442 s = str + strlen(str) - 1;
443 if (*s == '\n' && s > str && *(s - 1) == '\r') {
451 gint file_strip_crs(const gchar *file)
453 FILE *fp = NULL, *outfp = NULL;
455 gchar *out = get_tmp_file();
459 fp = fopen(file, "rb");
463 outfp = fopen(out, "wb");
469 while (fgets(buf, sizeof (buf), fp) != NULL) {
471 if (fputs(buf, outfp) == EOF) {
479 if (fclose(outfp) == EOF) {
483 if (rename_force(out, file) < 0)
494 /* Similar to `strstr' but this function ignores the case of both strings. */
495 gchar *strcasestr(const gchar *haystack, const gchar *needle)
497 register size_t haystack_len, needle_len;
499 haystack_len = strlen(haystack);
500 needle_len = strlen(needle);
502 if (haystack_len < needle_len || needle_len == 0)
505 while (haystack_len >= needle_len) {
506 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
507 return (gchar *)haystack;
517 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
518 gconstpointer needle, size_t needlelen)
520 const gchar *haystack_ = (const gchar *)haystack;
521 const gchar *needle_ = (const gchar *)needle;
522 const gchar *haystack_cur = (const gchar *)haystack;
525 return memchr(haystack_, *needle_, haystacklen);
527 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
529 if (haystacklen - (haystack_cur - haystack_) < needlelen)
531 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
532 return (gpointer)haystack_cur;
540 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
541 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
543 register const gchar *s = src;
544 register gchar *d = dest;
554 /* Examine if next block is non-ASCII string */
555 gboolean is_next_nonascii(const gchar *s)
559 /* skip head space */
560 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
562 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
563 if (*(guchar *)p > 127 || *(guchar *)p < 32)
570 gint get_next_word_len(const gchar *s)
574 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
580 static void trim_subject_for_compare(gchar *str)
584 eliminate_parenthesis(str, '[', ']');
585 eliminate_parenthesis(str, '(', ')');
588 srcp = str + subject_get_prefix_length(str);
590 memmove(str, srcp, strlen(srcp) + 1);
593 static void trim_subject_for_sort(gchar *str)
599 srcp = str + subject_get_prefix_length(str);
601 memmove(str, srcp, strlen(srcp) + 1);
604 /* compare subjects */
605 gint subject_compare(const gchar *s1, const gchar *s2)
609 if (!s1 || !s2) return -1;
610 if (!*s1 || !*s2) return -1;
612 Xstrdup_a(str1, s1, return -1);
613 Xstrdup_a(str2, s2, return -1);
615 trim_subject_for_compare(str1);
616 trim_subject_for_compare(str2);
618 if (!*str1 || !*str2) return -1;
620 return strcmp(str1, str2);
623 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
627 if (!s1 || !s2) return -1;
629 Xstrdup_a(str1, s1, return -1);
630 Xstrdup_a(str2, s2, return -1);
632 trim_subject_for_sort(str1);
633 trim_subject_for_sort(str2);
635 return g_utf8_collate(str1, str2);
638 void trim_subject(gchar *str)
640 register gchar *srcp;
646 srcp = str + subject_get_prefix_length(str);
651 } else if (*srcp == '(') {
663 else if (*srcp == cl)
670 while (g_ascii_isspace(*srcp)) srcp++;
671 memmove(str, srcp, strlen(srcp) + 1);
674 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
676 register gchar *srcp, *destp;
681 while ((destp = strchr(destp, op))) {
687 else if (*srcp == cl)
693 while (g_ascii_isspace(*srcp)) srcp++;
694 memmove(destp, srcp, strlen(srcp) + 1);
698 void extract_parenthesis(gchar *str, gchar op, gchar cl)
700 register gchar *srcp, *destp;
705 while ((srcp = strchr(destp, op))) {
708 memmove(destp, srcp + 1, strlen(srcp));
713 else if (*destp == cl)
725 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
728 register gchar *srcp, *destp;
730 gboolean in_quote = FALSE;
734 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
737 memmove(destp, srcp + 1, strlen(srcp));
740 if (*destp == op && !in_quote)
742 else if (*destp == cl && !in_quote)
744 else if (*destp == quote_chr)
756 void extract_quote(gchar *str, gchar quote_chr)
760 if ((str = strchr(str, quote_chr))) {
762 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
763 memmove(p - 1, p, strlen(p) + 1);
768 memmove(str, str + 1, p - str);
773 void eliminate_address_comment(gchar *str)
775 register gchar *srcp, *destp;
780 while ((destp = strchr(destp, '"'))) {
781 if ((srcp = strchr(destp + 1, '"'))) {
786 while (g_ascii_isspace(*srcp)) srcp++;
787 memmove(destp, srcp, strlen(srcp) + 1);
797 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
803 else if (*srcp == ')')
809 while (g_ascii_isspace(*srcp)) srcp++;
810 memmove(destp, srcp, strlen(srcp) + 1);
814 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
816 gboolean in_quote = FALSE;
819 if (*str == c && !in_quote)
821 if (*str == quote_chr)
829 void extract_address(gchar *str)
831 eliminate_address_comment(str);
832 if (strchr_with_skip_quote(str, '"', '<'))
833 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
837 void extract_list_id_str(gchar *str)
839 if (strchr_with_skip_quote(str, '"', '<'))
840 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
844 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
849 if (!str) return addr_list;
851 Xstrdup_a(work, str, return addr_list);
854 eliminate_address_comment(work);
857 while (workp && *workp) {
860 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
866 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
867 extract_parenthesis_with_skip_quote
868 (workp, '"', '<', '>');
872 addr_list = g_slist_append(addr_list, g_strdup(workp));
880 GSList *address_list_append(GSList *addr_list, const gchar *str)
882 return address_list_append_real(addr_list, str, TRUE);
885 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
887 return address_list_append_real(addr_list, str, FALSE);
890 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
894 if (!str) return msgid_list;
897 while (strp && *strp) {
898 const gchar *start, *end;
901 if ((start = strchr(strp, '<')) != NULL) {
902 end = strchr(start + 1, '>');
907 msgid = g_strndup(start + 1, end - start - 1);
910 msgid_list = g_slist_prepend(msgid_list, msgid);
920 GSList *references_list_append(GSList *msgid_list, const gchar *str)
924 list = references_list_prepend(NULL, str);
925 list = g_slist_reverse(list);
926 msgid_list = g_slist_concat(msgid_list, list);
931 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
936 if (!str) return group_list;
938 Xstrdup_a(work, str, return group_list);
942 while (workp && *workp) {
945 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
953 group_list = g_slist_append(group_list,
962 GList *add_history(GList *list, const gchar *str)
966 g_return_val_if_fail(str != NULL, list);
968 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
971 list = g_list_remove(list, old->data);
972 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
975 last = g_list_last(list);
978 list = g_list_remove(list, last->data);
982 list = g_list_prepend(list, g_strdup(str));
987 void remove_return(gchar *str)
989 register gchar *p = str;
992 if (*p == '\n' || *p == '\r')
993 memmove(p, p + 1, strlen(p));
999 void remove_space(gchar *str)
1001 register gchar *p = str;
1006 while (g_ascii_isspace(*(p + spc)))
1009 memmove(p, p + spc, strlen(p + spc) + 1);
1015 void unfold_line(gchar *str)
1017 register gchar *p = str;
1021 if (*p == '\n' || *p == '\r') {
1024 while (g_ascii_isspace(*(p + spc)))
1027 memmove(p, p + spc, strlen(p + spc) + 1);
1033 void subst_char(gchar *str, gchar orig, gchar subst)
1035 register gchar *p = str;
1044 static void subst_chars(gchar *str, gchar *orig, gchar subst)
1046 register gchar *p = str;
1049 if (strchr(orig, *p) != NULL)
1055 void subst_for_filename(gchar *str)
1060 subst_chars(str, "\t\r\n\\/*:", '_');
1062 subst_chars(str, "\t\r\n\\/*", '_');
1066 void subst_for_shellsafe_filename(gchar *str)
1070 subst_for_filename(str);
1071 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1074 gboolean is_ascii_str(const gchar *str)
1076 const guchar *p = (const guchar *)str;
1078 while (*p != '\0') {
1079 if (*p != '\t' && *p != ' ' &&
1080 *p != '\r' && *p != '\n' &&
1081 (*p < 32 || *p >= 127))
1089 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1091 gchar * position = NULL;
1092 gchar * tmp_pos = NULL;
1095 if (quote_chars == NULL)
1098 for (i = 0; i < strlen(quote_chars); i++) {
1099 tmp_pos = strrchr (str, quote_chars[i]);
1101 || (tmp_pos != NULL && position <= tmp_pos) )
1107 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1109 const gchar *first_pos;
1110 const gchar *last_pos;
1111 const gchar *p = str;
1112 gint quote_level = -1;
1114 /* speed up line processing by only searching to the last '>' */
1115 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1116 /* skip a line if it contains a '<' before the initial '>' */
1117 if (memchr(str, '<', first_pos - str) != NULL)
1119 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1123 while (p <= last_pos) {
1124 while (p < last_pos) {
1125 if (g_ascii_isspace(*p))
1131 if (strchr(quote_chars, *p))
1133 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1134 /* any characters are allowed except '-' and space */
1136 && !strchr(quote_chars, *p)
1137 && !g_ascii_isspace(*p)
1140 if (strchr(quote_chars, *p))
1152 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1154 const gchar *p = str, *q;
1155 gint cur_line = 0, len;
1157 while ((q = strchr(p, '\n')) != NULL) {
1159 if (len > max_chars) {
1169 if (len > max_chars) {
1178 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1180 gchar * position = NULL;
1181 gchar * tmp_pos = NULL;
1184 if (quote_chars == NULL)
1187 for (i = 0; i < strlen(quote_chars); i++) {
1188 tmp_pos = strchr (str, quote_chars[i]);
1190 || (tmp_pos != NULL && position >= tmp_pos) )
1196 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1198 register guint haystack_len, needle_len;
1199 gboolean in_squote = FALSE, in_dquote = FALSE;
1201 haystack_len = strlen(haystack);
1202 needle_len = strlen(needle);
1204 if (haystack_len < needle_len || needle_len == 0)
1207 while (haystack_len >= needle_len) {
1208 if (!in_squote && !in_dquote &&
1209 !strncmp(haystack, needle, needle_len))
1210 return (gchar *)haystack;
1212 /* 'foo"bar"' -> foo"bar"
1213 "foo'bar'" -> foo'bar' */
1214 if (*haystack == '\'') {
1217 else if (!in_dquote)
1219 } else if (*haystack == '\"') {
1222 else if (!in_squote)
1233 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1236 GSList *string_list = NULL, *slist;
1237 gchar **str_array, *s, *new_str;
1238 guint i, n = 1, len;
1240 g_return_val_if_fail(str != NULL, NULL);
1241 g_return_val_if_fail(delim != NULL, NULL);
1244 max_tokens = G_MAXINT;
1246 s = strstr_with_skip_quote(str, delim);
1248 guint delimiter_len = strlen(delim);
1252 new_str = g_strndup(str, len);
1254 if (new_str[0] == '\'' || new_str[0] == '\"') {
1255 if (new_str[len - 1] == new_str[0]) {
1256 new_str[len - 1] = '\0';
1257 memmove(new_str, new_str + 1, len - 1);
1260 string_list = g_slist_prepend(string_list, new_str);
1262 str = s + delimiter_len;
1263 s = strstr_with_skip_quote(str, delim);
1264 } while (--max_tokens && s);
1268 new_str = g_strdup(str);
1269 if (new_str[0] == '\'' || new_str[0] == '\"') {
1271 if (new_str[len - 1] == new_str[0]) {
1272 new_str[len - 1] = '\0';
1273 memmove(new_str, new_str + 1, len - 1);
1276 string_list = g_slist_prepend(string_list, new_str);
1280 str_array = g_new(gchar*, n);
1284 str_array[i--] = NULL;
1285 for (slist = string_list; slist; slist = slist->next)
1286 str_array[i--] = slist->data;
1288 g_slist_free(string_list);
1293 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1295 gchar *abbrev_group;
1297 const gchar *p = group;
1300 g_return_val_if_fail(group != NULL, NULL);
1302 last = group + strlen(group);
1303 abbrev_group = ap = g_malloc(strlen(group) + 1);
1308 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1310 while (*p != '.') p++;
1313 return abbrev_group;
1318 return abbrev_group;
1321 gchar *trim_string(const gchar *str, gint len)
1323 const gchar *p = str;
1328 if (!str) return NULL;
1329 if (strlen(str) <= len)
1330 return g_strdup(str);
1331 if (g_utf8_validate(str, -1, NULL) == FALSE)
1332 return g_strdup(str);
1334 while (*p != '\0') {
1335 mb_len = g_utf8_skip[*(guchar *)p];
1338 else if (new_len + mb_len > len)
1345 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1346 return g_strconcat(new_str, "...", NULL);
1349 GList *uri_list_extract_filenames(const gchar *uri_list)
1351 GList *result = NULL;
1353 gchar *escaped_utf8uri;
1359 while (g_ascii_isspace(*p)) p++;
1360 if (!strncmp(p, "file:", 5)) {
1363 while (*q && *q != '\n' && *q != '\r') q++;
1366 gchar *file, *locale_file = NULL;
1368 while (q > p && g_ascii_isspace(*q))
1370 Xalloca(escaped_utf8uri, q - p + 2,
1372 Xalloca(file, q - p + 2,
1375 strncpy(escaped_utf8uri, p, q - p + 1);
1376 escaped_utf8uri[q - p + 1] = '\0';
1377 decode_uri(file, escaped_utf8uri);
1379 * g_filename_from_uri() rejects escaped/locale encoded uri
1380 * string which come from Nautilus.
1382 if (g_utf8_validate(file, -1, NULL))
1384 = conv_codeset_strdup(
1387 conv_get_locale_charset_str());
1389 locale_file = g_strdup(file + 5);
1390 result = g_list_append(result, locale_file);
1394 p = strchr(p, '\n');
1401 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1404 static gint axtoi(const gchar *hexstr)
1406 gint hi, lo, result;
1409 if ('0' <= hi && hi <= '9') {
1412 if ('a' <= hi && hi <= 'f') {
1415 if ('A' <= hi && hi <= 'F') {
1420 if ('0' <= lo && lo <= '9') {
1423 if ('a' <= lo && lo <= 'f') {
1426 if ('A' <= lo && lo <= 'F') {
1429 result = lo + (16 * hi);
1433 gboolean is_uri_string(const gchar *str)
1435 while (str && *str && g_ascii_isspace(*str))
1437 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1438 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1439 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1440 g_ascii_strncasecmp(str, "www.", 4) == 0);
1443 gchar *get_uri_path(const gchar *uri)
1445 while (uri && *uri && g_ascii_isspace(*uri))
1447 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1448 return (gchar *)(uri + 7);
1449 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1450 return (gchar *)(uri + 8);
1451 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1452 return (gchar *)(uri + 6);
1454 return (gchar *)uri;
1457 gint get_uri_len(const gchar *str)
1461 if (is_uri_string(str)) {
1462 for (p = str; *p != '\0'; p++) {
1463 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1472 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1473 * plusses, and escape characters are used)
1475 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1477 gchar *dec = decoded_uri;
1478 const gchar *enc = encoded_uri;
1483 if (isxdigit((guchar)enc[0]) &&
1484 isxdigit((guchar)enc[1])) {
1490 if (with_plus && *enc == '+')
1502 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1504 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1507 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1509 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1510 decode_uri(buffer, encoded_uri);
1514 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1515 gchar **subject, gchar **body, gchar ***attach)
1519 const gchar *forbidden_uris[] = { ".gnupg/",
1523 gint num_attach = 0;
1524 gchar **my_att = NULL;
1526 Xstrdup_a(tmp_mailto, mailto, return -1);
1528 if (!strncmp(tmp_mailto, "mailto:", 7))
1531 p = strchr(tmp_mailto, '?');
1538 *to = decode_uri_gdup(tmp_mailto);
1540 my_att = g_malloc(sizeof(char *));
1544 gchar *field, *value;
1561 if (*value == '\0') continue;
1563 if (cc && !g_ascii_strcasecmp(field, "cc")) {
1565 *cc = decode_uri_gdup(value);
1567 gchar *tmp = decode_uri_gdup(value);
1568 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1572 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1574 *bcc = decode_uri_gdup(value);
1576 gchar *tmp = decode_uri_gdup(value);
1577 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1581 } else if (subject && !*subject &&
1582 !g_ascii_strcasecmp(field, "subject")) {
1583 *subject = decode_uri_gdup(value);
1584 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1585 *body = decode_uri_gdup(value);
1586 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1588 gchar *tmp = decode_uri_gdup(value);
1589 for (; forbidden_uris[i]; i++) {
1590 if (strstr(tmp, forbidden_uris[i])) {
1591 g_print("Refusing to attach '%s', potential private data leak\n",
1599 /* attach is correct */
1601 printf("realloc my_att %d\n", (num_attach+1));
1602 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1603 my_att[num_attach-1] = tmp;
1604 my_att[num_attach] = NULL;
1616 #include <windows.h>
1617 #ifndef CSIDL_APPDATA
1618 #define CSIDL_APPDATA 0x001a
1620 #ifndef CSIDL_LOCAL_APPDATA
1621 #define CSIDL_LOCAL_APPDATA 0x001c
1623 #ifndef CSIDL_FLAG_CREATE
1624 #define CSIDL_FLAG_CREATE 0x8000
1626 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1630 w32_strerror (int w32_errno)
1632 static char strerr[256];
1633 int ec = (int)GetLastError ();
1637 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1638 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1639 strerr, DIM (strerr)-1, NULL);
1643 static __inline__ void *
1644 dlopen (const char * name, int flag)
1646 void * hd = LoadLibrary (name);
1650 static __inline__ void *
1651 dlsym (void * hd, const char * sym)
1655 void * fnc = GetProcAddress (hd, sym);
1664 static __inline__ const char *
1667 return w32_strerror (0);
1671 static __inline__ int
1683 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1685 static int initialized;
1686 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1690 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1696 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1698 handle = dlopen (dllnames[i], RTLD_LAZY);
1701 func = dlsym (handle, "SHGetFolderPathA");
1712 return func (a,b,c,d,e);
1717 /* Returns a static string with the directroy from which the module
1718 has been loaded. Returns an empty string on error. */
1719 static char *w32_get_module_dir(void)
1721 static char *moddir;
1724 char name[MAX_PATH+10];
1727 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1730 p = strrchr (name, '\\');
1736 moddir = g_strdup (name);
1740 #endif /* G_OS_WIN32 */
1742 /* Return a static string with the locale dir. */
1743 const gchar *get_locale_dir(void)
1745 static gchar *loc_dir;
1749 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1750 "\\share\\locale", NULL);
1753 loc_dir = LOCALEDIR;
1759 const gchar *get_home_dir(void)
1762 static char home_dir[MAX_PATH] = "";
1764 if (home_dir[0] == '\0') {
1765 if (w32_shgetfolderpath
1766 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1767 NULL, 0, home_dir) < 0)
1768 strcpy (home_dir, "C:\\Sylpheed");
1772 static const gchar *homeenv = NULL;
1777 if (!homeenv && g_getenv("HOME") != NULL)
1778 homeenv = g_strdup(g_getenv("HOME"));
1780 homeenv = g_get_home_dir();
1786 static gchar *claws_rc_dir = NULL;
1787 static gboolean rc_dir_alt = FALSE;
1788 const gchar *get_rc_dir(void)
1792 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1795 return claws_rc_dir;
1798 void set_rc_dir(const gchar *dir)
1800 if (claws_rc_dir != NULL) {
1801 g_print("Error: rc_dir already set\n");
1804 if (g_path_is_absolute(dir))
1805 claws_rc_dir = g_strdup(dir);
1807 claws_rc_dir = g_strconcat(g_get_current_dir(),
1808 G_DIR_SEPARATOR_S, dir, NULL);
1810 debug_print("set rc_dir to %s\n", claws_rc_dir);
1811 if (!is_dir_exist(claws_rc_dir)) {
1812 if (make_dir_hier(claws_rc_dir) != 0) {
1813 g_print("Error: can't create %s\n",
1820 gboolean rc_dir_is_alt(void) {
1824 const gchar *get_mail_base_dir(void)
1827 static gchar *mail_base_dir = NULL;
1830 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1833 return mail_base_dir;
1835 return get_home_dir();
1840 const gchar *prefs_common_get_data_root(void);
1841 gchar *last_data_root = NULL;
1844 const gchar *get_news_cache_dir(void)
1846 static gchar *news_cache_dir = NULL;
1848 const gchar *data_root = prefs_common_get_data_root();
1849 if (strcmp2(data_root, last_data_root)) {
1850 g_free(news_cache_dir);
1851 news_cache_dir = NULL;
1854 if (!news_cache_dir)
1856 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1857 NEWS_CACHE_DIR, NULL);
1861 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1862 "Claws", G_DIR_SEPARATOR_S,
1863 g_get_user_name(), G_DIR_SEPARATOR_S,
1864 NEWS_CACHE_DIR, NULL);
1865 g_free(last_data_root);
1866 last_data_root = g_strdup(last_data_root);
1868 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1869 NEWS_CACHE_DIR, NULL);
1870 g_free(last_data_root);
1871 last_data_root = NULL;
1875 return news_cache_dir;
1878 const gchar *get_imap_cache_dir(void)
1880 static gchar *imap_cache_dir = NULL;
1882 const gchar *data_root = prefs_common_get_data_root();
1883 if (strcmp2(data_root, last_data_root)) {
1884 g_free(imap_cache_dir);
1885 imap_cache_dir = NULL;
1889 if (!imap_cache_dir)
1891 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1892 IMAP_CACHE_DIR, NULL);
1896 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1897 "Claws", G_DIR_SEPARATOR_S,
1898 g_get_user_name(), G_DIR_SEPARATOR_S,
1899 IMAP_CACHE_DIR, NULL);
1900 g_free(last_data_root);
1901 last_data_root = g_strdup(last_data_root);
1903 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 IMAP_CACHE_DIR, NULL);
1905 g_free(last_data_root);
1906 last_data_root = NULL;
1911 return imap_cache_dir;
1914 const gchar *get_mime_tmp_dir(void)
1916 static gchar *mime_tmp_dir = NULL;
1919 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1920 MIME_TMP_DIR, NULL);
1922 return mime_tmp_dir;
1925 const gchar *get_template_dir(void)
1927 static gchar *template_dir = NULL;
1930 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1931 TEMPLATE_DIR, NULL);
1933 return template_dir;
1936 /* Return the default directory for Plugins. */
1937 const gchar *get_plugin_dir(void)
1940 static gchar *plugin_dir = NULL;
1943 plugin_dir = g_strconcat(w32_get_module_dir(),
1944 "\\lib\\claws-mail\\plugins\\",
1948 if (is_dir_exist(PLUGINDIR))
1951 static gchar *plugin_dir = NULL;
1953 plugin_dir = g_strconcat(get_rc_dir(),
1954 G_DIR_SEPARATOR_S, "plugins",
1955 G_DIR_SEPARATOR_S, NULL);
1961 const gchar *get_tmp_dir(void)
1963 static gchar *tmp_dir = NULL;
1966 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1972 gchar *get_tmp_file(void)
1975 static guint32 id = 0;
1977 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1978 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1983 const gchar *get_domain_name(void)
1986 static gchar *domain_name = NULL;
1992 if (gethostname(hostname, sizeof(hostname)) != 0) {
1993 perror("gethostname");
1994 domain_name = "unknown";
1996 hostname[sizeof(hostname) - 1] = '\0';
1997 if ((hp = my_gethostbyname(hostname)) == NULL) {
1998 perror("gethostbyname");
1999 domain_name = g_strdup(hostname);
2001 domain_name = g_strdup(hp->h_name);
2004 debug_print("domain name = %s\n", domain_name);
2013 off_t get_file_size(const gchar *file)
2017 if (g_stat(file, &s) < 0) {
2018 FILE_OP_ERROR(file, "stat");
2025 time_t get_file_mtime(const gchar *file)
2029 if (g_stat(file, &s) < 0) {
2030 FILE_OP_ERROR(file, "stat");
2037 off_t get_file_size_as_crlf(const gchar *file)
2041 gchar buf[BUFFSIZE];
2043 if ((fp = g_fopen(file, "rb")) == NULL) {
2044 FILE_OP_ERROR(file, "fopen");
2048 while (fgets(buf, sizeof(buf), fp) != NULL) {
2050 size += strlen(buf) + 2;
2054 FILE_OP_ERROR(file, "fgets");
2063 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2070 if (g_stat(file, &s) < 0) {
2071 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2075 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2082 /* Test on whether FILE is a relative file name. This is
2083 * straightforward for Unix but more complex for Windows. */
2084 gboolean is_relative_filename(const gchar *file)
2089 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2090 return FALSE; /* Prefixed with a hostname - this can't
2091 * be a relative name. */
2093 if ( ((*file >= 'a' && *file <= 'z')
2094 || (*file >= 'A' && *file <= 'Z'))
2096 file += 2; /* Skip drive letter. */
2098 return !(*file == '\\' || *file == '/');
2100 return !(*file == G_DIR_SEPARATOR);
2105 gboolean is_dir_exist(const gchar *dir)
2110 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2113 gboolean is_file_entry_exist(const gchar *file)
2118 return g_file_test(file, G_FILE_TEST_EXISTS);
2121 gboolean dirent_is_regular_file(struct dirent *d)
2123 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2124 if (d->d_type == DT_REG)
2126 else if (d->d_type != DT_UNKNOWN)
2130 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2133 gint change_dir(const gchar *dir)
2135 gchar *prevdir = NULL;
2138 prevdir = g_get_current_dir();
2140 if (g_chdir(dir) < 0) {
2141 FILE_OP_ERROR(dir, "chdir");
2142 if (debug_mode) g_free(prevdir);
2144 } else if (debug_mode) {
2147 cwd = g_get_current_dir();
2148 if (strcmp(prevdir, cwd) != 0)
2149 g_print("current dir: %s\n", cwd);
2157 gint make_dir(const gchar *dir)
2159 if (g_mkdir(dir, S_IRWXU) < 0) {
2160 FILE_OP_ERROR(dir, "mkdir");
2163 if (g_chmod(dir, S_IRWXU) < 0)
2164 FILE_OP_ERROR(dir, "chmod");
2169 gint make_dir_hier(const gchar *dir)
2174 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2175 parent_dir = g_strndup(dir, p - dir);
2176 if (*parent_dir != '\0') {
2177 if (!is_dir_exist(parent_dir)) {
2178 if (make_dir(parent_dir) < 0) {
2187 if (!is_dir_exist(dir)) {
2188 if (make_dir(dir) < 0)
2195 gint remove_all_files(const gchar *dir)
2198 const gchar *dir_name;
2201 prev_dir = g_get_current_dir();
2203 if (g_chdir(dir) < 0) {
2204 FILE_OP_ERROR(dir, "chdir");
2209 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2210 g_warning("failed to open directory: %s\n", dir);
2215 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2216 if (g_unlink(dir_name) < 0)
2217 FILE_OP_ERROR(dir_name, "unlink");
2222 if (g_chdir(prev_dir) < 0) {
2223 FILE_OP_ERROR(prev_dir, "chdir");
2233 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2236 const gchar *dir_name;
2240 prev_dir = g_get_current_dir();
2242 if (g_chdir(dir) < 0) {
2243 FILE_OP_ERROR(dir, "chdir");
2248 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2249 g_warning("failed to open directory: %s\n", dir);
2254 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2255 file_no = to_number(dir_name);
2256 if (file_no > 0 && first <= file_no && file_no <= last) {
2257 if (is_dir_exist(dir_name))
2259 if (g_unlink(dir_name) < 0)
2260 FILE_OP_ERROR(dir_name, "unlink");
2266 if (g_chdir(prev_dir) < 0) {
2267 FILE_OP_ERROR(prev_dir, "chdir");
2277 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2280 const gchar *dir_name;
2284 prev_dir = g_get_current_dir();
2286 if (g_chdir(dir) < 0) {
2287 FILE_OP_ERROR(dir, "chdir");
2292 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2293 FILE_OP_ERROR(dir, "opendir");
2298 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2299 file_no = to_number(dir_name);
2300 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2301 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2302 if (is_dir_exist(dir_name))
2304 if (g_unlink(dir_name) < 0)
2305 FILE_OP_ERROR(dir_name, "unlink");
2311 if (g_chdir(prev_dir) < 0) {
2312 FILE_OP_ERROR(prev_dir, "chdir");
2322 gint remove_all_numbered_files(const gchar *dir)
2324 return remove_numbered_files(dir, 0, UINT_MAX);
2327 gint remove_dir_recursive(const gchar *dir)
2331 const gchar *dir_name;
2334 if (g_stat(dir, &s) < 0) {
2335 FILE_OP_ERROR(dir, "stat");
2336 if (ENOENT == errno) return 0;
2340 if (!S_ISDIR(s.st_mode)) {
2341 if (g_unlink(dir) < 0) {
2342 FILE_OP_ERROR(dir, "unlink");
2349 prev_dir = g_get_current_dir();
2350 /* g_print("prev_dir = %s\n", prev_dir); */
2352 if (!path_cmp(prev_dir, dir)) {
2354 if (g_chdir("..") < 0) {
2355 FILE_OP_ERROR(dir, "chdir");
2358 prev_dir = g_get_current_dir();
2361 if (g_chdir(dir) < 0) {
2362 FILE_OP_ERROR(dir, "chdir");
2367 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2368 g_warning("failed to open directory: %s\n", dir);
2374 /* remove all files in the directory */
2375 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2376 /* g_print("removing %s\n", dir_name); */
2378 if (is_dir_exist(dir_name)) {
2379 if (remove_dir_recursive(dir_name) < 0) {
2380 g_warning("can't remove directory\n");
2384 if (g_unlink(dir_name) < 0)
2385 FILE_OP_ERROR(dir_name, "unlink");
2391 if (g_chdir(prev_dir) < 0) {
2392 FILE_OP_ERROR(prev_dir, "chdir");
2399 if (g_rmdir(dir) < 0) {
2400 FILE_OP_ERROR(dir, "rmdir");
2407 gint rename_force(const gchar *oldpath, const gchar *newpath)
2410 if (!is_file_entry_exist(oldpath)) {
2414 if (is_file_exist(newpath)) {
2415 if (g_unlink(newpath) < 0)
2416 FILE_OP_ERROR(newpath, "unlink");
2419 return g_rename(oldpath, newpath);
2423 * Append src file body to the tail of dest file.
2424 * Now keep_backup has no effects.
2426 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2428 FILE *src_fp, *dest_fp;
2432 gboolean err = FALSE;
2434 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2435 FILE_OP_ERROR(src, "fopen");
2439 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2440 FILE_OP_ERROR(dest, "fopen");
2445 if (change_file_mode_rw(dest_fp, dest) < 0) {
2446 FILE_OP_ERROR(dest, "chmod");
2447 g_warning("can't change file mode\n");
2450 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2451 if (n_read < sizeof(buf) && ferror(src_fp))
2453 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2454 g_warning("writing to %s failed.\n", dest);
2462 if (ferror(src_fp)) {
2463 FILE_OP_ERROR(src, "fread");
2467 if (fclose(dest_fp) == EOF) {
2468 FILE_OP_ERROR(dest, "fclose");
2480 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2482 FILE *src_fp, *dest_fp;
2485 gchar *dest_bak = NULL;
2486 gboolean err = FALSE;
2488 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2489 FILE_OP_ERROR(src, "fopen");
2492 if (is_file_exist(dest)) {
2493 dest_bak = g_strconcat(dest, ".bak", NULL);
2494 if (rename_force(dest, dest_bak) < 0) {
2495 FILE_OP_ERROR(dest, "rename");
2502 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2503 FILE_OP_ERROR(dest, "fopen");
2506 if (rename_force(dest_bak, dest) < 0)
2507 FILE_OP_ERROR(dest_bak, "rename");
2513 if (change_file_mode_rw(dest_fp, dest) < 0) {
2514 FILE_OP_ERROR(dest, "chmod");
2515 g_warning("can't change file mode\n");
2518 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2519 if (n_read < sizeof(buf) && ferror(src_fp))
2521 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2522 g_warning("writing to %s failed.\n", dest);
2527 if (rename_force(dest_bak, dest) < 0)
2528 FILE_OP_ERROR(dest_bak, "rename");
2535 if (ferror(src_fp)) {
2536 FILE_OP_ERROR(src, "fread");
2540 if (fclose(dest_fp) == EOF) {
2541 FILE_OP_ERROR(dest, "fclose");
2548 if (rename_force(dest_bak, dest) < 0)
2549 FILE_OP_ERROR(dest_bak, "rename");
2555 if (keep_backup == FALSE && dest_bak)
2563 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2565 if (overwrite == FALSE && is_file_exist(dest)) {
2566 g_warning("move_file(): file %s already exists.", dest);
2570 if (rename_force(src, dest) == 0) return 0;
2572 if (EXDEV != errno) {
2573 FILE_OP_ERROR(src, "rename");
2577 if (copy_file(src, dest, FALSE) < 0) return -1;
2584 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2587 gint bytes_left, to_read;
2590 if (fseek(fp, offset, SEEK_SET) < 0) {
2595 bytes_left = length;
2596 to_read = MIN(bytes_left, sizeof(buf));
2598 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2599 if (n_read < to_read && ferror(fp))
2601 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2604 bytes_left -= n_read;
2605 if (bytes_left == 0)
2607 to_read = MIN(bytes_left, sizeof(buf));
2618 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2621 gboolean err = FALSE;
2623 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2624 FILE_OP_ERROR(dest, "fopen");
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 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2636 if (!err && fclose(dest_fp) == EOF) {
2637 FILE_OP_ERROR(dest, "fclose");
2642 g_warning("writing to %s failed.\n", dest);
2650 /* convert line endings into CRLF. If the last line doesn't end with
2651 * linebreak, add it.
2653 gchar *canonicalize_str(const gchar *str)
2659 for (p = str; *p != '\0'; ++p) {
2666 if (p == str || *(p - 1) != '\n')
2669 out = outp = g_malloc(new_len + 1);
2670 for (p = str; *p != '\0'; ++p) {
2677 if (p == str || *(p - 1) != '\n') {
2686 gint canonicalize_file(const gchar *src, const gchar *dest)
2688 FILE *src_fp, *dest_fp;
2689 gchar buf[BUFFSIZE];
2691 gboolean err = FALSE;
2692 gboolean last_linebreak = FALSE;
2694 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2695 FILE_OP_ERROR(src, "fopen");
2699 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2700 FILE_OP_ERROR(dest, "fopen");
2705 if (change_file_mode_rw(dest_fp, dest) < 0) {
2706 FILE_OP_ERROR(dest, "chmod");
2707 g_warning("can't change file mode\n");
2710 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2714 if (len == 0) break;
2715 last_linebreak = FALSE;
2717 if (buf[len - 1] != '\n') {
2718 last_linebreak = TRUE;
2719 r = fputs(buf, dest_fp);
2720 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2721 r = fputs(buf, dest_fp);
2724 r = fwrite(buf, 1, len - 1, dest_fp);
2729 r = fputs("\r\n", dest_fp);
2733 g_warning("writing to %s failed.\n", dest);
2741 if (last_linebreak == TRUE) {
2742 if (fputs("\r\n", dest_fp) == EOF)
2746 if (ferror(src_fp)) {
2747 FILE_OP_ERROR(src, "fgets");
2751 if (fclose(dest_fp) == EOF) {
2752 FILE_OP_ERROR(dest, "fclose");
2764 gint canonicalize_file_replace(const gchar *file)
2768 tmp_file = get_tmp_file();
2770 if (canonicalize_file(file, tmp_file) < 0) {
2775 if (move_file(tmp_file, file, TRUE) < 0) {
2776 g_warning("can't replace %s .\n", file);
2786 gchar *normalize_newlines(const gchar *str)
2788 const gchar *p = str;
2791 out = outp = g_malloc(strlen(str) + 1);
2792 for (p = str; *p != '\0'; ++p) {
2794 if (*(p + 1) != '\n')
2805 gchar *get_outgoing_rfc2822_str(FILE *fp)
2807 gchar buf[BUFFSIZE];
2811 str = g_string_new(NULL);
2813 /* output header part */
2814 while (fgets(buf, sizeof(buf), fp) != NULL) {
2816 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2823 else if (next != ' ' && next != '\t') {
2827 if (fgets(buf, sizeof(buf), fp) == NULL)
2831 g_string_append(str, buf);
2832 g_string_append(str, "\r\n");
2838 /* output body part */
2839 while (fgets(buf, sizeof(buf), fp) != NULL) {
2842 g_string_append_c(str, '.');
2843 g_string_append(str, buf);
2844 g_string_append(str, "\r\n");
2848 g_string_free(str, FALSE);
2854 * Create a new boundary in a way that it is very unlikely that this
2855 * will occur in the following text. It would be easy to ensure
2856 * uniqueness if everything is either quoted-printable or base64
2857 * encoded (note that conversion is allowed), but because MIME bodies
2858 * may be nested, it may happen that the same boundary has already
2861 * boundary := 0*69<bchars> bcharsnospace
2862 * bchars := bcharsnospace / " "
2863 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2864 * "+" / "_" / "," / "-" / "." /
2865 * "/" / ":" / "=" / "?"
2867 * some special characters removed because of buggy MTAs
2870 gchar *generate_mime_boundary(const gchar *prefix)
2872 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2873 "abcdefghijklmnopqrstuvwxyz"
2878 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2879 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2882 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2886 gint change_file_mode_rw(FILE *fp, const gchar *file)
2889 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2891 return g_chmod(file, S_IRUSR|S_IWUSR);
2895 FILE *my_tmpfile(void)
2897 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2898 const gchar suffix[] = ".XXXXXX";
2899 const gchar *tmpdir;
2901 const gchar *progname;
2908 tmpdir = get_tmp_dir();
2909 tmplen = strlen(tmpdir);
2910 progname = g_get_prgname();
2911 if (progname == NULL)
2912 progname = "claws-mail";
2913 proglen = strlen(progname);
2914 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2917 memcpy(fname, tmpdir, tmplen);
2918 fname[tmplen] = G_DIR_SEPARATOR;
2919 memcpy(fname + tmplen + 1, progname, proglen);
2920 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2922 fd = mkstemp(fname);
2929 /* verify that we can write in the file after unlinking */
2930 if (write(fd, buf, 1) < 0) {
2937 fp = fdopen(fd, "w+b");
2945 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2950 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2954 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2955 dir, G_DIR_SEPARATOR);
2956 fd = mkstemp_name(template, filename);
2959 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2960 fd = mkstemp(*filename);
2962 return fdopen(fd, "w+");
2965 FILE *str_open_as_stream(const gchar *str)
2970 g_return_val_if_fail(str != NULL, NULL);
2974 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2979 if (len == 0) return fp;
2981 if (fwrite(str, 1, len, fp) != len) {
2982 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2991 gint str_write_to_file(const gchar *str, const gchar *file)
2996 g_return_val_if_fail(str != NULL, -1);
2997 g_return_val_if_fail(file != NULL, -1);
2999 if ((fp = g_fopen(file, "wb")) == NULL) {
3000 FILE_OP_ERROR(file, "fopen");
3010 if (fwrite(str, 1, len, fp) != len) {
3011 FILE_OP_ERROR(file, "fwrite");
3017 if (fclose(fp) == EOF) {
3018 FILE_OP_ERROR(file, "fclose");
3026 gchar *file_read_to_str(const gchar *file)
3031 g_return_val_if_fail(file != NULL, NULL);
3033 if ((fp = g_fopen(file, "rb")) == NULL) {
3034 FILE_OP_ERROR(file, "fopen");
3038 str = file_read_stream_to_str(fp);
3045 gchar *file_read_stream_to_str(FILE *fp)
3052 g_return_val_if_fail(fp != NULL, NULL);
3054 array = g_byte_array_new();
3056 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3057 if (n_read < sizeof(buf) && ferror(fp))
3059 g_byte_array_append(array, buf, n_read);
3063 FILE_OP_ERROR("file stream", "fread");
3064 g_byte_array_free(array, TRUE);
3069 g_byte_array_append(array, buf, 1);
3070 str = (gchar *)array->data;
3071 g_byte_array_free(array, FALSE);
3073 if (!g_utf8_validate(str, -1, NULL)) {
3074 const gchar *src_codeset, *dest_codeset;
3076 src_codeset = conv_get_locale_charset_str();
3077 dest_codeset = CS_UTF_8;
3078 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3087 char *fgets_crlf(char *buf, int size, FILE *stream)
3089 gboolean is_cr = FALSE;
3090 gboolean last_was_cr = FALSE;
3095 while (--size > 0 && (c = getc(stream)) != EOF)
3098 is_cr = (c == '\r');
3108 last_was_cr = is_cr;
3110 if (c == EOF && cs == buf)
3118 static gint execute_async(gchar *const argv[])
3120 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3122 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3123 NULL, NULL, NULL, FALSE) == FALSE) {
3124 g_warning("Couldn't execute command: %s\n", argv[0]);
3131 static gint execute_sync(gchar *const argv[])
3135 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3137 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3138 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3139 g_warning("Couldn't execute command: %s\n", argv[0]);
3144 if (WIFEXITED(status))
3145 return WEXITSTATUS(status);
3153 gint execute_command_line(const gchar *cmdline, gboolean async)
3158 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3160 argv = strsplit_with_quote(cmdline, " ", 0);
3163 ret = execute_async(argv);
3165 ret = execute_sync(argv);
3172 gchar *get_command_output(const gchar *cmdline)
3174 gchar *child_stdout;
3177 g_return_val_if_fail(cmdline != NULL, NULL);
3179 debug_print("get_command_output(): executing: %s\n", cmdline);
3181 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3183 g_warning("Couldn't execute command: %s\n", cmdline);
3187 return child_stdout;
3190 static gint is_unchanged_uri_char(char c)
3201 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3207 for(i = 0; i < strlen(uri) ; i++) {
3208 if (is_unchanged_uri_char(uri[i])) {
3209 if (k + 2 >= bufsize)
3211 encoded_uri[k++] = uri[i];
3214 char * hexa = "0123456789ABCDEF";
3216 if (k + 4 >= bufsize)
3218 encoded_uri[k++] = '%';
3219 encoded_uri[k++] = hexa[uri[i] / 16];
3220 encoded_uri[k++] = hexa[uri[i] % 16];
3226 gint open_uri(const gchar *uri, const gchar *cmdline)
3229 gchar buf[BUFFSIZE];
3231 gchar encoded_uri[BUFFSIZE];
3232 g_return_val_if_fail(uri != NULL, -1);
3234 /* an option to choose whether to use encode_uri or not ? */
3235 encode_uri(encoded_uri, BUFFSIZE, uri);
3238 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3239 !strchr(p + 2, '%'))
3240 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3243 g_warning("Open URI command line is invalid "
3244 "(there must be only one '%%s'): %s",
3246 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3249 execute_command_line(buf, TRUE);
3251 extern osso_context_t *get_osso_context(void);
3252 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3253 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3254 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3259 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3261 gchar buf[BUFFSIZE];
3264 g_return_val_if_fail(filepath != NULL, -1);
3267 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3268 !strchr(p + 2, '%'))
3269 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3272 g_warning("Open Text Editor command line is invalid "
3273 "(there must be only one '%%s'): %s",
3275 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3278 execute_command_line(buf, TRUE);
3283 time_t remote_tzoffset_sec(const gchar *zone)
3285 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3291 time_t remoteoffset;
3293 strncpy(zone3, zone, 3);
3297 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3298 (c == '+' || c == '-')) {
3299 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3301 remoteoffset = -remoteoffset;
3302 } else if (!strncmp(zone, "UT" , 2) ||
3303 !strncmp(zone, "GMT", 2)) {
3305 } else if (strlen(zone3) == 3) {
3306 for (p = ustzstr; *p != '\0'; p += 3) {
3307 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3308 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3309 remoteoffset = iustz * 3600;
3315 } else if (strlen(zone3) == 1) {
3317 case 'Z': remoteoffset = 0; break;
3318 case 'A': remoteoffset = -1; break;
3319 case 'B': remoteoffset = -2; break;
3320 case 'C': remoteoffset = -3; break;
3321 case 'D': remoteoffset = -4; break;
3322 case 'E': remoteoffset = -5; break;
3323 case 'F': remoteoffset = -6; break;
3324 case 'G': remoteoffset = -7; break;
3325 case 'H': remoteoffset = -8; break;
3326 case 'I': remoteoffset = -9; break;
3327 case 'K': remoteoffset = -10; break; /* J is not used */
3328 case 'L': remoteoffset = -11; break;
3329 case 'M': remoteoffset = -12; break;
3330 case 'N': remoteoffset = 1; break;
3331 case 'O': remoteoffset = 2; break;
3332 case 'P': remoteoffset = 3; break;
3333 case 'Q': remoteoffset = 4; break;
3334 case 'R': remoteoffset = 5; break;
3335 case 'S': remoteoffset = 6; break;
3336 case 'T': remoteoffset = 7; break;
3337 case 'U': remoteoffset = 8; break;
3338 case 'V': remoteoffset = 9; break;
3339 case 'W': remoteoffset = 10; break;
3340 case 'X': remoteoffset = 11; break;
3341 case 'Y': remoteoffset = 12; break;
3342 default: remoteoffset = 0; break;
3344 remoteoffset = remoteoffset * 3600;
3348 return remoteoffset;
3351 time_t tzoffset_sec(time_t *now)
3355 struct tm buf1, buf2;
3357 gmt = *gmtime_r(now, &buf1);
3358 lt = localtime_r(now, &buf2);
3360 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3362 if (lt->tm_year < gmt.tm_year)
3364 else if (lt->tm_year > gmt.tm_year)
3366 else if (lt->tm_yday < gmt.tm_yday)
3368 else if (lt->tm_yday > gmt.tm_yday)
3371 if (off >= 24 * 60) /* should be impossible */
3372 off = 23 * 60 + 59; /* if not, insert silly value */
3373 if (off <= -24 * 60)
3374 off = -(23 * 60 + 59);
3379 /* calculate timezone offset */
3380 gchar *tzoffset(time_t *now)
3382 static gchar offset_string[6];
3386 struct tm buf1, buf2;
3388 gmt = *gmtime_r(now, &buf1);
3389 lt = localtime_r(now, &buf2);
3391 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3393 if (lt->tm_year < gmt.tm_year)
3395 else if (lt->tm_year > gmt.tm_year)
3397 else if (lt->tm_yday < gmt.tm_yday)
3399 else if (lt->tm_yday > gmt.tm_yday)
3407 if (off >= 24 * 60) /* should be impossible */
3408 off = 23 * 60 + 59; /* if not, insert silly value */
3410 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3412 return offset_string;
3415 void get_rfc822_date(gchar *buf, gint len)
3419 gchar day[4], mon[4];
3420 gint dd, hh, mm, ss, yyyy;
3422 gchar buf2[BUFFSIZE];
3425 lt = localtime_r(&t, &buf1);
3427 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3428 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3430 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3431 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3434 void debug_set_mode(gboolean mode)
3439 gboolean debug_get_mode(void)
3444 void debug_print_real(const gchar *format, ...)
3447 gchar buf[BUFFSIZE];
3449 if (!debug_mode) return;
3451 va_start(args, format);
3452 g_vsnprintf(buf, sizeof(buf), format, args);
3459 const char * debug_srcname(const char *file)
3461 const char *s = strrchr (file, '/');
3466 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3468 if (subject == NULL)
3471 subject += subject_get_prefix_length(subject);
3473 return g_hash_table_lookup(subject_table, subject);
3476 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3479 if (subject == NULL || *subject == 0)
3481 subject += subject_get_prefix_length(subject);
3482 g_hash_table_insert(subject_table, subject, data);
3485 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3487 if (subject == NULL)
3490 subject += subject_get_prefix_length(subject);
3491 g_hash_table_remove(subject_table, subject);
3495 *\brief Check if a string is prefixed with known (combinations)
3496 * of prefixes. The function assumes that each prefix
3497 * is terminated by zero or exactly _one_ space.
3499 *\param str String to check for a prefixes
3501 *\return int Number of chars in the prefix that should be skipped
3502 * for a "clean" subject line. If no prefix was found, 0
3505 int subject_get_prefix_length(const gchar *subject)
3507 /*!< Array with allowable reply prefixes regexps. */
3508 static const gchar * const prefixes[] = {
3509 "Re\\:", /* "Re:" */
3510 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3511 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3512 "Aw\\:", /* "Aw:" (German) */
3513 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3514 "Res\\:", /* "Res:" (Brazilian Outlook) */
3515 "Fw\\:", /* "Fw:" Forward */
3516 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3517 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3518 "Rif\\:", /* "Rif:" (Italian Outlook) */
3519 "Sv\\:", /* "Sv" (Norwegian) */
3520 "Vs\\:", /* "Vs" (Norwegian) */
3521 "Ad\\:", /* "Ad" (Norwegian) */
3522 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3525 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3528 static regex_t regex;
3529 static gboolean init_;
3531 if (!subject) return 0;
3532 if (!*subject) return 0;
3535 GString *s = g_string_new("");
3537 for (n = 0; n < PREFIXES; n++)
3538 /* Terminate each prefix regexpression by a
3539 * "\ ?" (zero or ONE space), and OR them */
3540 g_string_append_printf(s, "(%s\\ ?)%s",
3545 g_string_prepend(s, "(");
3546 g_string_append(s, ")+"); /* match at least once */
3547 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3550 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3551 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3552 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3553 debug_print("Error compiling regexp %s\n", s->str);
3554 g_string_free(s, TRUE);
3558 g_string_free(s, TRUE);
3562 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3568 static guint g_stricase_hash(gconstpointer gptr)
3570 guint hash_result = 0;
3573 for (str = gptr; str && *str; str++) {
3574 hash_result += toupper(*str);
3580 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3582 const char *str1 = gptr1;
3583 const char *str2 = gptr2;
3585 return !strcasecmp(str1, str2);
3588 gint g_int_compare(gconstpointer a, gconstpointer b)
3590 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3593 gchar *generate_msgid(gchar *buf, gint len)
3601 lt = localtime_r(&t, &buft);
3603 if (strcmp(buf, "") == 0) {
3604 addr = g_strconcat("@", get_domain_name(), NULL);
3607 addr = g_strconcat("@", buf, NULL);
3610 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3611 lt->tm_year + 1900, lt->tm_mon + 1,
3612 lt->tm_mday, lt->tm_hour,
3613 lt->tm_min, lt->tm_sec,
3614 (guint) rand(), addr);
3621 quote_cmd_argument()
3623 return a quoted string safely usable in argument of a command.
3625 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3628 gint quote_cmd_argument(gchar * result, guint size,
3638 for(p = path ; * p != '\0' ; p ++) {
3640 if (isalnum((guchar)*p) || (* p == '/')) {
3641 if (remaining > 0) {
3647 result[size - 1] = '\0';
3652 if (remaining >= 2) {
3660 result[size - 1] = '\0';
3665 if (remaining > 0) {
3669 result[size - 1] = '\0';
3683 static void g_node_map_recursive(GNode *node, gpointer data)
3685 GNodeMapData *mapdata = (GNodeMapData *) data;
3687 GNodeMapData newmapdata;
3690 newdata = mapdata->func(node->data, mapdata->data);
3691 if (newdata != NULL) {
3692 newnode = g_node_new(newdata);
3693 g_node_append(mapdata->parent, newnode);
3695 newmapdata.parent = newnode;
3696 newmapdata.func = mapdata->func;
3697 newmapdata.data = mapdata->data;
3699 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3703 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3706 GNodeMapData mapdata;
3708 g_return_val_if_fail(node != NULL, NULL);
3709 g_return_val_if_fail(func != NULL, NULL);
3711 root = g_node_new(func(node->data, data));
3713 mapdata.parent = root;
3714 mapdata.func = func;
3715 mapdata.data = data;
3717 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3722 #define HEX_TO_INT(val, hex) \
3726 if ('0' <= c && c <= '9') { \
3728 } else if ('a' <= c && c <= 'f') { \
3729 val = c - 'a' + 10; \
3730 } else if ('A' <= c && c <= 'F') { \
3731 val = c - 'A' + 10; \
3737 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3744 if (hi == -1 || lo == -1)
3747 *out = (hi << 4) + lo;
3751 #define INT_TO_HEX(hex, val) \
3754 hex = '0' + (val); \
3756 hex = 'A' + (val) - 10; \
3759 void get_hex_str(gchar *out, guchar ch)
3763 INT_TO_HEX(hex, ch >> 4);
3765 INT_TO_HEX(hex, ch & 0x0f);
3771 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3773 #define G_PRINT_REF g_print
3777 *\brief Register ref counted pointer. It is based on GBoxed, so should
3778 * work with anything that uses the GType system. The semantics
3779 * are similar to a C++ auto pointer, with the exception that
3780 * C doesn't have automatic closure (calling destructors) when
3781 * exiting a block scope.
3782 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3783 * function directly.
3785 *\return GType A GType type.
3787 GType g_auto_pointer_register(void)
3789 static GType auto_pointer_type;
3790 if (!auto_pointer_type)
3792 g_boxed_type_register_static
3793 ("G_TYPE_AUTO_POINTER",
3794 (GBoxedCopyFunc) g_auto_pointer_copy,
3795 (GBoxedFreeFunc) g_auto_pointer_free);
3796 return auto_pointer_type;
3800 *\brief Structure with g_new() allocated pointer guarded by the
3803 typedef struct AutoPointerRef {
3804 void (*free) (gpointer);
3810 *\brief The auto pointer opaque structure that references the
3811 * pointer guard block.
3813 typedef struct AutoPointer {
3814 AutoPointerRef *ref;
3815 gpointer ptr; /*!< access to protected pointer */
3819 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3823 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3824 * ... when assigning, copying and freeing storage elements
3826 * gtk_list_store_new(N_S_COLUMNS,
3827 * G_TYPE_AUTO_POINTER,
3831 * Template *precious_data = g_new0(Template, 1);
3832 * g_pointer protect = g_auto_pointer_new(precious_data);
3834 * gtk_list_store_set(container, &iter,
3838 * ... the gtk_list_store has copied the pointer and
3839 * ... incremented its reference count, we should free
3840 * ... the auto pointer (in C++ a destructor would do
3841 * ... this for us when leaving block scope)
3843 * g_auto_pointer_free(protect);
3845 * ... gtk_list_store_set() now manages the data. When
3846 * ... *explicitly* requesting a pointer from the list
3847 * ... store, don't forget you get a copy that should be
3848 * ... freed with g_auto_pointer_free() eventually.
3852 *\param pointer Pointer to be guarded.
3854 *\return GAuto * Pointer that should be used in containers with
3857 GAuto *g_auto_pointer_new(gpointer p)
3859 AutoPointerRef *ref;
3865 ref = g_new0(AutoPointerRef, 1);
3866 ptr = g_new0(AutoPointer, 1);
3876 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3882 *\brief Allocate an autopointer using the passed \a free function to
3883 * free the guarded pointer
3885 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3892 aptr = g_auto_pointer_new(p);
3893 aptr->ref->free = free_;
3897 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3899 if (auto_ptr == NULL)
3901 return ((AutoPointer *) auto_ptr)->ptr;
3905 *\brief Copies an auto pointer by. It's mostly not necessary
3906 * to call this function directly, unless you copy/assign
3907 * the guarded pointer.
3909 *\param auto_ptr Auto pointer returned by previous call to
3910 * g_auto_pointer_new_XXX()
3912 *\return gpointer An auto pointer
3914 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3917 AutoPointerRef *ref;
3920 if (auto_ptr == NULL)
3925 newp = g_new0(AutoPointer, 1);
3928 newp->ptr = ref->pointer;
3932 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3938 *\brief Free an auto pointer
3940 void g_auto_pointer_free(GAuto *auto_ptr)
3943 AutoPointerRef *ref;
3945 if (auto_ptr == NULL)
3951 if (--(ref->cnt) == 0) {
3953 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3955 ref->free(ref->pointer);
3960 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3965 void replace_returns(gchar *str)
3970 while (strstr(str, "\n")) {
3971 *strstr(str, "\n") = ' ';
3973 while (strstr(str, "\r")) {
3974 *strstr(str, "\r") = ' ';
3978 /* get_uri_part() - retrieves a URI starting from scanpos.
3979 Returns TRUE if succesful */
3980 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3981 const gchar **bp, const gchar **ep, gboolean hdr)
3984 gint parenthese_cnt = 0;
3986 g_return_val_if_fail(start != NULL, FALSE);
3987 g_return_val_if_fail(scanpos != NULL, FALSE);
3988 g_return_val_if_fail(bp != NULL, FALSE);
3989 g_return_val_if_fail(ep != NULL, FALSE);
3993 /* find end point of URI */
3994 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
3995 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
3996 !IS_ASCII(*(const guchar *)ep_) ||
3997 strchr("[]{}<>\"", *ep_)) {
3999 } else if (strchr("(", *ep_)) {
4001 } else if (strchr(")", *ep_)) {
4002 if (parenthese_cnt > 0)
4009 /* no punctuation at end of string */
4011 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4012 * should pass some URI type to this function and decide on that whether
4013 * to perform punctuation stripping */
4015 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4017 for (; ep_ - 1 > scanpos + 1 &&
4018 IS_REAL_PUNCT(*(ep_ - 1));
4022 #undef IS_REAL_PUNCT
4029 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4031 while (bp && *bp && g_ascii_isspace(*bp))
4033 return g_strndup(bp, ep - bp);
4036 /* valid mail address characters */
4037 #define IS_RFC822_CHAR(ch) \
4041 !g_ascii_isspace(ch) && \
4042 !strchr("(),;<>\"", (ch)))
4044 /* alphabet and number within 7bit ASCII */
4045 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4046 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4048 static GHashTable *create_domain_tab(void)
4050 static const gchar *toplvl_domains [] = {
4052 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4053 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4054 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4055 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4056 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4057 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4058 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4059 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4060 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4061 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4062 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4063 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4064 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4065 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4066 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4067 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4068 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4069 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4070 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4071 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4072 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4073 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4074 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4075 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4076 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4077 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4080 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4082 g_return_val_if_fail(htab, NULL);
4083 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4084 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4088 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4090 const gint MAX_LVL_DOM_NAME_LEN = 6;
4091 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4092 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4095 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4098 for (p = buf; p < m && first < last; *p++ = *first++)
4102 return g_hash_table_lookup(tab, buf) != NULL;
4105 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4106 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4107 const gchar **bp, const gchar **ep, gboolean hdr)
4109 /* more complex than the uri part because we need to scan back and forward starting from
4110 * the scan position. */
4111 gboolean result = FALSE;
4112 const gchar *bp_ = NULL;
4113 const gchar *ep_ = NULL;
4114 static GHashTable *dom_tab;
4115 const gchar *last_dot = NULL;
4116 const gchar *prelast_dot = NULL;
4117 const gchar *last_tld_char = NULL;
4119 /* the informative part of the email address (describing the name
4120 * of the email address owner) may contain quoted parts. the
4121 * closure stack stores the last encountered quotes. */
4122 gchar closure_stack[128];
4123 gchar *ptr = closure_stack;
4125 g_return_val_if_fail(start != NULL, FALSE);
4126 g_return_val_if_fail(scanpos != NULL, FALSE);
4127 g_return_val_if_fail(bp != NULL, FALSE);
4128 g_return_val_if_fail(ep != NULL, FALSE);
4131 const gchar *start_quote = NULL;
4132 const gchar *end_quote = NULL;
4134 /* go to the real start */
4135 if (start[0] == ',')
4137 if (start[0] == ';')
4139 while (start[0] == '\n' || start[0] == '\r')
4141 while (start[0] == ' ' || start[0] == '\t')
4146 /* check if there are quotes (to skip , in them) */
4147 if (*start == '"') {
4148 start_quote = start;
4150 end_quote = strstr(start, "\"");
4156 /* skip anything between quotes */
4157 if (start_quote && end_quote) {
4162 /* find end (either , or ; or end of line) */
4163 if (strstr(start, ",") && strstr(start, ";"))
4164 *ep = strstr(start,",") < strstr(start, ";")
4165 ? strstr(start, ",") : strstr(start, ";");
4166 else if (strstr(start, ","))
4167 *ep = strstr(start, ",");
4168 else if (strstr(start, ";"))
4169 *ep = strstr(start, ";");
4171 *ep = start+strlen(start);
4173 /* go back to real start */
4174 if (start_quote && end_quote) {
4175 start = start_quote;
4178 /* check there's still an @ in that, or search
4179 * further if possible */
4180 if (strstr(start, "@") && strstr(start, "@") < *ep)
4182 else if (*ep < start+strlen(start)) {
4185 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4193 dom_tab = create_domain_tab();
4194 g_return_val_if_fail(dom_tab, FALSE);
4196 /* scan start of address */
4197 for (bp_ = scanpos - 1;
4198 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4201 /* TODO: should start with an alnum? */
4203 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4206 if (bp_ != scanpos) {
4207 /* scan end of address */
4208 for (ep_ = scanpos + 1;
4209 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4211 prelast_dot = last_dot;
4213 if (*(last_dot + 1) == '.') {
4214 if (prelast_dot == NULL)
4216 last_dot = prelast_dot;
4221 /* TODO: really should terminate with an alnum? */
4222 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4227 if (last_dot == NULL)
4229 if (last_dot >= ep_)
4230 last_dot = prelast_dot;
4231 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4235 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4236 if (*last_tld_char == '?')
4239 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4246 if (!result) return FALSE;
4248 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4249 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4250 && IS_RFC822_CHAR(*(ep_ + 3))) {
4251 /* this informative part with an @ in it is
4252 * followed by the email address */
4255 /* go to matching '>' (or next non-rfc822 char, like \n) */
4256 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4259 /* include the bracket */
4260 if (*ep_ == '>') ep_++;
4262 /* include the leading quote */
4270 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4271 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4274 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4275 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4278 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4279 #define IN_STACK() (ptr > closure_stack)
4280 /* has underrun check */
4281 #define POP_STACK() if(IN_STACK()) --ptr
4282 /* has overrun check */
4283 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4284 /* has underrun check */
4285 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4289 /* scan for the informative part. */
4290 for (bp_ -= 2; bp_ >= start; bp_--) {
4291 /* if closure on the stack keep scanning */
4292 if (PEEK_STACK() == *bp_) {
4296 if (*bp_ == '\'' || *bp_ == '"') {
4301 /* if nothing in the closure stack, do the special conditions
4302 * the following if..else expression simply checks whether
4303 * a token is acceptable. if not acceptable, the clause
4304 * should terminate the loop with a 'break' */
4305 if (!PEEK_STACK()) {
4307 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4308 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4309 /* hyphens are allowed, but only in
4311 } else if (strchr(" \"'", *bp_)) {
4312 /* but anything not being a punctiation
4315 break; /* anything else is rejected */
4322 /* scan forward (should start with an alnum) */
4323 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4339 #undef IS_ASCII_ALNUM
4340 #undef IS_RFC822_CHAR
4342 gchar *make_email_string(const gchar *bp, const gchar *ep)
4344 /* returns a mailto: URI; mailto: is also used to detect the
4345 * uri type later on in the button_pressed signal handler */
4349 tmp = g_strndup(bp, ep - bp);
4350 result = g_strconcat("mailto:", tmp, NULL);
4356 gchar *make_http_string(const gchar *bp, const gchar *ep)
4358 /* returns an http: URI; */
4362 while (bp && *bp && g_ascii_isspace(*bp))
4364 tmp = g_strndup(bp, ep - bp);
4365 result = g_strconcat("http://", tmp, NULL);
4371 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4373 FILE *fp = fopen(path, "rb");
4374 gchar buf[BUFFSIZE];
4375 gchar *result = NULL;
4378 while (fgets(buf, sizeof (buf), fp) != NULL) {
4379 gchar **parts = g_strsplit(buf, ";", 3);
4380 gchar *trimmed = parts[0];
4381 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4383 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4384 trimmed[strlen(trimmed)-1] = '\0';
4386 if (!strcmp(trimmed, type)) {
4387 gboolean needsterminal = FALSE;
4388 if (parts[2] && strstr(parts[2], "needsterminal")) {
4389 needsterminal = TRUE;
4391 if (parts[2] && strstr(parts[2], "test=")) {
4392 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4393 gchar *testcmd = orig_testcmd;
4394 if (strstr(testcmd,";"))
4395 *(strstr(testcmd,";")) = '\0';
4396 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4398 while (testcmd[strlen(testcmd)-1] == '\n')
4399 testcmd[strlen(testcmd)-1] = '\0';
4400 while (testcmd[strlen(testcmd)-1] == '\r')
4401 testcmd[strlen(testcmd)-1] = '\0';
4402 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4403 testcmd[strlen(testcmd)-1] = '\0';
4405 if (strstr(testcmd, "%s")) {
4406 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4407 gint res = system(tmp);
4409 g_free(orig_testcmd);
4416 gint res = system(testcmd);
4417 g_free(orig_testcmd);
4427 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4429 while (trimmed[strlen(trimmed)-1] == '\n')
4430 trimmed[strlen(trimmed)-1] = '\0';
4431 while (trimmed[strlen(trimmed)-1] == '\r')
4432 trimmed[strlen(trimmed)-1] = '\0';
4433 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4434 trimmed[strlen(trimmed)-1] = '\0';
4435 result = g_strdup(trimmed);
4438 /* if there are no single quotes around %s, add them.
4439 * '.*%s.*' is ok, as in display 'png:%s'
4441 if (strstr(result, "%s")
4442 && !(strstr(result, "'") < strstr(result,"%s") &&
4443 strstr(strstr(result,"%s"), "'"))) {
4444 gchar *start = g_strdup(result);
4445 gchar *end = g_strdup(strstr(result, "%s")+2);
4447 *strstr(start, "%s") = '\0';
4448 tmp = g_strconcat(start,"'%s'",end, NULL);
4454 if (needsterminal) {
4455 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4466 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4468 gchar *result = NULL;
4472 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4473 result = mailcap_get_command_in_file(path, type, file_to_open);
4477 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4481 void mailcap_update_default(const gchar *type, const gchar *command)
4483 gchar *path = NULL, *outpath = NULL;
4484 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4485 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4486 FILE *fp = fopen(path, "rb");
4487 FILE *outfp = fopen(outpath, "wb");
4488 gchar buf[BUFFSIZE];
4489 gboolean err = FALSE;
4497 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4498 gchar **parts = g_strsplit(buf, ";", 3);
4499 gchar *trimmed = parts[0];
4500 while (trimmed[0] == ' ')
4502 while (trimmed[strlen(trimmed)-1] == ' ')
4503 trimmed[strlen(trimmed)-1] = '\0';
4505 if (!strcmp(trimmed, type)) {
4510 if(fputs(buf, outfp) == EOF) {
4517 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4523 if (fclose(outfp) == EOF)
4527 g_rename(outpath, path);
4533 gint copy_dir(const gchar *src, const gchar *dst)
4538 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4539 g_warning("failed to open directory: %s\n", src);
4543 if (make_dir(dst) < 0)
4546 while ((name = g_dir_read_name(dir)) != NULL) {
4547 gchar *old_file, *new_file;
4548 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4549 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4550 debug_print("copying: %s -> %s\n", old_file, new_file);
4551 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4552 gint r = copy_file(old_file, new_file, TRUE);
4559 /* Windows has no symlinks. Or well, Vista seems to
4560 have something like this but the semantics might be
4561 different. Thus we don't use it under Windows. */
4562 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4565 gchar *target = g_file_read_link(old_file, &error);
4567 r = symlink(target, new_file);
4574 #endif /*G_OS_WIN32*/
4575 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4576 gint r = copy_dir(old_file, new_file);
4587 /* crude test to see if a file is an email. */
4588 gboolean file_is_email (const gchar *filename)
4594 if (filename == NULL)
4596 if ((fp = g_fopen(filename, "rb")) == NULL)
4598 while (i < 60 && score < 3
4599 && fgets(buffer, sizeof (buffer), fp) > 0) {
4600 if (!strncmp(buffer, "From:", strlen("From:")))
4602 if (!strncmp(buffer, "To:", strlen("To:")))
4604 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4609 return (score >= 3);
4612 gboolean sc_g_list_bigger(GList *list, gint max)
4616 while (cur && i <= max+1) {
4623 gboolean sc_g_slist_bigger(GSList *list, gint max)
4627 while (cur && i <= max+1) {
4634 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4635 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4636 NULL, NULL, NULL, NULL, NULL, NULL};
4637 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4638 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4639 NULL, NULL, NULL, NULL, NULL, NULL};
4640 const gchar *s_am_up = NULL;
4641 const gchar *s_pm_up = NULL;
4642 const gchar *s_am_low = NULL;
4643 const gchar *s_pm_low = NULL;
4644 const gchar *def_loc_format = NULL;
4645 const gchar *date_loc_format = NULL;
4646 const gchar *time_loc_format = NULL;
4647 const gchar *time_am_pm = NULL;
4649 static gboolean time_names_init_done = FALSE;
4651 static void init_time_names(void)
4653 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4654 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4655 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4656 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4657 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4658 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4659 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4661 monthnames[0] = Q_("Complete month name for use by strftime|January");
4662 monthnames[1] = Q_("Complete month name for use by strftime|February");
4663 monthnames[2] = Q_("Complete month name for use by strftime|March");
4664 monthnames[3] = Q_("Complete month name for use by strftime|April");
4665 monthnames[4] = Q_("Complete month name for use by strftime|May");
4666 monthnames[5] = Q_("Complete month name for use by strftime|June");
4667 monthnames[6] = Q_("Complete month name for use by strftime|July");
4668 monthnames[7] = Q_("Complete month name for use by strftime|August");
4669 monthnames[8] = Q_("Complete month name for use by strftime|September");
4670 monthnames[9] = Q_("Complete month name for use by strftime|October");
4671 monthnames[10] = Q_("Complete month name for use by strftime|November");
4672 monthnames[11] = Q_("Complete month name for use by strftime|December");
4674 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4675 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4676 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4677 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4678 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4679 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4680 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4682 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4683 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4684 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4685 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4686 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4687 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4688 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4689 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4690 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4691 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4692 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4693 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4695 s_am_up = Q_("For use by strftime (morning)|AM");
4696 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4697 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4698 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4700 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4701 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4702 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4704 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4706 time_names_init_done = TRUE;
4709 #define CHECK_SIZE() { \
4710 total_done += len; \
4711 if (total_done >= buflen) { \
4712 buf[buflen-1] = '\0'; \
4717 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4719 gchar *curpos = buf;
4720 gint total_done = 0;
4721 gchar subbuf[64], subfmt[64];
4722 static time_t last_tzset = (time_t)0;
4724 if (!time_names_init_done)
4727 if (format == NULL || lt == NULL)
4730 if (last_tzset != time(NULL)) {
4732 last_tzset = time(NULL);
4735 if (*format == '%') {
4736 gint len = 0, tmp = 0;
4740 len = 1; CHECK_SIZE();
4744 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4745 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4748 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4749 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4753 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4754 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4757 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4758 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4761 fast_strftime(subbuf, 64, def_loc_format, lt);
4762 len = strlen(subbuf); CHECK_SIZE();
4763 strncpy2(curpos, subbuf, buflen - total_done);
4766 total_done += 2; CHECK_SIZE();
4767 tmp = (lt->tm_year + 1900)/100;
4768 *curpos++ = '0'+(tmp / 10);
4769 *curpos++ = '0'+(tmp % 10);
4772 total_done += 2; CHECK_SIZE();
4773 *curpos++ = '0'+(lt->tm_mday / 10);
4774 *curpos++ = '0'+(lt->tm_mday % 10);
4777 total_done += 8; CHECK_SIZE();
4778 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4779 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4781 *curpos++ = '0'+(lt->tm_mday / 10);
4782 *curpos++ = '0'+(lt->tm_mday % 10);
4784 tmp = lt->tm_year%100;
4785 *curpos++ = '0'+(tmp / 10);
4786 *curpos++ = '0'+(tmp % 10);
4789 len = 2; CHECK_SIZE();
4790 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4793 len = 10; CHECK_SIZE();
4794 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4795 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4798 total_done += 2; CHECK_SIZE();
4799 *curpos++ = '0'+(lt->tm_hour / 10);
4800 *curpos++ = '0'+(lt->tm_hour % 10);
4803 total_done += 2; CHECK_SIZE();
4809 *curpos++ = '0'+(tmp / 10);
4810 *curpos++ = '0'+(tmp % 10);
4813 len = 3; CHECK_SIZE();
4814 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4817 len = 2; CHECK_SIZE();
4818 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4821 len = 2; CHECK_SIZE();
4827 snprintf(curpos, buflen - total_done, "%2d", tmp);
4830 total_done += 2; CHECK_SIZE();
4831 tmp = lt->tm_mon + 1;
4832 *curpos++ = '0'+(tmp / 10);
4833 *curpos++ = '0'+(tmp % 10);
4836 total_done += 2; CHECK_SIZE();
4837 *curpos++ = '0'+(lt->tm_min / 10);
4838 *curpos++ = '0'+(lt->tm_min % 10);
4841 len = 1; CHECK_SIZE();
4845 if (lt->tm_hour >= 12) {
4846 len = strlen(s_pm_up); CHECK_SIZE();
4847 snprintf(curpos, buflen-total_done, s_pm_up);
4849 len = strlen(s_am_up); CHECK_SIZE();
4850 snprintf(curpos, buflen-total_done, s_am_up);
4854 if (lt->tm_hour >= 12) {
4855 len = strlen(s_pm_low); CHECK_SIZE();
4856 snprintf(curpos, buflen-total_done, s_pm_low);
4858 len = strlen(s_am_low); CHECK_SIZE();
4859 snprintf(curpos, buflen-total_done, s_am_low);
4863 fast_strftime(subbuf, 64, time_am_pm, lt);
4864 len = strlen(subbuf); CHECK_SIZE();
4865 strncpy2(curpos, subbuf, buflen - total_done);
4868 total_done += 5; CHECK_SIZE();
4869 *curpos++ = '0'+(lt->tm_hour / 10);
4870 *curpos++ = '0'+(lt->tm_hour % 10);
4872 *curpos++ = '0'+(lt->tm_min / 10);
4873 *curpos++ = '0'+(lt->tm_min % 10);
4876 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4877 len = strlen(subbuf); CHECK_SIZE();
4878 strncpy2(curpos, subbuf, buflen - total_done);
4881 total_done += 2; CHECK_SIZE();
4882 *curpos++ = '0'+(lt->tm_sec / 10);
4883 *curpos++ = '0'+(lt->tm_sec % 10);
4886 len = 1; CHECK_SIZE();
4890 total_done += 8; CHECK_SIZE();
4891 *curpos++ = '0'+(lt->tm_hour / 10);
4892 *curpos++ = '0'+(lt->tm_hour % 10);
4894 *curpos++ = '0'+(lt->tm_min / 10);
4895 *curpos++ = '0'+(lt->tm_min % 10);
4897 *curpos++ = '0'+(lt->tm_sec / 10);
4898 *curpos++ = '0'+(lt->tm_sec % 10);
4901 len = 1; CHECK_SIZE();
4902 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4905 len = 1; CHECK_SIZE();
4906 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4909 fast_strftime(subbuf, 64, date_loc_format, lt);
4910 len = strlen(subbuf); CHECK_SIZE();
4911 strncpy2(curpos, subbuf, buflen - total_done);
4914 fast_strftime(subbuf, 64, time_loc_format, lt);
4915 len = strlen(subbuf); CHECK_SIZE();
4916 strncpy2(curpos, subbuf, buflen - total_done);
4919 total_done += 2; CHECK_SIZE();
4920 tmp = lt->tm_year%100;
4921 *curpos++ = '0'+(tmp / 10);
4922 *curpos++ = '0'+(tmp % 10);
4925 len = 4; CHECK_SIZE();
4926 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4936 /* let these complicated ones be done with the libc */
4937 snprintf(subfmt, 64, "%%%c", *format);
4938 strftime(subbuf, 64, subfmt, lt);
4939 len = strlen(subbuf); CHECK_SIZE();
4940 strncpy2(curpos, subbuf, buflen - total_done);
4944 /* let these complicated modifiers be done with the libc */
4945 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4946 strftime(subbuf, 64, subfmt, lt);
4947 len = strlen(subbuf); CHECK_SIZE();
4948 strncpy2(curpos, subbuf, buflen - total_done);
4952 g_warning("format error (%c)", *format);
4959 int len = 1; CHECK_SIZE();
4960 *curpos++ = *format++;