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/",
1524 Xstrdup_a(tmp_mailto, mailto, return -1);
1526 if (!strncmp(tmp_mailto, "mailto:", 7))
1529 p = strchr(tmp_mailto, '?');
1536 *to = decode_uri_gdup(tmp_mailto);
1539 gchar *field, *value;
1556 if (*value == '\0') continue;
1558 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1559 *cc = decode_uri_gdup(value);
1560 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1561 *bcc = decode_uri_gdup(value);
1562 } else if (subject && !*subject &&
1563 !g_ascii_strcasecmp(field, "subject")) {
1564 *subject = decode_uri_gdup(value);
1565 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1566 *body = decode_uri_gdup(value);
1567 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1569 *attach = decode_uri_gdup(value);
1570 for (; forbidden_uris[i]; i++) {
1571 if (strstr(*attach, forbidden_uris[i])) {
1572 g_print("Refusing to attach '%s', potential private data leak\n",
1587 #include <windows.h>
1588 #ifndef CSIDL_APPDATA
1589 #define CSIDL_APPDATA 0x001a
1591 #ifndef CSIDL_LOCAL_APPDATA
1592 #define CSIDL_LOCAL_APPDATA 0x001c
1594 #ifndef CSIDL_FLAG_CREATE
1595 #define CSIDL_FLAG_CREATE 0x8000
1597 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1601 w32_strerror (int w32_errno)
1603 static char strerr[256];
1604 int ec = (int)GetLastError ();
1608 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1609 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1610 strerr, DIM (strerr)-1, NULL);
1614 static __inline__ void *
1615 dlopen (const char * name, int flag)
1617 void * hd = LoadLibrary (name);
1621 static __inline__ void *
1622 dlsym (void * hd, const char * sym)
1626 void * fnc = GetProcAddress (hd, sym);
1635 static __inline__ const char *
1638 return w32_strerror (0);
1642 static __inline__ int
1654 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1656 static int initialized;
1657 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1661 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1667 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1669 handle = dlopen (dllnames[i], RTLD_LAZY);
1672 func = dlsym (handle, "SHGetFolderPathA");
1683 return func (a,b,c,d,e);
1688 /* Returns a static string with the directroy from which the module
1689 has been loaded. Returns an empty string on error. */
1690 static char *w32_get_module_dir(void)
1692 static char *moddir;
1695 char name[MAX_PATH+10];
1698 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1701 p = strrchr (name, '\\');
1707 moddir = g_strdup (name);
1711 #endif /* G_OS_WIN32 */
1713 /* Return a static string with the locale dir. */
1714 const gchar *get_locale_dir(void)
1716 static gchar *loc_dir;
1720 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1721 "\\share\\locale", NULL);
1724 loc_dir = LOCALEDIR;
1730 const gchar *get_home_dir(void)
1733 static char home_dir[MAX_PATH] = "";
1735 if (home_dir[0] == '\0') {
1736 if (w32_shgetfolderpath
1737 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1738 NULL, 0, home_dir) < 0)
1739 strcpy (home_dir, "C:\\Sylpheed");
1743 static const gchar *homeenv = NULL;
1748 if (!homeenv && g_getenv("HOME") != NULL)
1749 homeenv = g_strdup(g_getenv("HOME"));
1751 homeenv = g_get_home_dir();
1757 static gchar *claws_rc_dir = NULL;
1758 static gboolean rc_dir_alt = FALSE;
1759 const gchar *get_rc_dir(void)
1763 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1766 return claws_rc_dir;
1769 void set_rc_dir(const gchar *dir)
1771 if (claws_rc_dir != NULL) {
1772 g_print("Error: rc_dir already set\n");
1775 if (g_path_is_absolute(dir))
1776 claws_rc_dir = g_strdup(dir);
1778 claws_rc_dir = g_strconcat(g_get_current_dir(),
1779 G_DIR_SEPARATOR_S, dir, NULL);
1781 debug_print("set rc_dir to %s\n", claws_rc_dir);
1782 if (!is_dir_exist(claws_rc_dir)) {
1783 if (make_dir_hier(claws_rc_dir) != 0) {
1784 g_print("Error: can't create %s\n",
1791 gboolean rc_dir_is_alt(void) {
1795 const gchar *get_mail_base_dir(void)
1798 static gchar *mail_base_dir = NULL;
1801 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1804 return mail_base_dir;
1806 return get_home_dir();
1811 const gchar *prefs_common_get_data_root(void);
1812 gchar *last_data_root = NULL;
1815 const gchar *get_news_cache_dir(void)
1817 static gchar *news_cache_dir = NULL;
1819 const gchar *data_root = prefs_common_get_data_root();
1820 if (strcmp2(data_root, last_data_root)) {
1821 g_free(news_cache_dir);
1822 news_cache_dir = NULL;
1825 if (!news_cache_dir)
1827 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1828 NEWS_CACHE_DIR, NULL);
1832 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1833 "Claws", G_DIR_SEPARATOR_S,
1834 g_get_user_name(), G_DIR_SEPARATOR_S,
1835 NEWS_CACHE_DIR, NULL);
1836 g_free(last_data_root);
1837 last_data_root = g_strdup(last_data_root);
1839 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1840 NEWS_CACHE_DIR, NULL);
1841 g_free(last_data_root);
1842 last_data_root = NULL;
1846 return news_cache_dir;
1849 const gchar *get_imap_cache_dir(void)
1851 static gchar *imap_cache_dir = NULL;
1853 const gchar *data_root = prefs_common_get_data_root();
1854 if (strcmp2(data_root, last_data_root)) {
1855 g_free(imap_cache_dir);
1856 imap_cache_dir = NULL;
1860 if (!imap_cache_dir)
1862 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1863 IMAP_CACHE_DIR, NULL);
1867 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1868 "Claws", G_DIR_SEPARATOR_S,
1869 g_get_user_name(), G_DIR_SEPARATOR_S,
1870 IMAP_CACHE_DIR, NULL);
1871 g_free(last_data_root);
1872 last_data_root = g_strdup(last_data_root);
1874 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1875 IMAP_CACHE_DIR, NULL);
1876 g_free(last_data_root);
1877 last_data_root = NULL;
1882 return imap_cache_dir;
1885 const gchar *get_mime_tmp_dir(void)
1887 static gchar *mime_tmp_dir = NULL;
1890 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1891 MIME_TMP_DIR, NULL);
1893 return mime_tmp_dir;
1896 const gchar *get_template_dir(void)
1898 static gchar *template_dir = NULL;
1901 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1902 TEMPLATE_DIR, NULL);
1904 return template_dir;
1907 /* Return the default directory for Plugins. */
1908 const gchar *get_plugin_dir(void)
1911 static gchar *plugin_dir = NULL;
1914 plugin_dir = g_strconcat(w32_get_module_dir(),
1915 "\\lib\\claws-mail\\plugins\\",
1919 if (is_dir_exist(PLUGINDIR))
1922 static gchar *plugin_dir = NULL;
1924 plugin_dir = g_strconcat(get_rc_dir(),
1925 G_DIR_SEPARATOR_S, "plugins",
1926 G_DIR_SEPARATOR_S, NULL);
1932 const gchar *get_tmp_dir(void)
1934 static gchar *tmp_dir = NULL;
1937 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1943 gchar *get_tmp_file(void)
1946 static guint32 id = 0;
1948 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1949 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1954 const gchar *get_domain_name(void)
1957 static gchar *domain_name = NULL;
1963 if (gethostname(hostname, sizeof(hostname)) != 0) {
1964 perror("gethostname");
1965 domain_name = "unknown";
1967 hostname[sizeof(hostname) - 1] = '\0';
1968 if ((hp = my_gethostbyname(hostname)) == NULL) {
1969 perror("gethostbyname");
1970 domain_name = g_strdup(hostname);
1972 domain_name = g_strdup(hp->h_name);
1975 debug_print("domain name = %s\n", domain_name);
1984 off_t get_file_size(const gchar *file)
1988 if (g_stat(file, &s) < 0) {
1989 FILE_OP_ERROR(file, "stat");
1996 time_t get_file_mtime(const gchar *file)
2000 if (g_stat(file, &s) < 0) {
2001 FILE_OP_ERROR(file, "stat");
2008 off_t get_file_size_as_crlf(const gchar *file)
2012 gchar buf[BUFFSIZE];
2014 if ((fp = g_fopen(file, "rb")) == NULL) {
2015 FILE_OP_ERROR(file, "fopen");
2019 while (fgets(buf, sizeof(buf), fp) != NULL) {
2021 size += strlen(buf) + 2;
2025 FILE_OP_ERROR(file, "fgets");
2034 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2041 if (g_stat(file, &s) < 0) {
2042 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2046 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2053 /* Test on whether FILE is a relative file name. This is
2054 * straightforward for Unix but more complex for Windows. */
2055 gboolean is_relative_filename(const gchar *file)
2060 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2061 return FALSE; /* Prefixed with a hostname - this can't
2062 * be a relative name. */
2064 if ( ((*file >= 'a' && *file <= 'z')
2065 || (*file >= 'A' && *file <= 'Z'))
2067 file += 2; /* Skip drive letter. */
2069 return !(*file == '\\' || *file == '/');
2071 return !(*file == G_DIR_SEPARATOR);
2076 gboolean is_dir_exist(const gchar *dir)
2081 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2084 gboolean is_file_entry_exist(const gchar *file)
2089 return g_file_test(file, G_FILE_TEST_EXISTS);
2092 gboolean dirent_is_regular_file(struct dirent *d)
2094 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2095 if (d->d_type == DT_REG)
2097 else if (d->d_type != DT_UNKNOWN)
2101 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2104 gint change_dir(const gchar *dir)
2106 gchar *prevdir = NULL;
2109 prevdir = g_get_current_dir();
2111 if (g_chdir(dir) < 0) {
2112 FILE_OP_ERROR(dir, "chdir");
2113 if (debug_mode) g_free(prevdir);
2115 } else if (debug_mode) {
2118 cwd = g_get_current_dir();
2119 if (strcmp(prevdir, cwd) != 0)
2120 g_print("current dir: %s\n", cwd);
2128 gint make_dir(const gchar *dir)
2130 if (g_mkdir(dir, S_IRWXU) < 0) {
2131 FILE_OP_ERROR(dir, "mkdir");
2134 if (g_chmod(dir, S_IRWXU) < 0)
2135 FILE_OP_ERROR(dir, "chmod");
2140 gint make_dir_hier(const gchar *dir)
2145 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2146 parent_dir = g_strndup(dir, p - dir);
2147 if (*parent_dir != '\0') {
2148 if (!is_dir_exist(parent_dir)) {
2149 if (make_dir(parent_dir) < 0) {
2158 if (!is_dir_exist(dir)) {
2159 if (make_dir(dir) < 0)
2166 gint remove_all_files(const gchar *dir)
2169 const gchar *dir_name;
2172 prev_dir = g_get_current_dir();
2174 if (g_chdir(dir) < 0) {
2175 FILE_OP_ERROR(dir, "chdir");
2180 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2181 g_warning("failed to open directory: %s\n", dir);
2186 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2187 if (g_unlink(dir_name) < 0)
2188 FILE_OP_ERROR(dir_name, "unlink");
2193 if (g_chdir(prev_dir) < 0) {
2194 FILE_OP_ERROR(prev_dir, "chdir");
2204 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2207 const gchar *dir_name;
2211 prev_dir = g_get_current_dir();
2213 if (g_chdir(dir) < 0) {
2214 FILE_OP_ERROR(dir, "chdir");
2219 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2220 g_warning("failed to open directory: %s\n", dir);
2225 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2226 file_no = to_number(dir_name);
2227 if (file_no > 0 && first <= file_no && file_no <= last) {
2228 if (is_dir_exist(dir_name))
2230 if (g_unlink(dir_name) < 0)
2231 FILE_OP_ERROR(dir_name, "unlink");
2237 if (g_chdir(prev_dir) < 0) {
2238 FILE_OP_ERROR(prev_dir, "chdir");
2248 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2251 const gchar *dir_name;
2255 prev_dir = g_get_current_dir();
2257 if (g_chdir(dir) < 0) {
2258 FILE_OP_ERROR(dir, "chdir");
2263 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2264 FILE_OP_ERROR(dir, "opendir");
2269 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2270 file_no = to_number(dir_name);
2271 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2272 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2273 if (is_dir_exist(dir_name))
2275 if (g_unlink(dir_name) < 0)
2276 FILE_OP_ERROR(dir_name, "unlink");
2282 if (g_chdir(prev_dir) < 0) {
2283 FILE_OP_ERROR(prev_dir, "chdir");
2293 gint remove_all_numbered_files(const gchar *dir)
2295 return remove_numbered_files(dir, 0, UINT_MAX);
2298 gint remove_dir_recursive(const gchar *dir)
2302 const gchar *dir_name;
2305 if (g_stat(dir, &s) < 0) {
2306 FILE_OP_ERROR(dir, "stat");
2307 if (ENOENT == errno) return 0;
2311 if (!S_ISDIR(s.st_mode)) {
2312 if (g_unlink(dir) < 0) {
2313 FILE_OP_ERROR(dir, "unlink");
2320 prev_dir = g_get_current_dir();
2321 /* g_print("prev_dir = %s\n", prev_dir); */
2323 if (!path_cmp(prev_dir, dir)) {
2325 if (g_chdir("..") < 0) {
2326 FILE_OP_ERROR(dir, "chdir");
2329 prev_dir = g_get_current_dir();
2332 if (g_chdir(dir) < 0) {
2333 FILE_OP_ERROR(dir, "chdir");
2338 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2339 g_warning("failed to open directory: %s\n", dir);
2345 /* remove all files in the directory */
2346 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2347 /* g_print("removing %s\n", dir_name); */
2349 if (is_dir_exist(dir_name)) {
2350 if (remove_dir_recursive(dir_name) < 0) {
2351 g_warning("can't remove directory\n");
2355 if (g_unlink(dir_name) < 0)
2356 FILE_OP_ERROR(dir_name, "unlink");
2362 if (g_chdir(prev_dir) < 0) {
2363 FILE_OP_ERROR(prev_dir, "chdir");
2370 if (g_rmdir(dir) < 0) {
2371 FILE_OP_ERROR(dir, "rmdir");
2378 gint rename_force(const gchar *oldpath, const gchar *newpath)
2381 if (!is_file_entry_exist(oldpath)) {
2385 if (is_file_exist(newpath)) {
2386 if (g_unlink(newpath) < 0)
2387 FILE_OP_ERROR(newpath, "unlink");
2390 return g_rename(oldpath, newpath);
2394 * Append src file body to the tail of dest file.
2395 * Now keep_backup has no effects.
2397 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2399 FILE *src_fp, *dest_fp;
2403 gboolean err = FALSE;
2405 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2406 FILE_OP_ERROR(src, "fopen");
2410 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2411 FILE_OP_ERROR(dest, "fopen");
2416 if (change_file_mode_rw(dest_fp, dest) < 0) {
2417 FILE_OP_ERROR(dest, "chmod");
2418 g_warning("can't change file mode\n");
2421 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2422 if (n_read < sizeof(buf) && ferror(src_fp))
2424 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2425 g_warning("writing to %s failed.\n", dest);
2433 if (ferror(src_fp)) {
2434 FILE_OP_ERROR(src, "fread");
2438 if (fclose(dest_fp) == EOF) {
2439 FILE_OP_ERROR(dest, "fclose");
2451 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2453 FILE *src_fp, *dest_fp;
2456 gchar *dest_bak = NULL;
2457 gboolean err = FALSE;
2459 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2460 FILE_OP_ERROR(src, "fopen");
2463 if (is_file_exist(dest)) {
2464 dest_bak = g_strconcat(dest, ".bak", NULL);
2465 if (rename_force(dest, dest_bak) < 0) {
2466 FILE_OP_ERROR(dest, "rename");
2473 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2474 FILE_OP_ERROR(dest, "fopen");
2477 if (rename_force(dest_bak, dest) < 0)
2478 FILE_OP_ERROR(dest_bak, "rename");
2484 if (change_file_mode_rw(dest_fp, dest) < 0) {
2485 FILE_OP_ERROR(dest, "chmod");
2486 g_warning("can't change file mode\n");
2489 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2490 if (n_read < sizeof(buf) && ferror(src_fp))
2492 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2493 g_warning("writing to %s failed.\n", dest);
2498 if (rename_force(dest_bak, dest) < 0)
2499 FILE_OP_ERROR(dest_bak, "rename");
2506 if (ferror(src_fp)) {
2507 FILE_OP_ERROR(src, "fread");
2511 if (fclose(dest_fp) == EOF) {
2512 FILE_OP_ERROR(dest, "fclose");
2519 if (rename_force(dest_bak, dest) < 0)
2520 FILE_OP_ERROR(dest_bak, "rename");
2526 if (keep_backup == FALSE && dest_bak)
2534 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2536 if (overwrite == FALSE && is_file_exist(dest)) {
2537 g_warning("move_file(): file %s already exists.", dest);
2541 if (rename_force(src, dest) == 0) return 0;
2543 if (EXDEV != errno) {
2544 FILE_OP_ERROR(src, "rename");
2548 if (copy_file(src, dest, FALSE) < 0) return -1;
2555 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2558 gint bytes_left, to_read;
2561 if (fseek(fp, offset, SEEK_SET) < 0) {
2566 bytes_left = length;
2567 to_read = MIN(bytes_left, sizeof(buf));
2569 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2570 if (n_read < to_read && ferror(fp))
2572 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2575 bytes_left -= n_read;
2576 if (bytes_left == 0)
2578 to_read = MIN(bytes_left, sizeof(buf));
2589 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2592 gboolean err = FALSE;
2594 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2595 FILE_OP_ERROR(dest, "fopen");
2599 if (change_file_mode_rw(dest_fp, dest) < 0) {
2600 FILE_OP_ERROR(dest, "chmod");
2601 g_warning("can't change file mode\n");
2604 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2607 if (!err && fclose(dest_fp) == EOF) {
2608 FILE_OP_ERROR(dest, "fclose");
2613 g_warning("writing to %s failed.\n", dest);
2621 /* convert line endings into CRLF. If the last line doesn't end with
2622 * linebreak, add it.
2624 gchar *canonicalize_str(const gchar *str)
2630 for (p = str; *p != '\0'; ++p) {
2637 if (p == str || *(p - 1) != '\n')
2640 out = outp = g_malloc(new_len + 1);
2641 for (p = str; *p != '\0'; ++p) {
2648 if (p == str || *(p - 1) != '\n') {
2657 gint canonicalize_file(const gchar *src, const gchar *dest)
2659 FILE *src_fp, *dest_fp;
2660 gchar buf[BUFFSIZE];
2662 gboolean err = FALSE;
2663 gboolean last_linebreak = FALSE;
2665 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2666 FILE_OP_ERROR(src, "fopen");
2670 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2671 FILE_OP_ERROR(dest, "fopen");
2676 if (change_file_mode_rw(dest_fp, dest) < 0) {
2677 FILE_OP_ERROR(dest, "chmod");
2678 g_warning("can't change file mode\n");
2681 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2685 if (len == 0) break;
2686 last_linebreak = FALSE;
2688 if (buf[len - 1] != '\n') {
2689 last_linebreak = TRUE;
2690 r = fputs(buf, dest_fp);
2691 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2692 r = fputs(buf, dest_fp);
2695 r = fwrite(buf, 1, len - 1, dest_fp);
2700 r = fputs("\r\n", dest_fp);
2704 g_warning("writing to %s failed.\n", dest);
2712 if (last_linebreak == TRUE) {
2713 if (fputs("\r\n", dest_fp) == EOF)
2717 if (ferror(src_fp)) {
2718 FILE_OP_ERROR(src, "fgets");
2722 if (fclose(dest_fp) == EOF) {
2723 FILE_OP_ERROR(dest, "fclose");
2735 gint canonicalize_file_replace(const gchar *file)
2739 tmp_file = get_tmp_file();
2741 if (canonicalize_file(file, tmp_file) < 0) {
2746 if (move_file(tmp_file, file, TRUE) < 0) {
2747 g_warning("can't replace %s .\n", file);
2757 gchar *normalize_newlines(const gchar *str)
2759 const gchar *p = str;
2762 out = outp = g_malloc(strlen(str) + 1);
2763 for (p = str; *p != '\0'; ++p) {
2765 if (*(p + 1) != '\n')
2776 gchar *get_outgoing_rfc2822_str(FILE *fp)
2778 gchar buf[BUFFSIZE];
2782 str = g_string_new(NULL);
2784 /* output header part */
2785 while (fgets(buf, sizeof(buf), fp) != NULL) {
2787 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2794 else if (next != ' ' && next != '\t') {
2798 if (fgets(buf, sizeof(buf), fp) == NULL)
2802 g_string_append(str, buf);
2803 g_string_append(str, "\r\n");
2809 /* output body part */
2810 while (fgets(buf, sizeof(buf), fp) != NULL) {
2813 g_string_append_c(str, '.');
2814 g_string_append(str, buf);
2815 g_string_append(str, "\r\n");
2819 g_string_free(str, FALSE);
2825 * Create a new boundary in a way that it is very unlikely that this
2826 * will occur in the following text. It would be easy to ensure
2827 * uniqueness if everything is either quoted-printable or base64
2828 * encoded (note that conversion is allowed), but because MIME bodies
2829 * may be nested, it may happen that the same boundary has already
2832 * boundary := 0*69<bchars> bcharsnospace
2833 * bchars := bcharsnospace / " "
2834 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2835 * "+" / "_" / "," / "-" / "." /
2836 * "/" / ":" / "=" / "?"
2838 * some special characters removed because of buggy MTAs
2841 gchar *generate_mime_boundary(const gchar *prefix)
2843 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2844 "abcdefghijklmnopqrstuvwxyz"
2849 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2850 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2853 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2857 gint change_file_mode_rw(FILE *fp, const gchar *file)
2860 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2862 return g_chmod(file, S_IRUSR|S_IWUSR);
2866 FILE *my_tmpfile(void)
2868 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2869 const gchar suffix[] = ".XXXXXX";
2870 const gchar *tmpdir;
2872 const gchar *progname;
2879 tmpdir = get_tmp_dir();
2880 tmplen = strlen(tmpdir);
2881 progname = g_get_prgname();
2882 if (progname == NULL)
2883 progname = "claws-mail";
2884 proglen = strlen(progname);
2885 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2888 memcpy(fname, tmpdir, tmplen);
2889 fname[tmplen] = G_DIR_SEPARATOR;
2890 memcpy(fname + tmplen + 1, progname, proglen);
2891 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2893 fd = mkstemp(fname);
2900 /* verify that we can write in the file after unlinking */
2901 if (write(fd, buf, 1) < 0) {
2908 fp = fdopen(fd, "w+b");
2916 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2921 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2925 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2926 dir, G_DIR_SEPARATOR);
2927 fd = mkstemp_name(template, filename);
2930 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2931 fd = mkstemp(*filename);
2933 return fdopen(fd, "w+");
2936 FILE *str_open_as_stream(const gchar *str)
2941 g_return_val_if_fail(str != NULL, NULL);
2945 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2950 if (len == 0) return fp;
2952 if (fwrite(str, 1, len, fp) != len) {
2953 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2962 gint str_write_to_file(const gchar *str, const gchar *file)
2967 g_return_val_if_fail(str != NULL, -1);
2968 g_return_val_if_fail(file != NULL, -1);
2970 if ((fp = g_fopen(file, "wb")) == NULL) {
2971 FILE_OP_ERROR(file, "fopen");
2981 if (fwrite(str, 1, len, fp) != len) {
2982 FILE_OP_ERROR(file, "fwrite");
2988 if (fclose(fp) == EOF) {
2989 FILE_OP_ERROR(file, "fclose");
2997 gchar *file_read_to_str(const gchar *file)
3002 g_return_val_if_fail(file != NULL, NULL);
3004 if ((fp = g_fopen(file, "rb")) == NULL) {
3005 FILE_OP_ERROR(file, "fopen");
3009 str = file_read_stream_to_str(fp);
3016 gchar *file_read_stream_to_str(FILE *fp)
3023 g_return_val_if_fail(fp != NULL, NULL);
3025 array = g_byte_array_new();
3027 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3028 if (n_read < sizeof(buf) && ferror(fp))
3030 g_byte_array_append(array, buf, n_read);
3034 FILE_OP_ERROR("file stream", "fread");
3035 g_byte_array_free(array, TRUE);
3040 g_byte_array_append(array, buf, 1);
3041 str = (gchar *)array->data;
3042 g_byte_array_free(array, FALSE);
3044 if (!g_utf8_validate(str, -1, NULL)) {
3045 const gchar *src_codeset, *dest_codeset;
3047 src_codeset = conv_get_locale_charset_str();
3048 dest_codeset = CS_UTF_8;
3049 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3058 char *fgets_crlf(char *buf, int size, FILE *stream)
3060 gboolean is_cr = FALSE;
3061 gboolean last_was_cr = FALSE;
3066 while (--size > 0 && (c = getc(stream)) != EOF)
3069 is_cr = (c == '\r');
3079 last_was_cr = is_cr;
3081 if (c == EOF && cs == buf)
3089 static gint execute_async(gchar *const argv[])
3091 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3093 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3094 NULL, NULL, NULL, FALSE) == FALSE) {
3095 g_warning("Couldn't execute command: %s\n", argv[0]);
3102 static gint execute_sync(gchar *const argv[])
3106 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3108 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3109 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3110 g_warning("Couldn't execute command: %s\n", argv[0]);
3115 if (WIFEXITED(status))
3116 return WEXITSTATUS(status);
3124 gint execute_command_line(const gchar *cmdline, gboolean async)
3129 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3131 argv = strsplit_with_quote(cmdline, " ", 0);
3134 ret = execute_async(argv);
3136 ret = execute_sync(argv);
3143 gchar *get_command_output(const gchar *cmdline)
3145 gchar *child_stdout;
3148 g_return_val_if_fail(cmdline != NULL, NULL);
3150 debug_print("get_command_output(): executing: %s\n", cmdline);
3152 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3154 g_warning("Couldn't execute command: %s\n", cmdline);
3158 return child_stdout;
3161 static gint is_unchanged_uri_char(char c)
3172 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3178 for(i = 0; i < strlen(uri) ; i++) {
3179 if (is_unchanged_uri_char(uri[i])) {
3180 if (k + 2 >= bufsize)
3182 encoded_uri[k++] = uri[i];
3185 char * hexa = "0123456789ABCDEF";
3187 if (k + 4 >= bufsize)
3189 encoded_uri[k++] = '%';
3190 encoded_uri[k++] = hexa[uri[i] / 16];
3191 encoded_uri[k++] = hexa[uri[i] % 16];
3197 gint open_uri(const gchar *uri, const gchar *cmdline)
3200 gchar buf[BUFFSIZE];
3202 gchar encoded_uri[BUFFSIZE];
3203 g_return_val_if_fail(uri != NULL, -1);
3205 /* an option to choose whether to use encode_uri or not ? */
3206 encode_uri(encoded_uri, BUFFSIZE, uri);
3209 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3210 !strchr(p + 2, '%'))
3211 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3214 g_warning("Open URI command line is invalid "
3215 "(there must be only one '%%s'): %s",
3217 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3220 execute_command_line(buf, TRUE);
3222 extern osso_context_t *get_osso_context(void);
3223 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3224 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3225 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3230 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3232 gchar buf[BUFFSIZE];
3235 g_return_val_if_fail(filepath != NULL, -1);
3238 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3239 !strchr(p + 2, '%'))
3240 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3243 g_warning("Open Text Editor command line is invalid "
3244 "(there must be only one '%%s'): %s",
3246 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3249 execute_command_line(buf, TRUE);
3254 time_t remote_tzoffset_sec(const gchar *zone)
3256 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3262 time_t remoteoffset;
3264 strncpy(zone3, zone, 3);
3268 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3269 (c == '+' || c == '-')) {
3270 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3272 remoteoffset = -remoteoffset;
3273 } else if (!strncmp(zone, "UT" , 2) ||
3274 !strncmp(zone, "GMT", 2)) {
3276 } else if (strlen(zone3) == 3) {
3277 for (p = ustzstr; *p != '\0'; p += 3) {
3278 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3279 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3280 remoteoffset = iustz * 3600;
3286 } else if (strlen(zone3) == 1) {
3288 case 'Z': remoteoffset = 0; break;
3289 case 'A': remoteoffset = -1; break;
3290 case 'B': remoteoffset = -2; break;
3291 case 'C': remoteoffset = -3; break;
3292 case 'D': remoteoffset = -4; break;
3293 case 'E': remoteoffset = -5; break;
3294 case 'F': remoteoffset = -6; break;
3295 case 'G': remoteoffset = -7; break;
3296 case 'H': remoteoffset = -8; break;
3297 case 'I': remoteoffset = -9; break;
3298 case 'K': remoteoffset = -10; break; /* J is not used */
3299 case 'L': remoteoffset = -11; break;
3300 case 'M': remoteoffset = -12; break;
3301 case 'N': remoteoffset = 1; break;
3302 case 'O': remoteoffset = 2; break;
3303 case 'P': remoteoffset = 3; break;
3304 case 'Q': remoteoffset = 4; break;
3305 case 'R': remoteoffset = 5; break;
3306 case 'S': remoteoffset = 6; break;
3307 case 'T': remoteoffset = 7; break;
3308 case 'U': remoteoffset = 8; break;
3309 case 'V': remoteoffset = 9; break;
3310 case 'W': remoteoffset = 10; break;
3311 case 'X': remoteoffset = 11; break;
3312 case 'Y': remoteoffset = 12; break;
3313 default: remoteoffset = 0; break;
3315 remoteoffset = remoteoffset * 3600;
3319 return remoteoffset;
3322 time_t tzoffset_sec(time_t *now)
3326 struct tm buf1, buf2;
3328 gmt = *gmtime_r(now, &buf1);
3329 lt = localtime_r(now, &buf2);
3331 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3333 if (lt->tm_year < gmt.tm_year)
3335 else if (lt->tm_year > gmt.tm_year)
3337 else if (lt->tm_yday < gmt.tm_yday)
3339 else if (lt->tm_yday > gmt.tm_yday)
3342 if (off >= 24 * 60) /* should be impossible */
3343 off = 23 * 60 + 59; /* if not, insert silly value */
3344 if (off <= -24 * 60)
3345 off = -(23 * 60 + 59);
3350 /* calculate timezone offset */
3351 gchar *tzoffset(time_t *now)
3353 static gchar offset_string[6];
3357 struct tm buf1, buf2;
3359 gmt = *gmtime_r(now, &buf1);
3360 lt = localtime_r(now, &buf2);
3362 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3364 if (lt->tm_year < gmt.tm_year)
3366 else if (lt->tm_year > gmt.tm_year)
3368 else if (lt->tm_yday < gmt.tm_yday)
3370 else if (lt->tm_yday > gmt.tm_yday)
3378 if (off >= 24 * 60) /* should be impossible */
3379 off = 23 * 60 + 59; /* if not, insert silly value */
3381 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3383 return offset_string;
3386 void get_rfc822_date(gchar *buf, gint len)
3390 gchar day[4], mon[4];
3391 gint dd, hh, mm, ss, yyyy;
3393 gchar buf2[BUFFSIZE];
3396 lt = localtime_r(&t, &buf1);
3398 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3399 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3401 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3402 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3405 void debug_set_mode(gboolean mode)
3410 gboolean debug_get_mode(void)
3415 void debug_print_real(const gchar *format, ...)
3418 gchar buf[BUFFSIZE];
3420 if (!debug_mode) return;
3422 va_start(args, format);
3423 g_vsnprintf(buf, sizeof(buf), format, args);
3430 const char * debug_srcname(const char *file)
3432 const char *s = strrchr (file, '/');
3437 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3439 if (subject == NULL)
3442 subject += subject_get_prefix_length(subject);
3444 return g_hash_table_lookup(subject_table, subject);
3447 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3450 if (subject == NULL || *subject == 0)
3452 subject += subject_get_prefix_length(subject);
3453 g_hash_table_insert(subject_table, subject, data);
3456 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3458 if (subject == NULL)
3461 subject += subject_get_prefix_length(subject);
3462 g_hash_table_remove(subject_table, subject);
3466 *\brief Check if a string is prefixed with known (combinations)
3467 * of prefixes. The function assumes that each prefix
3468 * is terminated by zero or exactly _one_ space.
3470 *\param str String to check for a prefixes
3472 *\return int Number of chars in the prefix that should be skipped
3473 * for a "clean" subject line. If no prefix was found, 0
3476 int subject_get_prefix_length(const gchar *subject)
3478 /*!< Array with allowable reply prefixes regexps. */
3479 static const gchar * const prefixes[] = {
3480 "Re\\:", /* "Re:" */
3481 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3482 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3483 "Aw\\:", /* "Aw:" (German) */
3484 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3485 "Res\\:", /* "Res:" (Brazilian Outlook) */
3486 "Fw\\:", /* "Fw:" Forward */
3487 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3488 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3489 "Rif\\:", /* "Rif:" (Italian Outlook) */
3490 "Sv\\:", /* "Sv" (Norwegian) */
3491 "Vs\\:", /* "Vs" (Norwegian) */
3492 "Ad\\:", /* "Ad" (Norwegian) */
3493 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3496 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3499 static regex_t regex;
3500 static gboolean init_;
3502 if (!subject) return 0;
3503 if (!*subject) return 0;
3506 GString *s = g_string_new("");
3508 for (n = 0; n < PREFIXES; n++)
3509 /* Terminate each prefix regexpression by a
3510 * "\ ?" (zero or ONE space), and OR them */
3511 g_string_append_printf(s, "(%s\\ ?)%s",
3516 g_string_prepend(s, "(");
3517 g_string_append(s, ")+"); /* match at least once */
3518 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3521 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3522 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3523 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3524 debug_print("Error compiling regexp %s\n", s->str);
3525 g_string_free(s, TRUE);
3529 g_string_free(s, TRUE);
3533 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3539 static guint g_stricase_hash(gconstpointer gptr)
3541 guint hash_result = 0;
3544 for (str = gptr; str && *str; str++) {
3545 hash_result += toupper(*str);
3551 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3553 const char *str1 = gptr1;
3554 const char *str2 = gptr2;
3556 return !strcasecmp(str1, str2);
3559 gint g_int_compare(gconstpointer a, gconstpointer b)
3561 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3564 gchar *generate_msgid(gchar *buf, gint len)
3572 lt = localtime_r(&t, &buft);
3574 if (strcmp(buf, "") == 0) {
3575 addr = g_strconcat("@", get_domain_name(), NULL);
3578 addr = g_strconcat("@", buf, NULL);
3581 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3582 lt->tm_year + 1900, lt->tm_mon + 1,
3583 lt->tm_mday, lt->tm_hour,
3584 lt->tm_min, lt->tm_sec,
3585 (guint) rand(), addr);
3592 quote_cmd_argument()
3594 return a quoted string safely usable in argument of a command.
3596 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3599 gint quote_cmd_argument(gchar * result, guint size,
3609 for(p = path ; * p != '\0' ; p ++) {
3611 if (isalnum((guchar)*p) || (* p == '/')) {
3612 if (remaining > 0) {
3618 result[size - 1] = '\0';
3623 if (remaining >= 2) {
3631 result[size - 1] = '\0';
3636 if (remaining > 0) {
3640 result[size - 1] = '\0';
3654 static void g_node_map_recursive(GNode *node, gpointer data)
3656 GNodeMapData *mapdata = (GNodeMapData *) data;
3658 GNodeMapData newmapdata;
3661 newdata = mapdata->func(node->data, mapdata->data);
3662 if (newdata != NULL) {
3663 newnode = g_node_new(newdata);
3664 g_node_append(mapdata->parent, newnode);
3666 newmapdata.parent = newnode;
3667 newmapdata.func = mapdata->func;
3668 newmapdata.data = mapdata->data;
3670 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3674 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3677 GNodeMapData mapdata;
3679 g_return_val_if_fail(node != NULL, NULL);
3680 g_return_val_if_fail(func != NULL, NULL);
3682 root = g_node_new(func(node->data, data));
3684 mapdata.parent = root;
3685 mapdata.func = func;
3686 mapdata.data = data;
3688 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3693 #define HEX_TO_INT(val, hex) \
3697 if ('0' <= c && c <= '9') { \
3699 } else if ('a' <= c && c <= 'f') { \
3700 val = c - 'a' + 10; \
3701 } else if ('A' <= c && c <= 'F') { \
3702 val = c - 'A' + 10; \
3708 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3715 if (hi == -1 || lo == -1)
3718 *out = (hi << 4) + lo;
3722 #define INT_TO_HEX(hex, val) \
3725 hex = '0' + (val); \
3727 hex = 'A' + (val) - 10; \
3730 void get_hex_str(gchar *out, guchar ch)
3734 INT_TO_HEX(hex, ch >> 4);
3736 INT_TO_HEX(hex, ch & 0x0f);
3742 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3744 #define G_PRINT_REF g_print
3748 *\brief Register ref counted pointer. It is based on GBoxed, so should
3749 * work with anything that uses the GType system. The semantics
3750 * are similar to a C++ auto pointer, with the exception that
3751 * C doesn't have automatic closure (calling destructors) when
3752 * exiting a block scope.
3753 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3754 * function directly.
3756 *\return GType A GType type.
3758 GType g_auto_pointer_register(void)
3760 static GType auto_pointer_type;
3761 if (!auto_pointer_type)
3763 g_boxed_type_register_static
3764 ("G_TYPE_AUTO_POINTER",
3765 (GBoxedCopyFunc) g_auto_pointer_copy,
3766 (GBoxedFreeFunc) g_auto_pointer_free);
3767 return auto_pointer_type;
3771 *\brief Structure with g_new() allocated pointer guarded by the
3774 typedef struct AutoPointerRef {
3775 void (*free) (gpointer);
3781 *\brief The auto pointer opaque structure that references the
3782 * pointer guard block.
3784 typedef struct AutoPointer {
3785 AutoPointerRef *ref;
3786 gpointer ptr; /*!< access to protected pointer */
3790 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3794 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3795 * ... when assigning, copying and freeing storage elements
3797 * gtk_list_store_new(N_S_COLUMNS,
3798 * G_TYPE_AUTO_POINTER,
3802 * Template *precious_data = g_new0(Template, 1);
3803 * g_pointer protect = g_auto_pointer_new(precious_data);
3805 * gtk_list_store_set(container, &iter,
3809 * ... the gtk_list_store has copied the pointer and
3810 * ... incremented its reference count, we should free
3811 * ... the auto pointer (in C++ a destructor would do
3812 * ... this for us when leaving block scope)
3814 * g_auto_pointer_free(protect);
3816 * ... gtk_list_store_set() now manages the data. When
3817 * ... *explicitly* requesting a pointer from the list
3818 * ... store, don't forget you get a copy that should be
3819 * ... freed with g_auto_pointer_free() eventually.
3823 *\param pointer Pointer to be guarded.
3825 *\return GAuto * Pointer that should be used in containers with
3828 GAuto *g_auto_pointer_new(gpointer p)
3830 AutoPointerRef *ref;
3836 ref = g_new0(AutoPointerRef, 1);
3837 ptr = g_new0(AutoPointer, 1);
3847 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3853 *\brief Allocate an autopointer using the passed \a free function to
3854 * free the guarded pointer
3856 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3863 aptr = g_auto_pointer_new(p);
3864 aptr->ref->free = free_;
3868 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3870 if (auto_ptr == NULL)
3872 return ((AutoPointer *) auto_ptr)->ptr;
3876 *\brief Copies an auto pointer by. It's mostly not necessary
3877 * to call this function directly, unless you copy/assign
3878 * the guarded pointer.
3880 *\param auto_ptr Auto pointer returned by previous call to
3881 * g_auto_pointer_new_XXX()
3883 *\return gpointer An auto pointer
3885 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3888 AutoPointerRef *ref;
3891 if (auto_ptr == NULL)
3896 newp = g_new0(AutoPointer, 1);
3899 newp->ptr = ref->pointer;
3903 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3909 *\brief Free an auto pointer
3911 void g_auto_pointer_free(GAuto *auto_ptr)
3914 AutoPointerRef *ref;
3916 if (auto_ptr == NULL)
3922 if (--(ref->cnt) == 0) {
3924 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3926 ref->free(ref->pointer);
3931 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3936 void replace_returns(gchar *str)
3941 while (strstr(str, "\n")) {
3942 *strstr(str, "\n") = ' ';
3944 while (strstr(str, "\r")) {
3945 *strstr(str, "\r") = ' ';
3949 /* get_uri_part() - retrieves a URI starting from scanpos.
3950 Returns TRUE if succesful */
3951 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3952 const gchar **bp, const gchar **ep, gboolean hdr)
3955 gint parenthese_cnt = 0;
3957 g_return_val_if_fail(start != NULL, FALSE);
3958 g_return_val_if_fail(scanpos != NULL, FALSE);
3959 g_return_val_if_fail(bp != NULL, FALSE);
3960 g_return_val_if_fail(ep != NULL, FALSE);
3964 /* find end point of URI */
3965 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
3966 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
3967 !IS_ASCII(*(const guchar *)ep_) ||
3968 strchr("[]{}<>\"", *ep_)) {
3970 } else if (strchr("(", *ep_)) {
3972 } else if (strchr(")", *ep_)) {
3973 if (parenthese_cnt > 0)
3980 /* no punctuation at end of string */
3982 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
3983 * should pass some URI type to this function and decide on that whether
3984 * to perform punctuation stripping */
3986 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
3988 for (; ep_ - 1 > scanpos + 1 &&
3989 IS_REAL_PUNCT(*(ep_ - 1));
3993 #undef IS_REAL_PUNCT
4000 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4002 while (bp && *bp && g_ascii_isspace(*bp))
4004 return g_strndup(bp, ep - bp);
4007 /* valid mail address characters */
4008 #define IS_RFC822_CHAR(ch) \
4012 !g_ascii_isspace(ch) && \
4013 !strchr("(),;<>\"", (ch)))
4015 /* alphabet and number within 7bit ASCII */
4016 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4017 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4019 static GHashTable *create_domain_tab(void)
4021 static const gchar *toplvl_domains [] = {
4023 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4024 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4025 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4026 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4027 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4028 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4029 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4030 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4031 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4032 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4033 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4034 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4035 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4036 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4037 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4038 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4039 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4040 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4041 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4042 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4043 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4044 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4045 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4046 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4047 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4048 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4051 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4053 g_return_val_if_fail(htab, NULL);
4054 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4055 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4059 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4061 const gint MAX_LVL_DOM_NAME_LEN = 6;
4062 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4063 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4066 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4069 for (p = buf; p < m && first < last; *p++ = *first++)
4073 return g_hash_table_lookup(tab, buf) != NULL;
4076 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4077 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4078 const gchar **bp, const gchar **ep, gboolean hdr)
4080 /* more complex than the uri part because we need to scan back and forward starting from
4081 * the scan position. */
4082 gboolean result = FALSE;
4083 const gchar *bp_ = NULL;
4084 const gchar *ep_ = NULL;
4085 static GHashTable *dom_tab;
4086 const gchar *last_dot = NULL;
4087 const gchar *prelast_dot = NULL;
4088 const gchar *last_tld_char = NULL;
4090 /* the informative part of the email address (describing the name
4091 * of the email address owner) may contain quoted parts. the
4092 * closure stack stores the last encountered quotes. */
4093 gchar closure_stack[128];
4094 gchar *ptr = closure_stack;
4096 g_return_val_if_fail(start != NULL, FALSE);
4097 g_return_val_if_fail(scanpos != NULL, FALSE);
4098 g_return_val_if_fail(bp != NULL, FALSE);
4099 g_return_val_if_fail(ep != NULL, FALSE);
4102 const gchar *start_quote = NULL;
4103 const gchar *end_quote = NULL;
4105 /* go to the real start */
4106 if (start[0] == ',')
4108 if (start[0] == ';')
4110 while (start[0] == '\n' || start[0] == '\r')
4112 while (start[0] == ' ' || start[0] == '\t')
4117 /* check if there are quotes (to skip , in them) */
4118 if (*start == '"') {
4119 start_quote = start;
4121 end_quote = strstr(start, "\"");
4127 /* skip anything between quotes */
4128 if (start_quote && end_quote) {
4133 /* find end (either , or ; or end of line) */
4134 if (strstr(start, ",") && strstr(start, ";"))
4135 *ep = strstr(start,",") < strstr(start, ";")
4136 ? strstr(start, ",") : strstr(start, ";");
4137 else if (strstr(start, ","))
4138 *ep = strstr(start, ",");
4139 else if (strstr(start, ";"))
4140 *ep = strstr(start, ";");
4142 *ep = start+strlen(start);
4144 /* go back to real start */
4145 if (start_quote && end_quote) {
4146 start = start_quote;
4149 /* check there's still an @ in that, or search
4150 * further if possible */
4151 if (strstr(start, "@") && strstr(start, "@") < *ep)
4153 else if (*ep < start+strlen(start)) {
4156 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4164 dom_tab = create_domain_tab();
4165 g_return_val_if_fail(dom_tab, FALSE);
4167 /* scan start of address */
4168 for (bp_ = scanpos - 1;
4169 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4172 /* TODO: should start with an alnum? */
4174 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4177 if (bp_ != scanpos) {
4178 /* scan end of address */
4179 for (ep_ = scanpos + 1;
4180 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4182 prelast_dot = last_dot;
4184 if (*(last_dot + 1) == '.') {
4185 if (prelast_dot == NULL)
4187 last_dot = prelast_dot;
4192 /* TODO: really should terminate with an alnum? */
4193 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4198 if (last_dot == NULL)
4200 if (last_dot >= ep_)
4201 last_dot = prelast_dot;
4202 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4206 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4207 if (*last_tld_char == '?')
4210 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4217 if (!result) return FALSE;
4219 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4220 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4221 && IS_RFC822_CHAR(*(ep_ + 3))) {
4222 /* this informative part with an @ in it is
4223 * followed by the email address */
4226 /* go to matching '>' (or next non-rfc822 char, like \n) */
4227 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4230 /* include the bracket */
4231 if (*ep_ == '>') ep_++;
4233 /* include the leading quote */
4241 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4242 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4245 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4246 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4249 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4250 #define IN_STACK() (ptr > closure_stack)
4251 /* has underrun check */
4252 #define POP_STACK() if(IN_STACK()) --ptr
4253 /* has overrun check */
4254 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4255 /* has underrun check */
4256 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4260 /* scan for the informative part. */
4261 for (bp_ -= 2; bp_ >= start; bp_--) {
4262 /* if closure on the stack keep scanning */
4263 if (PEEK_STACK() == *bp_) {
4267 if (*bp_ == '\'' || *bp_ == '"') {
4272 /* if nothing in the closure stack, do the special conditions
4273 * the following if..else expression simply checks whether
4274 * a token is acceptable. if not acceptable, the clause
4275 * should terminate the loop with a 'break' */
4276 if (!PEEK_STACK()) {
4278 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4279 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4280 /* hyphens are allowed, but only in
4282 } else if (strchr(" \"'", *bp_)) {
4283 /* but anything not being a punctiation
4286 break; /* anything else is rejected */
4293 /* scan forward (should start with an alnum) */
4294 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4310 #undef IS_ASCII_ALNUM
4311 #undef IS_RFC822_CHAR
4313 gchar *make_email_string(const gchar *bp, const gchar *ep)
4315 /* returns a mailto: URI; mailto: is also used to detect the
4316 * uri type later on in the button_pressed signal handler */
4320 tmp = g_strndup(bp, ep - bp);
4321 result = g_strconcat("mailto:", tmp, NULL);
4327 gchar *make_http_string(const gchar *bp, const gchar *ep)
4329 /* returns an http: URI; */
4333 while (bp && *bp && g_ascii_isspace(*bp))
4335 tmp = g_strndup(bp, ep - bp);
4336 result = g_strconcat("http://", tmp, NULL);
4342 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4344 FILE *fp = fopen(path, "rb");
4345 gchar buf[BUFFSIZE];
4346 gchar *result = NULL;
4349 while (fgets(buf, sizeof (buf), fp) != NULL) {
4350 gchar **parts = g_strsplit(buf, ";", 3);
4351 gchar *trimmed = parts[0];
4352 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4354 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4355 trimmed[strlen(trimmed)-1] = '\0';
4357 if (!strcmp(trimmed, type)) {
4358 gboolean needsterminal = FALSE;
4359 if (parts[2] && strstr(parts[2], "needsterminal")) {
4360 needsterminal = TRUE;
4362 if (parts[2] && strstr(parts[2], "test=")) {
4363 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4364 gchar *testcmd = orig_testcmd;
4365 if (strstr(testcmd,";"))
4366 *(strstr(testcmd,";")) = '\0';
4367 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4369 while (testcmd[strlen(testcmd)-1] == '\n')
4370 testcmd[strlen(testcmd)-1] = '\0';
4371 while (testcmd[strlen(testcmd)-1] == '\r')
4372 testcmd[strlen(testcmd)-1] = '\0';
4373 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4374 testcmd[strlen(testcmd)-1] = '\0';
4376 if (strstr(testcmd, "%s")) {
4377 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4378 gint res = system(tmp);
4380 g_free(orig_testcmd);
4387 gint res = system(testcmd);
4388 g_free(orig_testcmd);
4398 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4400 while (trimmed[strlen(trimmed)-1] == '\n')
4401 trimmed[strlen(trimmed)-1] = '\0';
4402 while (trimmed[strlen(trimmed)-1] == '\r')
4403 trimmed[strlen(trimmed)-1] = '\0';
4404 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4405 trimmed[strlen(trimmed)-1] = '\0';
4406 result = g_strdup(trimmed);
4409 /* if there are no single quotes around %s, add them.
4410 * '.*%s.*' is ok, as in display 'png:%s'
4412 if (strstr(result, "%s")
4413 && !(strstr(result, "'") < strstr(result,"%s") &&
4414 strstr(strstr(result,"%s"), "'"))) {
4415 gchar *start = g_strdup(result);
4416 gchar *end = g_strdup(strstr(result, "%s")+2);
4418 *strstr(start, "%s") = '\0';
4419 tmp = g_strconcat(start,"'%s'",end, NULL);
4425 if (needsterminal) {
4426 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4437 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4439 gchar *result = NULL;
4443 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4444 result = mailcap_get_command_in_file(path, type, file_to_open);
4448 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4452 void mailcap_update_default(const gchar *type, const gchar *command)
4454 gchar *path = NULL, *outpath = NULL;
4455 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4456 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4457 FILE *fp = fopen(path, "rb");
4458 FILE *outfp = fopen(outpath, "wb");
4459 gchar buf[BUFFSIZE];
4460 gboolean err = FALSE;
4468 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4469 gchar **parts = g_strsplit(buf, ";", 3);
4470 gchar *trimmed = parts[0];
4471 while (trimmed[0] == ' ')
4473 while (trimmed[strlen(trimmed)-1] == ' ')
4474 trimmed[strlen(trimmed)-1] = '\0';
4476 if (!strcmp(trimmed, type)) {
4481 if(fputs(buf, outfp) == EOF) {
4488 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4494 if (fclose(outfp) == EOF)
4498 g_rename(outpath, path);
4504 gint copy_dir(const gchar *src, const gchar *dst)
4509 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4510 g_warning("failed to open directory: %s\n", src);
4514 if (make_dir(dst) < 0)
4517 while ((name = g_dir_read_name(dir)) != NULL) {
4518 gchar *old_file, *new_file;
4519 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4520 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4521 debug_print("copying: %s -> %s\n", old_file, new_file);
4522 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4523 gint r = copy_file(old_file, new_file, TRUE);
4530 /* Windows has no symlinks. Or well, Vista seems to
4531 have something like this but the semantics might be
4532 different. Thus we don't use it under Windows. */
4533 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4536 gchar *target = g_file_read_link(old_file, &error);
4538 r = symlink(target, new_file);
4545 #endif /*G_OS_WIN32*/
4546 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4547 gint r = copy_dir(old_file, new_file);
4558 /* crude test to see if a file is an email. */
4559 gboolean file_is_email (const gchar *filename)
4565 if (filename == NULL)
4567 if ((fp = g_fopen(filename, "rb")) == NULL)
4569 while (i < 60 && score < 3
4570 && fgets(buffer, sizeof (buffer), fp) > 0) {
4571 if (!strncmp(buffer, "From:", strlen("From:")))
4573 if (!strncmp(buffer, "To:", strlen("To:")))
4575 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4580 return (score >= 3);
4583 gboolean sc_g_list_bigger(GList *list, gint max)
4587 while (cur && i <= max+1) {
4594 gboolean sc_g_slist_bigger(GSList *list, gint max)
4598 while (cur && i <= max+1) {
4605 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4606 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4607 NULL, NULL, NULL, NULL, NULL, NULL};
4608 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4609 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4610 NULL, NULL, NULL, NULL, NULL, NULL};
4611 const gchar *s_am_up = NULL;
4612 const gchar *s_pm_up = NULL;
4613 const gchar *s_am_low = NULL;
4614 const gchar *s_pm_low = NULL;
4615 const gchar *def_loc_format = NULL;
4616 const gchar *date_loc_format = NULL;
4617 const gchar *time_loc_format = NULL;
4618 const gchar *time_am_pm = NULL;
4620 static gboolean time_names_init_done = FALSE;
4622 static void init_time_names(void)
4624 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4625 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4626 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4627 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4628 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4629 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4630 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4632 monthnames[0] = Q_("Complete month name for use by strftime|January");
4633 monthnames[1] = Q_("Complete month name for use by strftime|February");
4634 monthnames[2] = Q_("Complete month name for use by strftime|March");
4635 monthnames[3] = Q_("Complete month name for use by strftime|April");
4636 monthnames[4] = Q_("Complete month name for use by strftime|May");
4637 monthnames[5] = Q_("Complete month name for use by strftime|June");
4638 monthnames[6] = Q_("Complete month name for use by strftime|July");
4639 monthnames[7] = Q_("Complete month name for use by strftime|August");
4640 monthnames[8] = Q_("Complete month name for use by strftime|September");
4641 monthnames[9] = Q_("Complete month name for use by strftime|October");
4642 monthnames[10] = Q_("Complete month name for use by strftime|November");
4643 monthnames[11] = Q_("Complete month name for use by strftime|December");
4645 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4646 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4647 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4648 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4649 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4650 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4651 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4653 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4654 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4655 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4656 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4657 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4658 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4659 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4660 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4661 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4662 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4663 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4664 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4666 s_am_up = Q_("For use by strftime (morning)|AM");
4667 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4668 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4669 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4671 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4672 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4673 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4675 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4677 time_names_init_done = TRUE;
4680 #define CHECK_SIZE() { \
4681 total_done += len; \
4682 if (total_done >= buflen) { \
4683 buf[buflen-1] = '\0'; \
4688 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4690 gchar *curpos = buf;
4691 gint total_done = 0;
4692 gchar subbuf[64], subfmt[64];
4693 static time_t last_tzset = (time_t)0;
4695 if (!time_names_init_done)
4698 if (format == NULL || lt == NULL)
4701 if (last_tzset != time(NULL)) {
4703 last_tzset = time(NULL);
4706 if (*format == '%') {
4707 gint len = 0, tmp = 0;
4711 len = 1; CHECK_SIZE();
4715 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4716 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4719 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4720 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4724 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4725 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4728 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4729 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4732 fast_strftime(subbuf, 64, def_loc_format, lt);
4733 len = strlen(subbuf); CHECK_SIZE();
4734 strncpy2(curpos, subbuf, buflen - total_done);
4737 total_done += 2; CHECK_SIZE();
4738 tmp = (lt->tm_year + 1900)/100;
4739 *curpos++ = '0'+(tmp / 10);
4740 *curpos++ = '0'+(tmp % 10);
4743 total_done += 2; CHECK_SIZE();
4744 *curpos++ = '0'+(lt->tm_mday / 10);
4745 *curpos++ = '0'+(lt->tm_mday % 10);
4748 total_done += 8; CHECK_SIZE();
4749 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4750 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4752 *curpos++ = '0'+(lt->tm_mday / 10);
4753 *curpos++ = '0'+(lt->tm_mday % 10);
4755 tmp = lt->tm_year%100;
4756 *curpos++ = '0'+(tmp / 10);
4757 *curpos++ = '0'+(tmp % 10);
4760 len = 2; CHECK_SIZE();
4761 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4764 len = 10; CHECK_SIZE();
4765 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4766 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4769 total_done += 2; CHECK_SIZE();
4770 *curpos++ = '0'+(lt->tm_hour / 10);
4771 *curpos++ = '0'+(lt->tm_hour % 10);
4774 total_done += 2; CHECK_SIZE();
4780 *curpos++ = '0'+(tmp / 10);
4781 *curpos++ = '0'+(tmp % 10);
4784 len = 3; CHECK_SIZE();
4785 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4788 len = 2; CHECK_SIZE();
4789 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4792 len = 2; CHECK_SIZE();
4798 snprintf(curpos, buflen - total_done, "%2d", tmp);
4801 total_done += 2; CHECK_SIZE();
4802 tmp = lt->tm_mon + 1;
4803 *curpos++ = '0'+(tmp / 10);
4804 *curpos++ = '0'+(tmp % 10);
4807 total_done += 2; CHECK_SIZE();
4808 *curpos++ = '0'+(lt->tm_min / 10);
4809 *curpos++ = '0'+(lt->tm_min % 10);
4812 len = 1; CHECK_SIZE();
4816 if (lt->tm_hour >= 12) {
4817 len = strlen(s_pm_up); CHECK_SIZE();
4818 snprintf(curpos, buflen-total_done, s_pm_up);
4820 len = strlen(s_am_up); CHECK_SIZE();
4821 snprintf(curpos, buflen-total_done, s_am_up);
4825 if (lt->tm_hour >= 12) {
4826 len = strlen(s_pm_low); CHECK_SIZE();
4827 snprintf(curpos, buflen-total_done, s_pm_low);
4829 len = strlen(s_am_low); CHECK_SIZE();
4830 snprintf(curpos, buflen-total_done, s_am_low);
4834 fast_strftime(subbuf, 64, time_am_pm, lt);
4835 len = strlen(subbuf); CHECK_SIZE();
4836 strncpy2(curpos, subbuf, buflen - total_done);
4839 total_done += 5; CHECK_SIZE();
4840 *curpos++ = '0'+(lt->tm_hour / 10);
4841 *curpos++ = '0'+(lt->tm_hour % 10);
4843 *curpos++ = '0'+(lt->tm_min / 10);
4844 *curpos++ = '0'+(lt->tm_min % 10);
4847 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4848 len = strlen(subbuf); CHECK_SIZE();
4849 strncpy2(curpos, subbuf, buflen - total_done);
4852 total_done += 2; CHECK_SIZE();
4853 *curpos++ = '0'+(lt->tm_sec / 10);
4854 *curpos++ = '0'+(lt->tm_sec % 10);
4857 len = 1; CHECK_SIZE();
4861 total_done += 8; CHECK_SIZE();
4862 *curpos++ = '0'+(lt->tm_hour / 10);
4863 *curpos++ = '0'+(lt->tm_hour % 10);
4865 *curpos++ = '0'+(lt->tm_min / 10);
4866 *curpos++ = '0'+(lt->tm_min % 10);
4868 *curpos++ = '0'+(lt->tm_sec / 10);
4869 *curpos++ = '0'+(lt->tm_sec % 10);
4872 len = 1; CHECK_SIZE();
4873 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4876 len = 1; CHECK_SIZE();
4877 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4880 fast_strftime(subbuf, 64, date_loc_format, lt);
4881 len = strlen(subbuf); CHECK_SIZE();
4882 strncpy2(curpos, subbuf, buflen - total_done);
4885 fast_strftime(subbuf, 64, time_loc_format, lt);
4886 len = strlen(subbuf); CHECK_SIZE();
4887 strncpy2(curpos, subbuf, buflen - total_done);
4890 total_done += 2; CHECK_SIZE();
4891 tmp = lt->tm_year%100;
4892 *curpos++ = '0'+(tmp / 10);
4893 *curpos++ = '0'+(tmp % 10);
4896 len = 4; CHECK_SIZE();
4897 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4907 /* let these complicated ones be done with the libc */
4908 snprintf(subfmt, 64, "%%%c", *format);
4909 strftime(subbuf, 64, subfmt, lt);
4910 len = strlen(subbuf); CHECK_SIZE();
4911 strncpy2(curpos, subbuf, buflen - total_done);
4915 /* let these complicated modifiers be done with the libc */
4916 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4917 strftime(subbuf, 64, subfmt, lt);
4918 len = strlen(subbuf); CHECK_SIZE();
4919 strncpy2(curpos, subbuf, buflen - total_done);
4923 g_warning("format error (%c)", *format);
4930 int len = 1; CHECK_SIZE();
4931 *curpos++ = *format++;