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;
1615 #include <windows.h>
1616 #ifndef CSIDL_APPDATA
1617 #define CSIDL_APPDATA 0x001a
1619 #ifndef CSIDL_LOCAL_APPDATA
1620 #define CSIDL_LOCAL_APPDATA 0x001c
1622 #ifndef CSIDL_FLAG_CREATE
1623 #define CSIDL_FLAG_CREATE 0x8000
1625 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1629 w32_strerror (int w32_errno)
1631 static char strerr[256];
1632 int ec = (int)GetLastError ();
1636 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1637 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1638 strerr, DIM (strerr)-1, NULL);
1642 static __inline__ void *
1643 dlopen (const char * name, int flag)
1645 void * hd = LoadLibrary (name);
1649 static __inline__ void *
1650 dlsym (void * hd, const char * sym)
1654 void * fnc = GetProcAddress (hd, sym);
1663 static __inline__ const char *
1666 return w32_strerror (0);
1670 static __inline__ int
1682 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1684 static int initialized;
1685 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1689 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1695 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1697 handle = dlopen (dllnames[i], RTLD_LAZY);
1700 func = dlsym (handle, "SHGetFolderPathA");
1711 return func (a,b,c,d,e);
1716 /* Returns a static string with the directroy from which the module
1717 has been loaded. Returns an empty string on error. */
1718 static char *w32_get_module_dir(void)
1720 static char *moddir;
1723 char name[MAX_PATH+10];
1726 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1729 p = strrchr (name, '\\');
1735 moddir = g_strdup (name);
1739 #endif /* G_OS_WIN32 */
1741 /* Return a static string with the locale dir. */
1742 const gchar *get_locale_dir(void)
1744 static gchar *loc_dir;
1748 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1749 "\\share\\locale", NULL);
1752 loc_dir = LOCALEDIR;
1758 const gchar *get_home_dir(void)
1761 static char home_dir[MAX_PATH] = "";
1763 if (home_dir[0] == '\0') {
1764 if (w32_shgetfolderpath
1765 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1766 NULL, 0, home_dir) < 0)
1767 strcpy (home_dir, "C:\\Sylpheed");
1771 static const gchar *homeenv = NULL;
1776 if (!homeenv && g_getenv("HOME") != NULL)
1777 homeenv = g_strdup(g_getenv("HOME"));
1779 homeenv = g_get_home_dir();
1785 static gchar *claws_rc_dir = NULL;
1786 static gboolean rc_dir_alt = FALSE;
1787 const gchar *get_rc_dir(void)
1791 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1794 return claws_rc_dir;
1797 void set_rc_dir(const gchar *dir)
1799 if (claws_rc_dir != NULL) {
1800 g_print("Error: rc_dir already set\n");
1803 if (g_path_is_absolute(dir))
1804 claws_rc_dir = g_strdup(dir);
1806 claws_rc_dir = g_strconcat(g_get_current_dir(),
1807 G_DIR_SEPARATOR_S, dir, NULL);
1809 debug_print("set rc_dir to %s\n", claws_rc_dir);
1810 if (!is_dir_exist(claws_rc_dir)) {
1811 if (make_dir_hier(claws_rc_dir) != 0) {
1812 g_print("Error: can't create %s\n",
1819 gboolean rc_dir_is_alt(void) {
1823 const gchar *get_mail_base_dir(void)
1826 static gchar *mail_base_dir = NULL;
1829 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1832 return mail_base_dir;
1834 return get_home_dir();
1839 const gchar *prefs_common_get_data_root(void);
1840 gchar *last_data_root = NULL;
1843 const gchar *get_news_cache_dir(void)
1845 static gchar *news_cache_dir = NULL;
1847 const gchar *data_root = prefs_common_get_data_root();
1848 if (strcmp2(data_root, last_data_root)) {
1849 g_free(news_cache_dir);
1850 news_cache_dir = NULL;
1853 if (!news_cache_dir)
1855 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1856 NEWS_CACHE_DIR, NULL);
1860 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1861 "Claws", G_DIR_SEPARATOR_S,
1862 g_get_user_name(), G_DIR_SEPARATOR_S,
1863 NEWS_CACHE_DIR, NULL);
1864 g_free(last_data_root);
1865 last_data_root = g_strdup(last_data_root);
1867 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1868 NEWS_CACHE_DIR, NULL);
1869 g_free(last_data_root);
1870 last_data_root = NULL;
1874 return news_cache_dir;
1877 const gchar *get_imap_cache_dir(void)
1879 static gchar *imap_cache_dir = NULL;
1881 const gchar *data_root = prefs_common_get_data_root();
1882 if (strcmp2(data_root, last_data_root)) {
1883 g_free(imap_cache_dir);
1884 imap_cache_dir = NULL;
1888 if (!imap_cache_dir)
1890 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1891 IMAP_CACHE_DIR, NULL);
1895 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1896 "Claws", G_DIR_SEPARATOR_S,
1897 g_get_user_name(), G_DIR_SEPARATOR_S,
1898 IMAP_CACHE_DIR, NULL);
1899 g_free(last_data_root);
1900 last_data_root = g_strdup(last_data_root);
1902 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1903 IMAP_CACHE_DIR, NULL);
1904 g_free(last_data_root);
1905 last_data_root = NULL;
1910 return imap_cache_dir;
1913 const gchar *get_mime_tmp_dir(void)
1915 static gchar *mime_tmp_dir = NULL;
1918 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1919 MIME_TMP_DIR, NULL);
1921 return mime_tmp_dir;
1924 const gchar *get_template_dir(void)
1926 static gchar *template_dir = NULL;
1929 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1930 TEMPLATE_DIR, NULL);
1932 return template_dir;
1935 /* Return the default directory for Plugins. */
1936 const gchar *get_plugin_dir(void)
1939 static gchar *plugin_dir = NULL;
1942 plugin_dir = g_strconcat(w32_get_module_dir(),
1943 "\\lib\\claws-mail\\plugins\\",
1947 if (is_dir_exist(PLUGINDIR))
1950 static gchar *plugin_dir = NULL;
1952 plugin_dir = g_strconcat(get_rc_dir(),
1953 G_DIR_SEPARATOR_S, "plugins",
1954 G_DIR_SEPARATOR_S, NULL);
1960 const gchar *get_tmp_dir(void)
1962 static gchar *tmp_dir = NULL;
1965 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1971 gchar *get_tmp_file(void)
1974 static guint32 id = 0;
1976 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1977 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1982 const gchar *get_domain_name(void)
1985 static gchar *domain_name = NULL;
1991 if (gethostname(hostname, sizeof(hostname)) != 0) {
1992 perror("gethostname");
1993 domain_name = "unknown";
1995 hostname[sizeof(hostname) - 1] = '\0';
1996 if ((hp = my_gethostbyname(hostname)) == NULL) {
1997 perror("gethostbyname");
1998 domain_name = g_strdup(hostname);
2000 domain_name = g_strdup(hp->h_name);
2003 debug_print("domain name = %s\n", domain_name);
2012 off_t get_file_size(const gchar *file)
2016 if (g_stat(file, &s) < 0) {
2017 FILE_OP_ERROR(file, "stat");
2024 time_t get_file_mtime(const gchar *file)
2028 if (g_stat(file, &s) < 0) {
2029 FILE_OP_ERROR(file, "stat");
2036 off_t get_file_size_as_crlf(const gchar *file)
2040 gchar buf[BUFFSIZE];
2042 if ((fp = g_fopen(file, "rb")) == NULL) {
2043 FILE_OP_ERROR(file, "fopen");
2047 while (fgets(buf, sizeof(buf), fp) != NULL) {
2049 size += strlen(buf) + 2;
2053 FILE_OP_ERROR(file, "fgets");
2062 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2069 if (g_stat(file, &s) < 0) {
2070 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2074 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2081 /* Test on whether FILE is a relative file name. This is
2082 * straightforward for Unix but more complex for Windows. */
2083 gboolean is_relative_filename(const gchar *file)
2088 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2089 return FALSE; /* Prefixed with a hostname - this can't
2090 * be a relative name. */
2092 if ( ((*file >= 'a' && *file <= 'z')
2093 || (*file >= 'A' && *file <= 'Z'))
2095 file += 2; /* Skip drive letter. */
2097 return !(*file == '\\' || *file == '/');
2099 return !(*file == G_DIR_SEPARATOR);
2104 gboolean is_dir_exist(const gchar *dir)
2109 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2112 gboolean is_file_entry_exist(const gchar *file)
2117 return g_file_test(file, G_FILE_TEST_EXISTS);
2120 gboolean dirent_is_regular_file(struct dirent *d)
2122 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2123 if (d->d_type == DT_REG)
2125 else if (d->d_type != DT_UNKNOWN)
2129 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2132 gint change_dir(const gchar *dir)
2134 gchar *prevdir = NULL;
2137 prevdir = g_get_current_dir();
2139 if (g_chdir(dir) < 0) {
2140 FILE_OP_ERROR(dir, "chdir");
2141 if (debug_mode) g_free(prevdir);
2143 } else if (debug_mode) {
2146 cwd = g_get_current_dir();
2147 if (strcmp(prevdir, cwd) != 0)
2148 g_print("current dir: %s\n", cwd);
2156 gint make_dir(const gchar *dir)
2158 if (g_mkdir(dir, S_IRWXU) < 0) {
2159 FILE_OP_ERROR(dir, "mkdir");
2162 if (g_chmod(dir, S_IRWXU) < 0)
2163 FILE_OP_ERROR(dir, "chmod");
2168 gint make_dir_hier(const gchar *dir)
2173 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2174 parent_dir = g_strndup(dir, p - dir);
2175 if (*parent_dir != '\0') {
2176 if (!is_dir_exist(parent_dir)) {
2177 if (make_dir(parent_dir) < 0) {
2186 if (!is_dir_exist(dir)) {
2187 if (make_dir(dir) < 0)
2194 gint remove_all_files(const gchar *dir)
2197 const gchar *dir_name;
2200 prev_dir = g_get_current_dir();
2202 if (g_chdir(dir) < 0) {
2203 FILE_OP_ERROR(dir, "chdir");
2208 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2209 g_warning("failed to open directory: %s\n", dir);
2214 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2215 if (g_unlink(dir_name) < 0)
2216 FILE_OP_ERROR(dir_name, "unlink");
2221 if (g_chdir(prev_dir) < 0) {
2222 FILE_OP_ERROR(prev_dir, "chdir");
2232 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2235 const gchar *dir_name;
2239 prev_dir = g_get_current_dir();
2241 if (g_chdir(dir) < 0) {
2242 FILE_OP_ERROR(dir, "chdir");
2247 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2248 g_warning("failed to open directory: %s\n", dir);
2253 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2254 file_no = to_number(dir_name);
2255 if (file_no > 0 && first <= file_no && file_no <= last) {
2256 if (is_dir_exist(dir_name))
2258 if (g_unlink(dir_name) < 0)
2259 FILE_OP_ERROR(dir_name, "unlink");
2265 if (g_chdir(prev_dir) < 0) {
2266 FILE_OP_ERROR(prev_dir, "chdir");
2276 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2279 const gchar *dir_name;
2283 prev_dir = g_get_current_dir();
2285 if (g_chdir(dir) < 0) {
2286 FILE_OP_ERROR(dir, "chdir");
2291 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2292 FILE_OP_ERROR(dir, "opendir");
2297 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2298 file_no = to_number(dir_name);
2299 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2300 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2301 if (is_dir_exist(dir_name))
2303 if (g_unlink(dir_name) < 0)
2304 FILE_OP_ERROR(dir_name, "unlink");
2310 if (g_chdir(prev_dir) < 0) {
2311 FILE_OP_ERROR(prev_dir, "chdir");
2321 gint remove_all_numbered_files(const gchar *dir)
2323 return remove_numbered_files(dir, 0, UINT_MAX);
2326 gint remove_dir_recursive(const gchar *dir)
2330 const gchar *dir_name;
2333 if (g_stat(dir, &s) < 0) {
2334 FILE_OP_ERROR(dir, "stat");
2335 if (ENOENT == errno) return 0;
2339 if (!S_ISDIR(s.st_mode)) {
2340 if (g_unlink(dir) < 0) {
2341 FILE_OP_ERROR(dir, "unlink");
2348 prev_dir = g_get_current_dir();
2349 /* g_print("prev_dir = %s\n", prev_dir); */
2351 if (!path_cmp(prev_dir, dir)) {
2353 if (g_chdir("..") < 0) {
2354 FILE_OP_ERROR(dir, "chdir");
2357 prev_dir = g_get_current_dir();
2360 if (g_chdir(dir) < 0) {
2361 FILE_OP_ERROR(dir, "chdir");
2366 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2367 g_warning("failed to open directory: %s\n", dir);
2373 /* remove all files in the directory */
2374 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2375 /* g_print("removing %s\n", dir_name); */
2377 if (is_dir_exist(dir_name)) {
2378 if (remove_dir_recursive(dir_name) < 0) {
2379 g_warning("can't remove directory\n");
2383 if (g_unlink(dir_name) < 0)
2384 FILE_OP_ERROR(dir_name, "unlink");
2390 if (g_chdir(prev_dir) < 0) {
2391 FILE_OP_ERROR(prev_dir, "chdir");
2398 if (g_rmdir(dir) < 0) {
2399 FILE_OP_ERROR(dir, "rmdir");
2406 gint rename_force(const gchar *oldpath, const gchar *newpath)
2409 if (!is_file_entry_exist(oldpath)) {
2413 if (is_file_exist(newpath)) {
2414 if (g_unlink(newpath) < 0)
2415 FILE_OP_ERROR(newpath, "unlink");
2418 return g_rename(oldpath, newpath);
2422 * Append src file body to the tail of dest file.
2423 * Now keep_backup has no effects.
2425 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2427 FILE *src_fp, *dest_fp;
2431 gboolean err = FALSE;
2433 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2434 FILE_OP_ERROR(src, "fopen");
2438 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2439 FILE_OP_ERROR(dest, "fopen");
2444 if (change_file_mode_rw(dest_fp, dest) < 0) {
2445 FILE_OP_ERROR(dest, "chmod");
2446 g_warning("can't change file mode\n");
2449 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2450 if (n_read < sizeof(buf) && ferror(src_fp))
2452 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2453 g_warning("writing to %s failed.\n", dest);
2461 if (ferror(src_fp)) {
2462 FILE_OP_ERROR(src, "fread");
2466 if (fclose(dest_fp) == EOF) {
2467 FILE_OP_ERROR(dest, "fclose");
2479 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2481 FILE *src_fp, *dest_fp;
2484 gchar *dest_bak = NULL;
2485 gboolean err = FALSE;
2487 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2488 FILE_OP_ERROR(src, "fopen");
2491 if (is_file_exist(dest)) {
2492 dest_bak = g_strconcat(dest, ".bak", NULL);
2493 if (rename_force(dest, dest_bak) < 0) {
2494 FILE_OP_ERROR(dest, "rename");
2501 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2502 FILE_OP_ERROR(dest, "fopen");
2505 if (rename_force(dest_bak, dest) < 0)
2506 FILE_OP_ERROR(dest_bak, "rename");
2512 if (change_file_mode_rw(dest_fp, dest) < 0) {
2513 FILE_OP_ERROR(dest, "chmod");
2514 g_warning("can't change file mode\n");
2517 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2518 if (n_read < sizeof(buf) && ferror(src_fp))
2520 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2521 g_warning("writing to %s failed.\n", dest);
2526 if (rename_force(dest_bak, dest) < 0)
2527 FILE_OP_ERROR(dest_bak, "rename");
2534 if (ferror(src_fp)) {
2535 FILE_OP_ERROR(src, "fread");
2539 if (fclose(dest_fp) == EOF) {
2540 FILE_OP_ERROR(dest, "fclose");
2547 if (rename_force(dest_bak, dest) < 0)
2548 FILE_OP_ERROR(dest_bak, "rename");
2554 if (keep_backup == FALSE && dest_bak)
2562 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2564 if (overwrite == FALSE && is_file_exist(dest)) {
2565 g_warning("move_file(): file %s already exists.", dest);
2569 if (rename_force(src, dest) == 0) return 0;
2571 if (EXDEV != errno) {
2572 FILE_OP_ERROR(src, "rename");
2576 if (copy_file(src, dest, FALSE) < 0) return -1;
2583 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2586 gint bytes_left, to_read;
2589 if (fseek(fp, offset, SEEK_SET) < 0) {
2594 bytes_left = length;
2595 to_read = MIN(bytes_left, sizeof(buf));
2597 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2598 if (n_read < to_read && ferror(fp))
2600 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2603 bytes_left -= n_read;
2604 if (bytes_left == 0)
2606 to_read = MIN(bytes_left, sizeof(buf));
2617 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2620 gboolean err = FALSE;
2622 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2623 FILE_OP_ERROR(dest, "fopen");
2627 if (change_file_mode_rw(dest_fp, dest) < 0) {
2628 FILE_OP_ERROR(dest, "chmod");
2629 g_warning("can't change file mode\n");
2632 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2635 if (!err && fclose(dest_fp) == EOF) {
2636 FILE_OP_ERROR(dest, "fclose");
2641 g_warning("writing to %s failed.\n", dest);
2649 /* convert line endings into CRLF. If the last line doesn't end with
2650 * linebreak, add it.
2652 gchar *canonicalize_str(const gchar *str)
2658 for (p = str; *p != '\0'; ++p) {
2665 if (p == str || *(p - 1) != '\n')
2668 out = outp = g_malloc(new_len + 1);
2669 for (p = str; *p != '\0'; ++p) {
2676 if (p == str || *(p - 1) != '\n') {
2685 gint canonicalize_file(const gchar *src, const gchar *dest)
2687 FILE *src_fp, *dest_fp;
2688 gchar buf[BUFFSIZE];
2690 gboolean err = FALSE;
2691 gboolean last_linebreak = FALSE;
2693 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2694 FILE_OP_ERROR(src, "fopen");
2698 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2699 FILE_OP_ERROR(dest, "fopen");
2704 if (change_file_mode_rw(dest_fp, dest) < 0) {
2705 FILE_OP_ERROR(dest, "chmod");
2706 g_warning("can't change file mode\n");
2709 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2713 if (len == 0) break;
2714 last_linebreak = FALSE;
2716 if (buf[len - 1] != '\n') {
2717 last_linebreak = TRUE;
2718 r = fputs(buf, dest_fp);
2719 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2720 r = fputs(buf, dest_fp);
2723 r = fwrite(buf, 1, len - 1, dest_fp);
2728 r = fputs("\r\n", dest_fp);
2732 g_warning("writing to %s failed.\n", dest);
2740 if (last_linebreak == TRUE) {
2741 if (fputs("\r\n", dest_fp) == EOF)
2745 if (ferror(src_fp)) {
2746 FILE_OP_ERROR(src, "fgets");
2750 if (fclose(dest_fp) == EOF) {
2751 FILE_OP_ERROR(dest, "fclose");
2763 gint canonicalize_file_replace(const gchar *file)
2767 tmp_file = get_tmp_file();
2769 if (canonicalize_file(file, tmp_file) < 0) {
2774 if (move_file(tmp_file, file, TRUE) < 0) {
2775 g_warning("can't replace %s .\n", file);
2785 gchar *normalize_newlines(const gchar *str)
2787 const gchar *p = str;
2790 out = outp = g_malloc(strlen(str) + 1);
2791 for (p = str; *p != '\0'; ++p) {
2793 if (*(p + 1) != '\n')
2804 gchar *get_outgoing_rfc2822_str(FILE *fp)
2806 gchar buf[BUFFSIZE];
2810 str = g_string_new(NULL);
2812 /* output header part */
2813 while (fgets(buf, sizeof(buf), fp) != NULL) {
2815 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2822 else if (next != ' ' && next != '\t') {
2826 if (fgets(buf, sizeof(buf), fp) == NULL)
2830 g_string_append(str, buf);
2831 g_string_append(str, "\r\n");
2837 /* output body part */
2838 while (fgets(buf, sizeof(buf), fp) != NULL) {
2841 g_string_append_c(str, '.');
2842 g_string_append(str, buf);
2843 g_string_append(str, "\r\n");
2847 g_string_free(str, FALSE);
2853 * Create a new boundary in a way that it is very unlikely that this
2854 * will occur in the following text. It would be easy to ensure
2855 * uniqueness if everything is either quoted-printable or base64
2856 * encoded (note that conversion is allowed), but because MIME bodies
2857 * may be nested, it may happen that the same boundary has already
2860 * boundary := 0*69<bchars> bcharsnospace
2861 * bchars := bcharsnospace / " "
2862 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2863 * "+" / "_" / "," / "-" / "." /
2864 * "/" / ":" / "=" / "?"
2866 * some special characters removed because of buggy MTAs
2869 gchar *generate_mime_boundary(const gchar *prefix)
2871 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2872 "abcdefghijklmnopqrstuvwxyz"
2877 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2878 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2881 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2885 gint change_file_mode_rw(FILE *fp, const gchar *file)
2888 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2890 return g_chmod(file, S_IRUSR|S_IWUSR);
2894 FILE *my_tmpfile(void)
2896 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2897 const gchar suffix[] = ".XXXXXX";
2898 const gchar *tmpdir;
2900 const gchar *progname;
2907 tmpdir = get_tmp_dir();
2908 tmplen = strlen(tmpdir);
2909 progname = g_get_prgname();
2910 if (progname == NULL)
2911 progname = "claws-mail";
2912 proglen = strlen(progname);
2913 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2916 memcpy(fname, tmpdir, tmplen);
2917 fname[tmplen] = G_DIR_SEPARATOR;
2918 memcpy(fname + tmplen + 1, progname, proglen);
2919 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2921 fd = mkstemp(fname);
2928 /* verify that we can write in the file after unlinking */
2929 if (write(fd, buf, 1) < 0) {
2936 fp = fdopen(fd, "w+b");
2944 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2949 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2953 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2954 dir, G_DIR_SEPARATOR);
2955 fd = mkstemp_name(template, filename);
2958 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2959 fd = mkstemp(*filename);
2961 return fdopen(fd, "w+");
2964 FILE *str_open_as_stream(const gchar *str)
2969 g_return_val_if_fail(str != NULL, NULL);
2973 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2978 if (len == 0) return fp;
2980 if (fwrite(str, 1, len, fp) != len) {
2981 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2990 gint str_write_to_file(const gchar *str, const gchar *file)
2995 g_return_val_if_fail(str != NULL, -1);
2996 g_return_val_if_fail(file != NULL, -1);
2998 if ((fp = g_fopen(file, "wb")) == NULL) {
2999 FILE_OP_ERROR(file, "fopen");
3009 if (fwrite(str, 1, len, fp) != len) {
3010 FILE_OP_ERROR(file, "fwrite");
3016 if (fclose(fp) == EOF) {
3017 FILE_OP_ERROR(file, "fclose");
3025 gchar *file_read_to_str(const gchar *file)
3030 g_return_val_if_fail(file != NULL, NULL);
3032 if ((fp = g_fopen(file, "rb")) == NULL) {
3033 FILE_OP_ERROR(file, "fopen");
3037 str = file_read_stream_to_str(fp);
3044 gchar *file_read_stream_to_str(FILE *fp)
3051 g_return_val_if_fail(fp != NULL, NULL);
3053 array = g_byte_array_new();
3055 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3056 if (n_read < sizeof(buf) && ferror(fp))
3058 g_byte_array_append(array, buf, n_read);
3062 FILE_OP_ERROR("file stream", "fread");
3063 g_byte_array_free(array, TRUE);
3068 g_byte_array_append(array, buf, 1);
3069 str = (gchar *)array->data;
3070 g_byte_array_free(array, FALSE);
3072 if (!g_utf8_validate(str, -1, NULL)) {
3073 const gchar *src_codeset, *dest_codeset;
3075 src_codeset = conv_get_locale_charset_str();
3076 dest_codeset = CS_UTF_8;
3077 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3086 char *fgets_crlf(char *buf, int size, FILE *stream)
3088 gboolean is_cr = FALSE;
3089 gboolean last_was_cr = FALSE;
3094 while (--size > 0 && (c = getc(stream)) != EOF)
3097 is_cr = (c == '\r');
3107 last_was_cr = is_cr;
3109 if (c == EOF && cs == buf)
3117 static gint execute_async(gchar *const argv[])
3119 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3121 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3122 NULL, NULL, NULL, FALSE) == FALSE) {
3123 g_warning("Couldn't execute command: %s\n", argv[0]);
3130 static gint execute_sync(gchar *const argv[])
3134 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3136 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3137 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3138 g_warning("Couldn't execute command: %s\n", argv[0]);
3143 if (WIFEXITED(status))
3144 return WEXITSTATUS(status);
3152 gint execute_command_line(const gchar *cmdline, gboolean async)
3157 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3159 argv = strsplit_with_quote(cmdline, " ", 0);
3162 ret = execute_async(argv);
3164 ret = execute_sync(argv);
3171 gchar *get_command_output(const gchar *cmdline)
3173 gchar *child_stdout;
3176 g_return_val_if_fail(cmdline != NULL, NULL);
3178 debug_print("get_command_output(): executing: %s\n", cmdline);
3180 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3182 g_warning("Couldn't execute command: %s\n", cmdline);
3186 return child_stdout;
3189 static gint is_unchanged_uri_char(char c)
3200 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3206 for(i = 0; i < strlen(uri) ; i++) {
3207 if (is_unchanged_uri_char(uri[i])) {
3208 if (k + 2 >= bufsize)
3210 encoded_uri[k++] = uri[i];
3213 char * hexa = "0123456789ABCDEF";
3215 if (k + 4 >= bufsize)
3217 encoded_uri[k++] = '%';
3218 encoded_uri[k++] = hexa[uri[i] / 16];
3219 encoded_uri[k++] = hexa[uri[i] % 16];
3225 gint open_uri(const gchar *uri, const gchar *cmdline)
3228 gchar buf[BUFFSIZE];
3230 gchar encoded_uri[BUFFSIZE];
3231 g_return_val_if_fail(uri != NULL, -1);
3233 /* an option to choose whether to use encode_uri or not ? */
3234 encode_uri(encoded_uri, BUFFSIZE, uri);
3237 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3238 !strchr(p + 2, '%'))
3239 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3242 g_warning("Open URI command line is invalid "
3243 "(there must be only one '%%s'): %s",
3245 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3248 execute_command_line(buf, TRUE);
3250 extern osso_context_t *get_osso_context(void);
3251 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3252 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3253 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3258 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3260 gchar buf[BUFFSIZE];
3263 g_return_val_if_fail(filepath != NULL, -1);
3266 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3267 !strchr(p + 2, '%'))
3268 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3271 g_warning("Open Text Editor command line is invalid "
3272 "(there must be only one '%%s'): %s",
3274 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3277 execute_command_line(buf, TRUE);
3282 time_t remote_tzoffset_sec(const gchar *zone)
3284 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3290 time_t remoteoffset;
3292 strncpy(zone3, zone, 3);
3296 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3297 (c == '+' || c == '-')) {
3298 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3300 remoteoffset = -remoteoffset;
3301 } else if (!strncmp(zone, "UT" , 2) ||
3302 !strncmp(zone, "GMT", 2)) {
3304 } else if (strlen(zone3) == 3) {
3305 for (p = ustzstr; *p != '\0'; p += 3) {
3306 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3307 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3308 remoteoffset = iustz * 3600;
3314 } else if (strlen(zone3) == 1) {
3316 case 'Z': remoteoffset = 0; break;
3317 case 'A': remoteoffset = -1; break;
3318 case 'B': remoteoffset = -2; break;
3319 case 'C': remoteoffset = -3; break;
3320 case 'D': remoteoffset = -4; break;
3321 case 'E': remoteoffset = -5; break;
3322 case 'F': remoteoffset = -6; break;
3323 case 'G': remoteoffset = -7; break;
3324 case 'H': remoteoffset = -8; break;
3325 case 'I': remoteoffset = -9; break;
3326 case 'K': remoteoffset = -10; break; /* J is not used */
3327 case 'L': remoteoffset = -11; break;
3328 case 'M': remoteoffset = -12; break;
3329 case 'N': remoteoffset = 1; break;
3330 case 'O': remoteoffset = 2; break;
3331 case 'P': remoteoffset = 3; break;
3332 case 'Q': remoteoffset = 4; break;
3333 case 'R': remoteoffset = 5; break;
3334 case 'S': remoteoffset = 6; break;
3335 case 'T': remoteoffset = 7; break;
3336 case 'U': remoteoffset = 8; break;
3337 case 'V': remoteoffset = 9; break;
3338 case 'W': remoteoffset = 10; break;
3339 case 'X': remoteoffset = 11; break;
3340 case 'Y': remoteoffset = 12; break;
3341 default: remoteoffset = 0; break;
3343 remoteoffset = remoteoffset * 3600;
3347 return remoteoffset;
3350 time_t tzoffset_sec(time_t *now)
3354 struct tm buf1, buf2;
3356 gmt = *gmtime_r(now, &buf1);
3357 lt = localtime_r(now, &buf2);
3359 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3361 if (lt->tm_year < gmt.tm_year)
3363 else if (lt->tm_year > gmt.tm_year)
3365 else if (lt->tm_yday < gmt.tm_yday)
3367 else if (lt->tm_yday > gmt.tm_yday)
3370 if (off >= 24 * 60) /* should be impossible */
3371 off = 23 * 60 + 59; /* if not, insert silly value */
3372 if (off <= -24 * 60)
3373 off = -(23 * 60 + 59);
3378 /* calculate timezone offset */
3379 gchar *tzoffset(time_t *now)
3381 static gchar offset_string[6];
3385 struct tm buf1, buf2;
3387 gmt = *gmtime_r(now, &buf1);
3388 lt = localtime_r(now, &buf2);
3390 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3392 if (lt->tm_year < gmt.tm_year)
3394 else if (lt->tm_year > gmt.tm_year)
3396 else if (lt->tm_yday < gmt.tm_yday)
3398 else if (lt->tm_yday > gmt.tm_yday)
3406 if (off >= 24 * 60) /* should be impossible */
3407 off = 23 * 60 + 59; /* if not, insert silly value */
3409 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3411 return offset_string;
3414 void get_rfc822_date(gchar *buf, gint len)
3418 gchar day[4], mon[4];
3419 gint dd, hh, mm, ss, yyyy;
3421 gchar buf2[BUFFSIZE];
3424 lt = localtime_r(&t, &buf1);
3426 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3427 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3429 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3430 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3433 void debug_set_mode(gboolean mode)
3438 gboolean debug_get_mode(void)
3443 void debug_print_real(const gchar *format, ...)
3446 gchar buf[BUFFSIZE];
3448 if (!debug_mode) return;
3450 va_start(args, format);
3451 g_vsnprintf(buf, sizeof(buf), format, args);
3458 const char * debug_srcname(const char *file)
3460 const char *s = strrchr (file, '/');
3465 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3467 if (subject == NULL)
3470 subject += subject_get_prefix_length(subject);
3472 return g_hash_table_lookup(subject_table, subject);
3475 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3478 if (subject == NULL || *subject == 0)
3480 subject += subject_get_prefix_length(subject);
3481 g_hash_table_insert(subject_table, subject, data);
3484 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3486 if (subject == NULL)
3489 subject += subject_get_prefix_length(subject);
3490 g_hash_table_remove(subject_table, subject);
3494 *\brief Check if a string is prefixed with known (combinations)
3495 * of prefixes. The function assumes that each prefix
3496 * is terminated by zero or exactly _one_ space.
3498 *\param str String to check for a prefixes
3500 *\return int Number of chars in the prefix that should be skipped
3501 * for a "clean" subject line. If no prefix was found, 0
3504 int subject_get_prefix_length(const gchar *subject)
3506 /*!< Array with allowable reply prefixes regexps. */
3507 static const gchar * const prefixes[] = {
3508 "Re\\:", /* "Re:" */
3509 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3510 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3511 "Aw\\:", /* "Aw:" (German) */
3512 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3513 "Res\\:", /* "Res:" (Brazilian Outlook) */
3514 "Fw\\:", /* "Fw:" Forward */
3515 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3516 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3517 "Rif\\:", /* "Rif:" (Italian Outlook) */
3518 "Sv\\:", /* "Sv" (Norwegian) */
3519 "Vs\\:", /* "Vs" (Norwegian) */
3520 "Ad\\:", /* "Ad" (Norwegian) */
3521 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3524 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3527 static regex_t regex;
3528 static gboolean init_;
3530 if (!subject) return 0;
3531 if (!*subject) return 0;
3534 GString *s = g_string_new("");
3536 for (n = 0; n < PREFIXES; n++)
3537 /* Terminate each prefix regexpression by a
3538 * "\ ?" (zero or ONE space), and OR them */
3539 g_string_append_printf(s, "(%s\\ ?)%s",
3544 g_string_prepend(s, "(");
3545 g_string_append(s, ")+"); /* match at least once */
3546 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3549 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3550 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3551 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3552 debug_print("Error compiling regexp %s\n", s->str);
3553 g_string_free(s, TRUE);
3557 g_string_free(s, TRUE);
3561 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3567 static guint g_stricase_hash(gconstpointer gptr)
3569 guint hash_result = 0;
3572 for (str = gptr; str && *str; str++) {
3573 hash_result += toupper(*str);
3579 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3581 const char *str1 = gptr1;
3582 const char *str2 = gptr2;
3584 return !strcasecmp(str1, str2);
3587 gint g_int_compare(gconstpointer a, gconstpointer b)
3589 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3592 gchar *generate_msgid(gchar *buf, gint len)
3600 lt = localtime_r(&t, &buft);
3602 if (strcmp(buf, "") == 0) {
3603 addr = g_strconcat("@", get_domain_name(), NULL);
3606 addr = g_strconcat("@", buf, NULL);
3609 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3610 lt->tm_year + 1900, lt->tm_mon + 1,
3611 lt->tm_mday, lt->tm_hour,
3612 lt->tm_min, lt->tm_sec,
3613 (guint) rand(), addr);
3620 quote_cmd_argument()
3622 return a quoted string safely usable in argument of a command.
3624 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3627 gint quote_cmd_argument(gchar * result, guint size,
3637 for(p = path ; * p != '\0' ; p ++) {
3639 if (isalnum((guchar)*p) || (* p == '/')) {
3640 if (remaining > 0) {
3646 result[size - 1] = '\0';
3651 if (remaining >= 2) {
3659 result[size - 1] = '\0';
3664 if (remaining > 0) {
3668 result[size - 1] = '\0';
3682 static void g_node_map_recursive(GNode *node, gpointer data)
3684 GNodeMapData *mapdata = (GNodeMapData *) data;
3686 GNodeMapData newmapdata;
3689 newdata = mapdata->func(node->data, mapdata->data);
3690 if (newdata != NULL) {
3691 newnode = g_node_new(newdata);
3692 g_node_append(mapdata->parent, newnode);
3694 newmapdata.parent = newnode;
3695 newmapdata.func = mapdata->func;
3696 newmapdata.data = mapdata->data;
3698 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3702 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3705 GNodeMapData mapdata;
3707 g_return_val_if_fail(node != NULL, NULL);
3708 g_return_val_if_fail(func != NULL, NULL);
3710 root = g_node_new(func(node->data, data));
3712 mapdata.parent = root;
3713 mapdata.func = func;
3714 mapdata.data = data;
3716 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3721 #define HEX_TO_INT(val, hex) \
3725 if ('0' <= c && c <= '9') { \
3727 } else if ('a' <= c && c <= 'f') { \
3728 val = c - 'a' + 10; \
3729 } else if ('A' <= c && c <= 'F') { \
3730 val = c - 'A' + 10; \
3736 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3743 if (hi == -1 || lo == -1)
3746 *out = (hi << 4) + lo;
3750 #define INT_TO_HEX(hex, val) \
3753 hex = '0' + (val); \
3755 hex = 'A' + (val) - 10; \
3758 void get_hex_str(gchar *out, guchar ch)
3762 INT_TO_HEX(hex, ch >> 4);
3764 INT_TO_HEX(hex, ch & 0x0f);
3770 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3772 #define G_PRINT_REF g_print
3776 *\brief Register ref counted pointer. It is based on GBoxed, so should
3777 * work with anything that uses the GType system. The semantics
3778 * are similar to a C++ auto pointer, with the exception that
3779 * C doesn't have automatic closure (calling destructors) when
3780 * exiting a block scope.
3781 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3782 * function directly.
3784 *\return GType A GType type.
3786 GType g_auto_pointer_register(void)
3788 static GType auto_pointer_type;
3789 if (!auto_pointer_type)
3791 g_boxed_type_register_static
3792 ("G_TYPE_AUTO_POINTER",
3793 (GBoxedCopyFunc) g_auto_pointer_copy,
3794 (GBoxedFreeFunc) g_auto_pointer_free);
3795 return auto_pointer_type;
3799 *\brief Structure with g_new() allocated pointer guarded by the
3802 typedef struct AutoPointerRef {
3803 void (*free) (gpointer);
3809 *\brief The auto pointer opaque structure that references the
3810 * pointer guard block.
3812 typedef struct AutoPointer {
3813 AutoPointerRef *ref;
3814 gpointer ptr; /*!< access to protected pointer */
3818 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3822 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3823 * ... when assigning, copying and freeing storage elements
3825 * gtk_list_store_new(N_S_COLUMNS,
3826 * G_TYPE_AUTO_POINTER,
3830 * Template *precious_data = g_new0(Template, 1);
3831 * g_pointer protect = g_auto_pointer_new(precious_data);
3833 * gtk_list_store_set(container, &iter,
3837 * ... the gtk_list_store has copied the pointer and
3838 * ... incremented its reference count, we should free
3839 * ... the auto pointer (in C++ a destructor would do
3840 * ... this for us when leaving block scope)
3842 * g_auto_pointer_free(protect);
3844 * ... gtk_list_store_set() now manages the data. When
3845 * ... *explicitly* requesting a pointer from the list
3846 * ... store, don't forget you get a copy that should be
3847 * ... freed with g_auto_pointer_free() eventually.
3851 *\param pointer Pointer to be guarded.
3853 *\return GAuto * Pointer that should be used in containers with
3856 GAuto *g_auto_pointer_new(gpointer p)
3858 AutoPointerRef *ref;
3864 ref = g_new0(AutoPointerRef, 1);
3865 ptr = g_new0(AutoPointer, 1);
3875 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3881 *\brief Allocate an autopointer using the passed \a free function to
3882 * free the guarded pointer
3884 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3891 aptr = g_auto_pointer_new(p);
3892 aptr->ref->free = free_;
3896 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3898 if (auto_ptr == NULL)
3900 return ((AutoPointer *) auto_ptr)->ptr;
3904 *\brief Copies an auto pointer by. It's mostly not necessary
3905 * to call this function directly, unless you copy/assign
3906 * the guarded pointer.
3908 *\param auto_ptr Auto pointer returned by previous call to
3909 * g_auto_pointer_new_XXX()
3911 *\return gpointer An auto pointer
3913 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3916 AutoPointerRef *ref;
3919 if (auto_ptr == NULL)
3924 newp = g_new0(AutoPointer, 1);
3927 newp->ptr = ref->pointer;
3931 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3937 *\brief Free an auto pointer
3939 void g_auto_pointer_free(GAuto *auto_ptr)
3942 AutoPointerRef *ref;
3944 if (auto_ptr == NULL)
3950 if (--(ref->cnt) == 0) {
3952 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3954 ref->free(ref->pointer);
3959 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3964 void replace_returns(gchar *str)
3969 while (strstr(str, "\n")) {
3970 *strstr(str, "\n") = ' ';
3972 while (strstr(str, "\r")) {
3973 *strstr(str, "\r") = ' ';
3977 /* get_uri_part() - retrieves a URI starting from scanpos.
3978 Returns TRUE if succesful */
3979 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3980 const gchar **bp, const gchar **ep, gboolean hdr)
3983 gint parenthese_cnt = 0;
3985 g_return_val_if_fail(start != NULL, FALSE);
3986 g_return_val_if_fail(scanpos != NULL, FALSE);
3987 g_return_val_if_fail(bp != NULL, FALSE);
3988 g_return_val_if_fail(ep != NULL, FALSE);
3992 /* find end point of URI */
3993 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
3994 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
3995 !IS_ASCII(*(const guchar *)ep_) ||
3996 strchr("[]{}<>\"", *ep_)) {
3998 } else if (strchr("(", *ep_)) {
4000 } else if (strchr(")", *ep_)) {
4001 if (parenthese_cnt > 0)
4008 /* no punctuation at end of string */
4010 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4011 * should pass some URI type to this function and decide on that whether
4012 * to perform punctuation stripping */
4014 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4016 for (; ep_ - 1 > scanpos + 1 &&
4017 IS_REAL_PUNCT(*(ep_ - 1));
4021 #undef IS_REAL_PUNCT
4028 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4030 while (bp && *bp && g_ascii_isspace(*bp))
4032 return g_strndup(bp, ep - bp);
4035 /* valid mail address characters */
4036 #define IS_RFC822_CHAR(ch) \
4040 !g_ascii_isspace(ch) && \
4041 !strchr("(),;<>\"", (ch)))
4043 /* alphabet and number within 7bit ASCII */
4044 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4045 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4047 static GHashTable *create_domain_tab(void)
4049 static const gchar *toplvl_domains [] = {
4051 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4052 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4053 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4054 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4055 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4056 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4057 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4058 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4059 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4060 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4061 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4062 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4063 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4064 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4065 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4066 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4067 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4068 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4069 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4070 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4071 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4072 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4073 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4074 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4075 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4076 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4079 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4081 g_return_val_if_fail(htab, NULL);
4082 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4083 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4087 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4089 const gint MAX_LVL_DOM_NAME_LEN = 6;
4090 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4091 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4094 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4097 for (p = buf; p < m && first < last; *p++ = *first++)
4101 return g_hash_table_lookup(tab, buf) != NULL;
4104 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4105 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4106 const gchar **bp, const gchar **ep, gboolean hdr)
4108 /* more complex than the uri part because we need to scan back and forward starting from
4109 * the scan position. */
4110 gboolean result = FALSE;
4111 const gchar *bp_ = NULL;
4112 const gchar *ep_ = NULL;
4113 static GHashTable *dom_tab;
4114 const gchar *last_dot = NULL;
4115 const gchar *prelast_dot = NULL;
4116 const gchar *last_tld_char = NULL;
4118 /* the informative part of the email address (describing the name
4119 * of the email address owner) may contain quoted parts. the
4120 * closure stack stores the last encountered quotes. */
4121 gchar closure_stack[128];
4122 gchar *ptr = closure_stack;
4124 g_return_val_if_fail(start != NULL, FALSE);
4125 g_return_val_if_fail(scanpos != NULL, FALSE);
4126 g_return_val_if_fail(bp != NULL, FALSE);
4127 g_return_val_if_fail(ep != NULL, FALSE);
4130 const gchar *start_quote = NULL;
4131 const gchar *end_quote = NULL;
4133 /* go to the real start */
4134 if (start[0] == ',')
4136 if (start[0] == ';')
4138 while (start[0] == '\n' || start[0] == '\r')
4140 while (start[0] == ' ' || start[0] == '\t')
4145 /* check if there are quotes (to skip , in them) */
4146 if (*start == '"') {
4147 start_quote = start;
4149 end_quote = strstr(start, "\"");
4155 /* skip anything between quotes */
4156 if (start_quote && end_quote) {
4161 /* find end (either , or ; or end of line) */
4162 if (strstr(start, ",") && strstr(start, ";"))
4163 *ep = strstr(start,",") < strstr(start, ";")
4164 ? strstr(start, ",") : strstr(start, ";");
4165 else if (strstr(start, ","))
4166 *ep = strstr(start, ",");
4167 else if (strstr(start, ";"))
4168 *ep = strstr(start, ";");
4170 *ep = start+strlen(start);
4172 /* go back to real start */
4173 if (start_quote && end_quote) {
4174 start = start_quote;
4177 /* check there's still an @ in that, or search
4178 * further if possible */
4179 if (strstr(start, "@") && strstr(start, "@") < *ep)
4181 else if (*ep < start+strlen(start)) {
4184 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4192 dom_tab = create_domain_tab();
4193 g_return_val_if_fail(dom_tab, FALSE);
4195 /* scan start of address */
4196 for (bp_ = scanpos - 1;
4197 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4200 /* TODO: should start with an alnum? */
4202 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4205 if (bp_ != scanpos) {
4206 /* scan end of address */
4207 for (ep_ = scanpos + 1;
4208 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4210 prelast_dot = last_dot;
4212 if (*(last_dot + 1) == '.') {
4213 if (prelast_dot == NULL)
4215 last_dot = prelast_dot;
4220 /* TODO: really should terminate with an alnum? */
4221 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4226 if (last_dot == NULL)
4228 if (last_dot >= ep_)
4229 last_dot = prelast_dot;
4230 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4234 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4235 if (*last_tld_char == '?')
4238 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4245 if (!result) return FALSE;
4247 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4248 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4249 && IS_RFC822_CHAR(*(ep_ + 3))) {
4250 /* this informative part with an @ in it is
4251 * followed by the email address */
4254 /* go to matching '>' (or next non-rfc822 char, like \n) */
4255 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4258 /* include the bracket */
4259 if (*ep_ == '>') ep_++;
4261 /* include the leading quote */
4269 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4270 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4273 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4274 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4277 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4278 #define IN_STACK() (ptr > closure_stack)
4279 /* has underrun check */
4280 #define POP_STACK() if(IN_STACK()) --ptr
4281 /* has overrun check */
4282 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4283 /* has underrun check */
4284 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4288 /* scan for the informative part. */
4289 for (bp_ -= 2; bp_ >= start; bp_--) {
4290 /* if closure on the stack keep scanning */
4291 if (PEEK_STACK() == *bp_) {
4295 if (*bp_ == '\'' || *bp_ == '"') {
4300 /* if nothing in the closure stack, do the special conditions
4301 * the following if..else expression simply checks whether
4302 * a token is acceptable. if not acceptable, the clause
4303 * should terminate the loop with a 'break' */
4304 if (!PEEK_STACK()) {
4306 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4307 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4308 /* hyphens are allowed, but only in
4310 } else if (strchr(" \"'", *bp_)) {
4311 /* but anything not being a punctiation
4314 break; /* anything else is rejected */
4321 /* scan forward (should start with an alnum) */
4322 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4338 #undef IS_ASCII_ALNUM
4339 #undef IS_RFC822_CHAR
4341 gchar *make_email_string(const gchar *bp, const gchar *ep)
4343 /* returns a mailto: URI; mailto: is also used to detect the
4344 * uri type later on in the button_pressed signal handler */
4348 tmp = g_strndup(bp, ep - bp);
4349 result = g_strconcat("mailto:", tmp, NULL);
4355 gchar *make_http_string(const gchar *bp, const gchar *ep)
4357 /* returns an http: URI; */
4361 while (bp && *bp && g_ascii_isspace(*bp))
4363 tmp = g_strndup(bp, ep - bp);
4364 result = g_strconcat("http://", tmp, NULL);
4370 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4372 FILE *fp = fopen(path, "rb");
4373 gchar buf[BUFFSIZE];
4374 gchar *result = NULL;
4377 while (fgets(buf, sizeof (buf), fp) != NULL) {
4378 gchar **parts = g_strsplit(buf, ";", 3);
4379 gchar *trimmed = parts[0];
4380 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4382 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4383 trimmed[strlen(trimmed)-1] = '\0';
4385 if (!strcmp(trimmed, type)) {
4386 gboolean needsterminal = FALSE;
4387 if (parts[2] && strstr(parts[2], "needsterminal")) {
4388 needsterminal = TRUE;
4390 if (parts[2] && strstr(parts[2], "test=")) {
4391 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4392 gchar *testcmd = orig_testcmd;
4393 if (strstr(testcmd,";"))
4394 *(strstr(testcmd,";")) = '\0';
4395 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4397 while (testcmd[strlen(testcmd)-1] == '\n')
4398 testcmd[strlen(testcmd)-1] = '\0';
4399 while (testcmd[strlen(testcmd)-1] == '\r')
4400 testcmd[strlen(testcmd)-1] = '\0';
4401 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4402 testcmd[strlen(testcmd)-1] = '\0';
4404 if (strstr(testcmd, "%s")) {
4405 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4406 gint res = system(tmp);
4408 g_free(orig_testcmd);
4415 gint res = system(testcmd);
4416 g_free(orig_testcmd);
4426 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4428 while (trimmed[strlen(trimmed)-1] == '\n')
4429 trimmed[strlen(trimmed)-1] = '\0';
4430 while (trimmed[strlen(trimmed)-1] == '\r')
4431 trimmed[strlen(trimmed)-1] = '\0';
4432 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4433 trimmed[strlen(trimmed)-1] = '\0';
4434 result = g_strdup(trimmed);
4437 /* if there are no single quotes around %s, add them.
4438 * '.*%s.*' is ok, as in display 'png:%s'
4440 if (strstr(result, "%s")
4441 && !(strstr(result, "'") < strstr(result,"%s") &&
4442 strstr(strstr(result,"%s"), "'"))) {
4443 gchar *start = g_strdup(result);
4444 gchar *end = g_strdup(strstr(result, "%s")+2);
4446 *strstr(start, "%s") = '\0';
4447 tmp = g_strconcat(start,"'%s'",end, NULL);
4453 if (needsterminal) {
4454 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4465 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4467 gchar *result = NULL;
4471 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4472 result = mailcap_get_command_in_file(path, type, file_to_open);
4476 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4480 void mailcap_update_default(const gchar *type, const gchar *command)
4482 gchar *path = NULL, *outpath = NULL;
4483 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4484 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4485 FILE *fp = fopen(path, "rb");
4486 FILE *outfp = fopen(outpath, "wb");
4487 gchar buf[BUFFSIZE];
4488 gboolean err = FALSE;
4496 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4497 gchar **parts = g_strsplit(buf, ";", 3);
4498 gchar *trimmed = parts[0];
4499 while (trimmed[0] == ' ')
4501 while (trimmed[strlen(trimmed)-1] == ' ')
4502 trimmed[strlen(trimmed)-1] = '\0';
4504 if (!strcmp(trimmed, type)) {
4509 if(fputs(buf, outfp) == EOF) {
4516 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4522 if (fclose(outfp) == EOF)
4526 g_rename(outpath, path);
4532 gint copy_dir(const gchar *src, const gchar *dst)
4537 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4538 g_warning("failed to open directory: %s\n", src);
4542 if (make_dir(dst) < 0)
4545 while ((name = g_dir_read_name(dir)) != NULL) {
4546 gchar *old_file, *new_file;
4547 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4548 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4549 debug_print("copying: %s -> %s\n", old_file, new_file);
4550 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4551 gint r = copy_file(old_file, new_file, TRUE);
4558 /* Windows has no symlinks. Or well, Vista seems to
4559 have something like this but the semantics might be
4560 different. Thus we don't use it under Windows. */
4561 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4564 gchar *target = g_file_read_link(old_file, &error);
4566 r = symlink(target, new_file);
4573 #endif /*G_OS_WIN32*/
4574 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4575 gint r = copy_dir(old_file, new_file);
4586 /* crude test to see if a file is an email. */
4587 gboolean file_is_email (const gchar *filename)
4593 if (filename == NULL)
4595 if ((fp = g_fopen(filename, "rb")) == NULL)
4597 while (i < 60 && score < 3
4598 && fgets(buffer, sizeof (buffer), fp) > 0) {
4599 if (!strncmp(buffer, "From:", strlen("From:")))
4601 if (!strncmp(buffer, "To:", strlen("To:")))
4603 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4608 return (score >= 3);
4611 gboolean sc_g_list_bigger(GList *list, gint max)
4615 while (cur && i <= max+1) {
4622 gboolean sc_g_slist_bigger(GSList *list, gint max)
4626 while (cur && i <= max+1) {
4633 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4634 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4635 NULL, NULL, NULL, NULL, NULL, NULL};
4636 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4637 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4638 NULL, NULL, NULL, NULL, NULL, NULL};
4639 const gchar *s_am_up = NULL;
4640 const gchar *s_pm_up = NULL;
4641 const gchar *s_am_low = NULL;
4642 const gchar *s_pm_low = NULL;
4643 const gchar *def_loc_format = NULL;
4644 const gchar *date_loc_format = NULL;
4645 const gchar *time_loc_format = NULL;
4646 const gchar *time_am_pm = NULL;
4648 static gboolean time_names_init_done = FALSE;
4650 static void init_time_names(void)
4652 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4653 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4654 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4655 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4656 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4657 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4658 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4660 monthnames[0] = Q_("Complete month name for use by strftime|January");
4661 monthnames[1] = Q_("Complete month name for use by strftime|February");
4662 monthnames[2] = Q_("Complete month name for use by strftime|March");
4663 monthnames[3] = Q_("Complete month name for use by strftime|April");
4664 monthnames[4] = Q_("Complete month name for use by strftime|May");
4665 monthnames[5] = Q_("Complete month name for use by strftime|June");
4666 monthnames[6] = Q_("Complete month name for use by strftime|July");
4667 monthnames[7] = Q_("Complete month name for use by strftime|August");
4668 monthnames[8] = Q_("Complete month name for use by strftime|September");
4669 monthnames[9] = Q_("Complete month name for use by strftime|October");
4670 monthnames[10] = Q_("Complete month name for use by strftime|November");
4671 monthnames[11] = Q_("Complete month name for use by strftime|December");
4673 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4674 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4675 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4676 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4677 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4678 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4679 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4681 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4682 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4683 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4684 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4685 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4686 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4687 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4688 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4689 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4690 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4691 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4692 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4694 s_am_up = Q_("For use by strftime (morning)|AM");
4695 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4696 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4697 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4699 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4700 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4701 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4703 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4705 time_names_init_done = TRUE;
4708 #define CHECK_SIZE() { \
4709 total_done += len; \
4710 if (total_done >= buflen) { \
4711 buf[buflen-1] = '\0'; \
4716 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4718 gchar *curpos = buf;
4719 gint total_done = 0;
4720 gchar subbuf[64], subfmt[64];
4721 static time_t last_tzset = (time_t)0;
4723 if (!time_names_init_done)
4726 if (format == NULL || lt == NULL)
4729 if (last_tzset != time(NULL)) {
4731 last_tzset = time(NULL);
4734 if (*format == '%') {
4735 gint len = 0, tmp = 0;
4739 len = 1; CHECK_SIZE();
4743 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4744 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4747 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4748 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4752 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4753 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4756 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4757 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4760 fast_strftime(subbuf, 64, def_loc_format, lt);
4761 len = strlen(subbuf); CHECK_SIZE();
4762 strncpy2(curpos, subbuf, buflen - total_done);
4765 total_done += 2; CHECK_SIZE();
4766 tmp = (lt->tm_year + 1900)/100;
4767 *curpos++ = '0'+(tmp / 10);
4768 *curpos++ = '0'+(tmp % 10);
4771 total_done += 2; CHECK_SIZE();
4772 *curpos++ = '0'+(lt->tm_mday / 10);
4773 *curpos++ = '0'+(lt->tm_mday % 10);
4776 total_done += 8; CHECK_SIZE();
4777 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4778 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4780 *curpos++ = '0'+(lt->tm_mday / 10);
4781 *curpos++ = '0'+(lt->tm_mday % 10);
4783 tmp = lt->tm_year%100;
4784 *curpos++ = '0'+(tmp / 10);
4785 *curpos++ = '0'+(tmp % 10);
4788 len = 2; CHECK_SIZE();
4789 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4792 len = 10; CHECK_SIZE();
4793 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4794 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4797 total_done += 2; CHECK_SIZE();
4798 *curpos++ = '0'+(lt->tm_hour / 10);
4799 *curpos++ = '0'+(lt->tm_hour % 10);
4802 total_done += 2; CHECK_SIZE();
4808 *curpos++ = '0'+(tmp / 10);
4809 *curpos++ = '0'+(tmp % 10);
4812 len = 3; CHECK_SIZE();
4813 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4816 len = 2; CHECK_SIZE();
4817 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4820 len = 2; CHECK_SIZE();
4826 snprintf(curpos, buflen - total_done, "%2d", tmp);
4829 total_done += 2; CHECK_SIZE();
4830 tmp = lt->tm_mon + 1;
4831 *curpos++ = '0'+(tmp / 10);
4832 *curpos++ = '0'+(tmp % 10);
4835 total_done += 2; CHECK_SIZE();
4836 *curpos++ = '0'+(lt->tm_min / 10);
4837 *curpos++ = '0'+(lt->tm_min % 10);
4840 len = 1; CHECK_SIZE();
4844 if (lt->tm_hour >= 12) {
4845 len = strlen(s_pm_up); CHECK_SIZE();
4846 snprintf(curpos, buflen-total_done, s_pm_up);
4848 len = strlen(s_am_up); CHECK_SIZE();
4849 snprintf(curpos, buflen-total_done, s_am_up);
4853 if (lt->tm_hour >= 12) {
4854 len = strlen(s_pm_low); CHECK_SIZE();
4855 snprintf(curpos, buflen-total_done, s_pm_low);
4857 len = strlen(s_am_low); CHECK_SIZE();
4858 snprintf(curpos, buflen-total_done, s_am_low);
4862 fast_strftime(subbuf, 64, time_am_pm, lt);
4863 len = strlen(subbuf); CHECK_SIZE();
4864 strncpy2(curpos, subbuf, buflen - total_done);
4867 total_done += 5; CHECK_SIZE();
4868 *curpos++ = '0'+(lt->tm_hour / 10);
4869 *curpos++ = '0'+(lt->tm_hour % 10);
4871 *curpos++ = '0'+(lt->tm_min / 10);
4872 *curpos++ = '0'+(lt->tm_min % 10);
4875 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4876 len = strlen(subbuf); CHECK_SIZE();
4877 strncpy2(curpos, subbuf, buflen - total_done);
4880 total_done += 2; CHECK_SIZE();
4881 *curpos++ = '0'+(lt->tm_sec / 10);
4882 *curpos++ = '0'+(lt->tm_sec % 10);
4885 len = 1; CHECK_SIZE();
4889 total_done += 8; CHECK_SIZE();
4890 *curpos++ = '0'+(lt->tm_hour / 10);
4891 *curpos++ = '0'+(lt->tm_hour % 10);
4893 *curpos++ = '0'+(lt->tm_min / 10);
4894 *curpos++ = '0'+(lt->tm_min % 10);
4896 *curpos++ = '0'+(lt->tm_sec / 10);
4897 *curpos++ = '0'+(lt->tm_sec % 10);
4900 len = 1; CHECK_SIZE();
4901 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4904 len = 1; CHECK_SIZE();
4905 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4908 fast_strftime(subbuf, 64, date_loc_format, lt);
4909 len = strlen(subbuf); CHECK_SIZE();
4910 strncpy2(curpos, subbuf, buflen - total_done);
4913 fast_strftime(subbuf, 64, time_loc_format, lt);
4914 len = strlen(subbuf); CHECK_SIZE();
4915 strncpy2(curpos, subbuf, buflen - total_done);
4918 total_done += 2; CHECK_SIZE();
4919 tmp = lt->tm_year%100;
4920 *curpos++ = '0'+(tmp / 10);
4921 *curpos++ = '0'+(tmp % 10);
4924 len = 4; CHECK_SIZE();
4925 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4935 /* let these complicated ones be done with the libc */
4936 snprintf(subfmt, 64, "%%%c", *format);
4937 strftime(subbuf, 64, subfmt, lt);
4938 len = strlen(subbuf); CHECK_SIZE();
4939 strncpy2(curpos, subbuf, buflen - total_done);
4943 /* let these complicated modifiers be done with the libc */
4944 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4945 strftime(subbuf, 64, subfmt, lt);
4946 len = strlen(subbuf); CHECK_SIZE();
4947 strncpy2(curpos, subbuf, buflen - total_done);
4951 g_warning("format error (%c)", *format);
4958 int len = 1; CHECK_SIZE();
4959 *curpos++ = *format++;