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>
65 #include <osso-browser-interface.h>
70 #include "../codeconv.h"
74 static gboolean debug_mode = FALSE;
76 static GSList *tempfiles=NULL;
79 /* Return true if we are running as root. This function should beused
80 instead of getuid () == 0. */
81 gboolean superuser_p (void)
84 return w32_is_administrator ();
92 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
93 gint g_chdir(const gchar *path)
96 if (G_WIN32_HAVE_WIDECHAR_API()) {
101 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107 retval = _wchdir(wpath);
119 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
120 if (cp_path == NULL) {
125 retval = chdir(cp_path);
138 gint g_chmod(const gchar *path, gint mode)
141 if (G_WIN32_HAVE_WIDECHAR_API()) {
146 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152 retval = _wchmod(wpath, mode);
164 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
165 if (cp_path == NULL) {
170 retval = chmod(cp_path, mode);
179 return chmod(path, mode);
182 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
186 gint mkstemp_name(const gchar *template, gchar **name_used)
188 static gulong count=0; /* W32-_mktemp only supports up to 27
192 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
193 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
194 (S_IRUSR | S_IWUSR));
196 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
198 perror(g_strdup_printf("cant create %s",*name_used));
204 #endif /* G_OS_WIN32 */
207 gint mkstemp(const gchar *template)
210 gint res = mkstemp_name(template, &dummyname);
214 #endif /* G_OS_WIN32 */
216 void list_free_strings(GList *list)
218 list = g_list_first(list);
220 while (list != NULL) {
226 void slist_free_strings(GSList *list)
228 while (list != NULL) {
234 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
239 void hash_free_strings(GHashTable *table)
241 g_hash_table_foreach(table, hash_free_strings_func, NULL);
244 gint str_case_equal(gconstpointer v, gconstpointer v2)
246 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
249 guint str_case_hash(gconstpointer key)
251 const gchar *p = key;
255 h = g_ascii_tolower(h);
256 for (p += 1; *p != '\0'; p++)
257 h = (h << 5) - h + g_ascii_tolower(*p);
263 void ptr_array_free_strings(GPtrArray *array)
268 g_return_if_fail(array != NULL);
270 for (i = 0; i < array->len; i++) {
271 str = g_ptr_array_index(array, i);
276 gboolean str_find(const gchar *haystack, const gchar *needle)
278 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
281 gboolean str_case_find(const gchar *haystack, const gchar *needle)
283 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
286 gint to_number(const gchar *nstr)
288 register const gchar *p;
290 if (*nstr == '\0') return -1;
292 for (p = nstr; *p != '\0'; p++)
293 if (!g_ascii_isdigit(*p)) return -1;
298 /* convert integer into string,
299 nstr must be not lower than 11 characters length */
300 gchar *itos_buf(gchar *nstr, gint n)
302 g_snprintf(nstr, 11, "%d", n);
306 /* convert integer into string */
309 static gchar nstr[11];
311 return itos_buf(nstr, n);
314 #define divide(num,divisor,i,d) \
316 i = num >> divisor; \
317 d = num & ((1<<divisor)-1); \
318 d = (d*100) >> divisor; \
321 gchar *to_human_readable(off_t size)
323 static gchar str[14];
324 static gchar *b_format = NULL, *kb_format = NULL,
325 *mb_format = NULL, *gb_format = NULL;
326 register int t = 0, r = 0;
327 if (b_format == NULL) {
329 kb_format = _("%d.%02dKB");
330 mb_format = _("%d.%02dMB");
331 gb_format = _("%.2fGB");
334 if (size < (off_t)1024) {
335 g_snprintf(str, sizeof(str), b_format, (gint)size);
337 } else if (size >> 10 < (off_t)1024) {
338 divide(size, 10, t, r);
339 g_snprintf(str, sizeof(str), kb_format, t, r);
341 } else if (size >> 20 < (off_t)1024) {
342 divide(size, 20, t, r);
343 g_snprintf(str, sizeof(str), mb_format, t, r);
346 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
351 /* strcmp with NULL-checking */
352 gint strcmp2(const gchar *s1, const gchar *s2)
354 if (s1 == NULL || s2 == NULL)
357 return strcmp(s1, s2);
359 /* strstr with NULL-checking */
360 gchar *strstr2(const gchar *s1, const gchar *s2)
362 if (s1 == NULL || s2 == NULL)
365 return strstr(s1, s2);
368 gint path_cmp(const gchar *s1, const gchar *s2)
373 gchar *s1buf, *s2buf;
376 if (s1 == NULL || s2 == NULL) return -1;
377 if (*s1 == '\0' || *s2 == '\0') return -1;
380 s1buf = g_strdup (s1);
381 s2buf = g_strdup (s2);
382 subst_char (s1buf, '/', G_DIR_SEPARATOR);
383 subst_char (s2buf, '/', G_DIR_SEPARATOR);
386 #endif /* !G_OS_WIN32 */
391 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
392 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
394 rc = strncmp(s1, s2, MAX(len1, len2));
398 #endif /* !G_OS_WIN32 */
402 /* remove trailing return code */
403 gchar *strretchomp(gchar *str)
407 if (!*str) return str;
409 for (s = str + strlen(str) - 1;
410 s >= str && (*s == '\n' || *s == '\r');
417 /* remove trailing character */
418 gchar *strtailchomp(gchar *str, gchar tail_char)
422 if (!*str) return str;
423 if (tail_char == '\0') return str;
425 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
431 /* remove CR (carriage return) */
432 gchar *strcrchomp(gchar *str)
436 if (!*str) return str;
438 s = str + strlen(str) - 1;
439 if (*s == '\n' && s > str && *(s - 1) == '\r') {
447 gint file_strip_crs(const gchar *file)
449 FILE *fp = NULL, *outfp = NULL;
451 gchar *out = get_tmp_file();
455 fp = fopen(file, "rb");
459 outfp = fopen(out, "wb");
465 while (fgets(buf, sizeof (buf), fp) != NULL) {
467 if (fputs(buf, outfp) == EOF) {
475 if (fclose(outfp) == EOF) {
479 if (rename_force(out, file) < 0)
490 /* Similar to `strstr' but this function ignores the case of both strings. */
491 gchar *strcasestr(const gchar *haystack, const gchar *needle)
493 register size_t haystack_len, needle_len;
495 haystack_len = strlen(haystack);
496 needle_len = strlen(needle);
498 if (haystack_len < needle_len || needle_len == 0)
501 while (haystack_len >= needle_len) {
502 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
503 return (gchar *)haystack;
513 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
514 gconstpointer needle, size_t needlelen)
516 const gchar *haystack_ = (const gchar *)haystack;
517 const gchar *needle_ = (const gchar *)needle;
518 const gchar *haystack_cur = (const gchar *)haystack;
521 return memchr(haystack_, *needle_, haystacklen);
523 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
525 if (haystacklen - (haystack_cur - haystack_) < needlelen)
527 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
528 return (gpointer)haystack_cur;
536 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
537 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
539 register const gchar *s = src;
540 register gchar *d = dest;
550 /* Examine if next block is non-ASCII string */
551 gboolean is_next_nonascii(const gchar *s)
555 /* skip head space */
556 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
558 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
559 if (*(guchar *)p > 127 || *(guchar *)p < 32)
566 gint get_next_word_len(const gchar *s)
570 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
576 static void trim_subject_for_compare(gchar *str)
580 eliminate_parenthesis(str, '[', ']');
581 eliminate_parenthesis(str, '(', ')');
584 srcp = str + subject_get_prefix_length(str);
586 memmove(str, srcp, strlen(srcp) + 1);
589 static void trim_subject_for_sort(gchar *str)
595 srcp = str + subject_get_prefix_length(str);
597 memmove(str, srcp, strlen(srcp) + 1);
600 /* compare subjects */
601 gint subject_compare(const gchar *s1, const gchar *s2)
605 if (!s1 || !s2) return -1;
606 if (!*s1 || !*s2) return -1;
608 Xstrdup_a(str1, s1, return -1);
609 Xstrdup_a(str2, s2, return -1);
611 trim_subject_for_compare(str1);
612 trim_subject_for_compare(str2);
614 if (!*str1 || !*str2) return -1;
616 return strcmp(str1, str2);
619 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
623 if (!s1 || !s2) return -1;
625 Xstrdup_a(str1, s1, return -1);
626 Xstrdup_a(str2, s2, return -1);
628 trim_subject_for_sort(str1);
629 trim_subject_for_sort(str2);
631 return g_utf8_collate(str1, str2);
634 void trim_subject(gchar *str)
636 register gchar *srcp;
642 srcp = str + subject_get_prefix_length(str);
647 } else if (*srcp == '(') {
659 else if (*srcp == cl)
666 while (g_ascii_isspace(*srcp)) srcp++;
667 memmove(str, srcp, strlen(srcp) + 1);
670 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
672 register gchar *srcp, *destp;
677 while ((destp = strchr(destp, op))) {
683 else if (*srcp == cl)
689 while (g_ascii_isspace(*srcp)) srcp++;
690 memmove(destp, srcp, strlen(srcp) + 1);
694 void extract_parenthesis(gchar *str, gchar op, gchar cl)
696 register gchar *srcp, *destp;
701 while ((srcp = strchr(destp, op))) {
704 memmove(destp, srcp + 1, strlen(srcp));
709 else if (*destp == cl)
721 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
724 register gchar *srcp, *destp;
726 gboolean in_quote = FALSE;
730 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
733 memmove(destp, srcp + 1, strlen(srcp));
736 if (*destp == op && !in_quote)
738 else if (*destp == cl && !in_quote)
740 else if (*destp == quote_chr)
752 void extract_quote(gchar *str, gchar quote_chr)
756 if ((str = strchr(str, quote_chr))) {
758 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
759 memmove(p - 1, p, strlen(p) + 1);
764 memmove(str, str + 1, p - str);
769 void eliminate_address_comment(gchar *str)
771 register gchar *srcp, *destp;
776 while ((destp = strchr(destp, '"'))) {
777 if ((srcp = strchr(destp + 1, '"'))) {
782 while (g_ascii_isspace(*srcp)) srcp++;
783 memmove(destp, srcp, strlen(srcp) + 1);
793 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
799 else if (*srcp == ')')
805 while (g_ascii_isspace(*srcp)) srcp++;
806 memmove(destp, srcp, strlen(srcp) + 1);
810 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
812 gboolean in_quote = FALSE;
815 if (*str == c && !in_quote)
817 if (*str == quote_chr)
825 void extract_address(gchar *str)
827 eliminate_address_comment(str);
828 if (strchr_with_skip_quote(str, '"', '<'))
829 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
833 void extract_list_id_str(gchar *str)
835 if (strchr_with_skip_quote(str, '"', '<'))
836 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
840 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
845 if (!str) return addr_list;
847 Xstrdup_a(work, str, return addr_list);
850 eliminate_address_comment(work);
853 while (workp && *workp) {
856 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
862 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
863 extract_parenthesis_with_skip_quote
864 (workp, '"', '<', '>');
868 addr_list = g_slist_append(addr_list, g_strdup(workp));
876 GSList *address_list_append(GSList *addr_list, const gchar *str)
878 return address_list_append_real(addr_list, str, TRUE);
881 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
883 return address_list_append_real(addr_list, str, FALSE);
886 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
890 if (!str) return msgid_list;
893 while (strp && *strp) {
894 const gchar *start, *end;
897 if ((start = strchr(strp, '<')) != NULL) {
898 end = strchr(start + 1, '>');
903 msgid = g_strndup(start + 1, end - start - 1);
906 msgid_list = g_slist_prepend(msgid_list, msgid);
916 GSList *references_list_append(GSList *msgid_list, const gchar *str)
920 list = references_list_prepend(NULL, str);
921 list = g_slist_reverse(list);
922 msgid_list = g_slist_concat(msgid_list, list);
927 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
932 if (!str) return group_list;
934 Xstrdup_a(work, str, return group_list);
938 while (workp && *workp) {
941 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
949 group_list = g_slist_append(group_list,
958 GList *add_history(GList *list, const gchar *str)
962 g_return_val_if_fail(str != NULL, list);
964 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
967 list = g_list_remove(list, old->data);
968 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
971 last = g_list_last(list);
974 list = g_list_remove(list, last->data);
978 list = g_list_prepend(list, g_strdup(str));
983 void remove_return(gchar *str)
985 register gchar *p = str;
988 if (*p == '\n' || *p == '\r')
989 memmove(p, p + 1, strlen(p));
995 void remove_space(gchar *str)
997 register gchar *p = str;
1002 while (g_ascii_isspace(*(p + spc)))
1005 memmove(p, p + spc, strlen(p + spc) + 1);
1011 void unfold_line(gchar *str)
1013 register gchar *p = str;
1017 if (*p == '\n' || *p == '\r') {
1020 while (g_ascii_isspace(*(p + spc)))
1023 memmove(p, p + spc, strlen(p + spc) + 1);
1029 void subst_char(gchar *str, gchar orig, gchar subst)
1031 register gchar *p = str;
1040 static void subst_chars(gchar *str, gchar *orig, gchar subst)
1042 register gchar *p = str;
1045 if (strchr(orig, *p) != NULL)
1051 void subst_for_filename(gchar *str)
1056 subst_chars(str, "\t\r\n\\/*:", '_');
1058 subst_chars(str, "\t\r\n\\/*", '_');
1062 void subst_for_shellsafe_filename(gchar *str)
1066 subst_for_filename(str);
1067 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1070 gboolean is_ascii_str(const gchar *str)
1072 const guchar *p = (const guchar *)str;
1074 while (*p != '\0') {
1075 if (*p != '\t' && *p != ' ' &&
1076 *p != '\r' && *p != '\n' &&
1077 (*p < 32 || *p >= 127))
1085 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1087 gchar * position = NULL;
1088 gchar * tmp_pos = NULL;
1091 if (quote_chars == NULL)
1094 for (i = 0; i < strlen(quote_chars); i++) {
1095 tmp_pos = strrchr (str, quote_chars[i]);
1097 || (tmp_pos != NULL && position <= tmp_pos) )
1103 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1105 const gchar *first_pos;
1106 const gchar *last_pos;
1107 const gchar *p = str;
1108 gint quote_level = -1;
1110 /* speed up line processing by only searching to the last '>' */
1111 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1112 /* skip a line if it contains a '<' before the initial '>' */
1113 if (memchr(str, '<', first_pos - str) != NULL)
1115 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1119 while (p <= last_pos) {
1120 while (p < last_pos) {
1121 if (g_ascii_isspace(*p))
1127 if (strchr(quote_chars, *p))
1129 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1130 /* any characters are allowed except '-' and space */
1132 && !strchr(quote_chars, *p)
1133 && !g_ascii_isspace(*p)
1136 if (strchr(quote_chars, *p))
1148 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1150 const gchar *p = str, *q;
1151 gint cur_line = 0, len;
1153 while ((q = strchr(p, '\n')) != NULL) {
1155 if (len > max_chars) {
1165 if (len > max_chars) {
1174 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1176 gchar * position = NULL;
1177 gchar * tmp_pos = NULL;
1180 if (quote_chars == NULL)
1183 for (i = 0; i < strlen(quote_chars); i++) {
1184 tmp_pos = strchr (str, quote_chars[i]);
1186 || (tmp_pos != NULL && position >= tmp_pos) )
1192 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1194 register guint haystack_len, needle_len;
1195 gboolean in_squote = FALSE, in_dquote = FALSE;
1197 haystack_len = strlen(haystack);
1198 needle_len = strlen(needle);
1200 if (haystack_len < needle_len || needle_len == 0)
1203 while (haystack_len >= needle_len) {
1204 if (!in_squote && !in_dquote &&
1205 !strncmp(haystack, needle, needle_len))
1206 return (gchar *)haystack;
1208 /* 'foo"bar"' -> foo"bar"
1209 "foo'bar'" -> foo'bar' */
1210 if (*haystack == '\'') {
1213 else if (!in_dquote)
1215 } else if (*haystack == '\"') {
1218 else if (!in_squote)
1229 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1232 GSList *string_list = NULL, *slist;
1233 gchar **str_array, *s, *new_str;
1234 guint i, n = 1, len;
1236 g_return_val_if_fail(str != NULL, NULL);
1237 g_return_val_if_fail(delim != NULL, NULL);
1240 max_tokens = G_MAXINT;
1242 s = strstr_with_skip_quote(str, delim);
1244 guint delimiter_len = strlen(delim);
1248 new_str = g_strndup(str, len);
1250 if (new_str[0] == '\'' || new_str[0] == '\"') {
1251 if (new_str[len - 1] == new_str[0]) {
1252 new_str[len - 1] = '\0';
1253 memmove(new_str, new_str + 1, len - 1);
1256 string_list = g_slist_prepend(string_list, new_str);
1258 str = s + delimiter_len;
1259 s = strstr_with_skip_quote(str, delim);
1260 } while (--max_tokens && s);
1264 new_str = g_strdup(str);
1265 if (new_str[0] == '\'' || new_str[0] == '\"') {
1267 if (new_str[len - 1] == new_str[0]) {
1268 new_str[len - 1] = '\0';
1269 memmove(new_str, new_str + 1, len - 1);
1272 string_list = g_slist_prepend(string_list, new_str);
1276 str_array = g_new(gchar*, n);
1280 str_array[i--] = NULL;
1281 for (slist = string_list; slist; slist = slist->next)
1282 str_array[i--] = slist->data;
1284 g_slist_free(string_list);
1289 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1291 gchar *abbrev_group;
1293 const gchar *p = group;
1296 g_return_val_if_fail(group != NULL, NULL);
1298 last = group + strlen(group);
1299 abbrev_group = ap = g_malloc(strlen(group) + 1);
1304 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1306 while (*p != '.') p++;
1309 return abbrev_group;
1314 return abbrev_group;
1317 gchar *trim_string(const gchar *str, gint len)
1319 const gchar *p = str;
1324 if (!str) return NULL;
1325 if (strlen(str) <= len)
1326 return g_strdup(str);
1327 if (g_utf8_validate(str, -1, NULL) == FALSE)
1328 return g_strdup(str);
1330 while (*p != '\0') {
1331 mb_len = g_utf8_skip[*(guchar *)p];
1334 else if (new_len + mb_len > len)
1341 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1342 return g_strconcat(new_str, "...", NULL);
1345 GList *uri_list_extract_filenames(const gchar *uri_list)
1347 GList *result = NULL;
1349 gchar *escaped_utf8uri;
1355 while (g_ascii_isspace(*p)) p++;
1356 if (!strncmp(p, "file:", 5)) {
1359 while (*q && *q != '\n' && *q != '\r') q++;
1362 gchar *file, *locale_file = NULL;
1364 while (q > p && g_ascii_isspace(*q))
1366 Xalloca(escaped_utf8uri, q - p + 2,
1368 Xalloca(file, q - p + 2,
1371 strncpy(escaped_utf8uri, p, q - p + 1);
1372 escaped_utf8uri[q - p + 1] = '\0';
1373 decode_uri(file, escaped_utf8uri);
1375 * g_filename_from_uri() rejects escaped/locale encoded uri
1376 * string which come from Nautilus.
1378 if (g_utf8_validate(file, -1, NULL))
1380 = conv_codeset_strdup(
1383 conv_get_locale_charset_str());
1385 locale_file = g_strdup(file + 5);
1386 result = g_list_append(result, locale_file);
1390 p = strchr(p, '\n');
1397 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1400 static gint axtoi(const gchar *hexstr)
1402 gint hi, lo, result;
1405 if ('0' <= hi && hi <= '9') {
1408 if ('a' <= hi && hi <= 'f') {
1411 if ('A' <= hi && hi <= 'F') {
1416 if ('0' <= lo && lo <= '9') {
1419 if ('a' <= lo && lo <= 'f') {
1422 if ('A' <= lo && lo <= 'F') {
1425 result = lo + (16 * hi);
1429 gboolean is_uri_string(const gchar *str)
1431 while (str && *str && g_ascii_isspace(*str))
1433 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1434 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1435 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1436 g_ascii_strncasecmp(str, "www.", 4) == 0);
1439 gchar *get_uri_path(const gchar *uri)
1441 while (uri && *uri && g_ascii_isspace(*uri))
1443 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1444 return (gchar *)(uri + 7);
1445 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1446 return (gchar *)(uri + 8);
1447 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1448 return (gchar *)(uri + 6);
1450 return (gchar *)uri;
1453 gint get_uri_len(const gchar *str)
1457 if (is_uri_string(str)) {
1458 for (p = str; *p != '\0'; p++) {
1459 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1468 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1469 * plusses, and escape characters are used)
1471 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1473 gchar *dec = decoded_uri;
1474 const gchar *enc = encoded_uri;
1479 if (isxdigit((guchar)enc[0]) &&
1480 isxdigit((guchar)enc[1])) {
1486 if (with_plus && *enc == '+')
1498 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1500 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1503 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1505 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1506 decode_uri(buffer, encoded_uri);
1510 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1511 gchar **subject, gchar **body, gchar **attach)
1515 const gchar *forbidden_uris[] = { ".gnupg/",
1520 Xstrdup_a(tmp_mailto, mailto, return -1);
1522 if (!strncmp(tmp_mailto, "mailto:", 7))
1525 p = strchr(tmp_mailto, '?');
1532 *to = decode_uri_gdup(tmp_mailto);
1535 gchar *field, *value;
1552 if (*value == '\0') continue;
1554 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1555 *cc = decode_uri_gdup(value);
1556 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1557 *bcc = decode_uri_gdup(value);
1558 } else if (subject && !*subject &&
1559 !g_ascii_strcasecmp(field, "subject")) {
1560 *subject = decode_uri_gdup(value);
1561 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1562 *body = decode_uri_gdup(value);
1563 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1565 *attach = decode_uri_gdup(value);
1566 for (; forbidden_uris[i]; i++) {
1567 if (strstr(*attach, forbidden_uris[i])) {
1568 g_print("Refusing to attach '%s', potential private data leak\n",
1583 #include <windows.h>
1584 #ifndef CSIDL_APPDATA
1585 #define CSIDL_APPDATA 0x001a
1587 #ifndef CSIDL_LOCAL_APPDATA
1588 #define CSIDL_LOCAL_APPDATA 0x001c
1590 #ifndef CSIDL_FLAG_CREATE
1591 #define CSIDL_FLAG_CREATE 0x8000
1593 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1597 w32_strerror (int w32_errno)
1599 static char strerr[256];
1600 int ec = (int)GetLastError ();
1604 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1605 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1606 strerr, DIM (strerr)-1, NULL);
1610 static __inline__ void *
1611 dlopen (const char * name, int flag)
1613 void * hd = LoadLibrary (name);
1617 static __inline__ void *
1618 dlsym (void * hd, const char * sym)
1622 void * fnc = GetProcAddress (hd, sym);
1631 static __inline__ const char *
1634 return w32_strerror (0);
1638 static __inline__ int
1650 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1652 static int initialized;
1653 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1657 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1663 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1665 handle = dlopen (dllnames[i], RTLD_LAZY);
1668 func = dlsym (handle, "SHGetFolderPathA");
1679 return func (a,b,c,d,e);
1684 /* Returns a static string with the directroy from which the module
1685 has been loaded. Returns an empty string on error. */
1686 static char *w32_get_module_dir(void)
1688 static char *moddir;
1691 char name[MAX_PATH+10];
1694 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1697 p = strrchr (name, '\\');
1703 moddir = g_strdup (name);
1707 #endif /* G_OS_WIN32 */
1709 /* Return a static string with the locale dir. */
1710 const gchar *get_locale_dir(void)
1712 static gchar *loc_dir;
1716 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1717 "\\share\\locale", NULL);
1720 loc_dir = LOCALEDIR;
1726 const gchar *get_home_dir(void)
1729 static char home_dir[MAX_PATH] = "";
1731 if (home_dir[0] == '\0') {
1732 if (w32_shgetfolderpath
1733 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1734 NULL, 0, home_dir) < 0)
1735 strcpy (home_dir, "C:\\Sylpheed");
1739 static const gchar *homeenv = NULL;
1744 if (!homeenv && g_getenv("HOME") != NULL)
1745 homeenv = g_strdup(g_getenv("HOME"));
1747 homeenv = g_get_home_dir();
1753 static gchar *claws_rc_dir = NULL;
1754 static gboolean rc_dir_alt = FALSE;
1755 const gchar *get_rc_dir(void)
1759 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1762 return claws_rc_dir;
1765 void set_rc_dir(const gchar *dir)
1767 if (claws_rc_dir != NULL) {
1768 g_print("Error: rc_dir already set\n");
1771 if (g_path_is_absolute(dir))
1772 claws_rc_dir = g_strdup(dir);
1774 claws_rc_dir = g_strconcat(g_get_current_dir(),
1775 G_DIR_SEPARATOR_S, dir, NULL);
1777 debug_print("set rc_dir to %s\n", claws_rc_dir);
1778 if (!is_dir_exist(claws_rc_dir)) {
1779 if (make_dir_hier(claws_rc_dir) != 0) {
1780 g_print("Error: can't create %s\n",
1787 gboolean rc_dir_is_alt(void) {
1791 const gchar *get_mail_base_dir(void)
1794 static gchar *mail_base_dir = NULL;
1797 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1800 return mail_base_dir;
1802 return get_home_dir();
1807 const gchar *prefs_common_get_data_root(void);
1808 gchar *last_data_root = NULL;
1811 const gchar *get_news_cache_dir(void)
1813 static gchar *news_cache_dir = NULL;
1815 const gchar *data_root = prefs_common_get_data_root();
1816 if (strcmp2(data_root, last_data_root)) {
1817 g_free(news_cache_dir);
1818 news_cache_dir = NULL;
1821 if (!news_cache_dir)
1823 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1824 NEWS_CACHE_DIR, NULL);
1828 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1829 "Claws", G_DIR_SEPARATOR_S,
1830 g_get_user_name(), G_DIR_SEPARATOR_S,
1831 NEWS_CACHE_DIR, NULL);
1832 g_free(last_data_root);
1833 last_data_root = g_strdup(last_data_root);
1835 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1836 NEWS_CACHE_DIR, NULL);
1837 g_free(last_data_root);
1838 last_data_root = NULL;
1842 return news_cache_dir;
1845 const gchar *get_imap_cache_dir(void)
1847 static gchar *imap_cache_dir = NULL;
1849 const gchar *data_root = prefs_common_get_data_root();
1850 if (strcmp2(data_root, last_data_root)) {
1851 g_free(imap_cache_dir);
1852 imap_cache_dir = NULL;
1856 if (!imap_cache_dir)
1858 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1859 IMAP_CACHE_DIR, NULL);
1863 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1864 "Claws", G_DIR_SEPARATOR_S,
1865 g_get_user_name(), G_DIR_SEPARATOR_S,
1866 IMAP_CACHE_DIR, NULL);
1867 g_free(last_data_root);
1868 last_data_root = g_strdup(last_data_root);
1870 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1871 IMAP_CACHE_DIR, NULL);
1872 g_free(last_data_root);
1873 last_data_root = NULL;
1878 return imap_cache_dir;
1881 const gchar *get_mime_tmp_dir(void)
1883 static gchar *mime_tmp_dir = NULL;
1886 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1887 MIME_TMP_DIR, NULL);
1889 return mime_tmp_dir;
1892 const gchar *get_template_dir(void)
1894 static gchar *template_dir = NULL;
1897 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1898 TEMPLATE_DIR, NULL);
1900 return template_dir;
1903 /* Return the default directory for Plugins. */
1904 const gchar *get_plugin_dir(void)
1907 static gchar *plugin_dir = NULL;
1910 plugin_dir = g_strconcat(w32_get_module_dir(),
1911 "\\lib\\claws-mail\\plugins\\",
1915 if (is_dir_exist(PLUGINDIR))
1918 static gchar *plugin_dir = NULL;
1920 plugin_dir = g_strconcat(get_rc_dir(),
1921 G_DIR_SEPARATOR_S, "plugins",
1922 G_DIR_SEPARATOR_S, NULL);
1928 const gchar *get_tmp_dir(void)
1930 static gchar *tmp_dir = NULL;
1933 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1939 gchar *get_tmp_file(void)
1942 static guint32 id = 0;
1944 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1945 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1950 const gchar *get_domain_name(void)
1953 static gchar *domain_name = NULL;
1959 if (gethostname(hostname, sizeof(hostname)) != 0) {
1960 perror("gethostname");
1961 domain_name = "unknown";
1963 hostname[sizeof(hostname) - 1] = '\0';
1964 if ((hp = my_gethostbyname(hostname)) == NULL) {
1965 perror("gethostbyname");
1966 domain_name = g_strdup(hostname);
1968 domain_name = g_strdup(hp->h_name);
1971 debug_print("domain name = %s\n", domain_name);
1980 off_t get_file_size(const gchar *file)
1984 if (g_stat(file, &s) < 0) {
1985 FILE_OP_ERROR(file, "stat");
1992 time_t get_file_mtime(const gchar *file)
1996 if (g_stat(file, &s) < 0) {
1997 FILE_OP_ERROR(file, "stat");
2004 off_t get_file_size_as_crlf(const gchar *file)
2008 gchar buf[BUFFSIZE];
2010 if ((fp = g_fopen(file, "rb")) == NULL) {
2011 FILE_OP_ERROR(file, "fopen");
2015 while (fgets(buf, sizeof(buf), fp) != NULL) {
2017 size += strlen(buf) + 2;
2021 FILE_OP_ERROR(file, "fgets");
2030 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2037 if (g_stat(file, &s) < 0) {
2038 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2042 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2049 /* Test on whether FILE is a relative file name. This is
2050 * straightforward for Unix but more complex for Windows. */
2051 gboolean is_relative_filename(const gchar *file)
2056 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2057 return FALSE; /* Prefixed with a hostname - this can't
2058 * be a relative name. */
2060 if ( ((*file >= 'a' && *file <= 'z')
2061 || (*file >= 'A' && *file <= 'Z'))
2063 file += 2; /* Skip drive letter. */
2065 return !(*file == '\\' || *file == '/');
2067 return !(*file == G_DIR_SEPARATOR);
2072 gboolean is_dir_exist(const gchar *dir)
2077 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2080 gboolean is_file_entry_exist(const gchar *file)
2085 return g_file_test(file, G_FILE_TEST_EXISTS);
2088 gboolean dirent_is_regular_file(struct dirent *d)
2090 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2091 if (d->d_type == DT_REG)
2093 else if (d->d_type != DT_UNKNOWN)
2097 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2100 gint change_dir(const gchar *dir)
2102 gchar *prevdir = NULL;
2105 prevdir = g_get_current_dir();
2107 if (g_chdir(dir) < 0) {
2108 FILE_OP_ERROR(dir, "chdir");
2109 if (debug_mode) g_free(prevdir);
2111 } else if (debug_mode) {
2114 cwd = g_get_current_dir();
2115 if (strcmp(prevdir, cwd) != 0)
2116 g_print("current dir: %s\n", cwd);
2124 gint make_dir(const gchar *dir)
2126 if (g_mkdir(dir, S_IRWXU) < 0) {
2127 FILE_OP_ERROR(dir, "mkdir");
2130 if (g_chmod(dir, S_IRWXU) < 0)
2131 FILE_OP_ERROR(dir, "chmod");
2136 gint make_dir_hier(const gchar *dir)
2141 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2142 parent_dir = g_strndup(dir, p - dir);
2143 if (*parent_dir != '\0') {
2144 if (!is_dir_exist(parent_dir)) {
2145 if (make_dir(parent_dir) < 0) {
2154 if (!is_dir_exist(dir)) {
2155 if (make_dir(dir) < 0)
2162 gint remove_all_files(const gchar *dir)
2165 const gchar *dir_name;
2168 prev_dir = g_get_current_dir();
2170 if (g_chdir(dir) < 0) {
2171 FILE_OP_ERROR(dir, "chdir");
2176 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2177 g_warning("failed to open directory: %s\n", dir);
2182 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2183 if (g_unlink(dir_name) < 0)
2184 FILE_OP_ERROR(dir_name, "unlink");
2189 if (g_chdir(prev_dir) < 0) {
2190 FILE_OP_ERROR(prev_dir, "chdir");
2200 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2203 const gchar *dir_name;
2207 prev_dir = g_get_current_dir();
2209 if (g_chdir(dir) < 0) {
2210 FILE_OP_ERROR(dir, "chdir");
2215 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2216 g_warning("failed to open directory: %s\n", dir);
2221 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2222 file_no = to_number(dir_name);
2223 if (file_no > 0 && first <= file_no && file_no <= last) {
2224 if (is_dir_exist(dir_name))
2226 if (g_unlink(dir_name) < 0)
2227 FILE_OP_ERROR(dir_name, "unlink");
2233 if (g_chdir(prev_dir) < 0) {
2234 FILE_OP_ERROR(prev_dir, "chdir");
2244 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2247 const gchar *dir_name;
2251 prev_dir = g_get_current_dir();
2253 if (g_chdir(dir) < 0) {
2254 FILE_OP_ERROR(dir, "chdir");
2259 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2260 FILE_OP_ERROR(dir, "opendir");
2265 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2266 file_no = to_number(dir_name);
2267 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2268 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2269 if (is_dir_exist(dir_name))
2271 if (g_unlink(dir_name) < 0)
2272 FILE_OP_ERROR(dir_name, "unlink");
2278 if (g_chdir(prev_dir) < 0) {
2279 FILE_OP_ERROR(prev_dir, "chdir");
2289 gint remove_all_numbered_files(const gchar *dir)
2291 return remove_numbered_files(dir, 0, UINT_MAX);
2294 gint remove_dir_recursive(const gchar *dir)
2298 const gchar *dir_name;
2301 if (g_stat(dir, &s) < 0) {
2302 FILE_OP_ERROR(dir, "stat");
2303 if (ENOENT == errno) return 0;
2307 if (!S_ISDIR(s.st_mode)) {
2308 if (g_unlink(dir) < 0) {
2309 FILE_OP_ERROR(dir, "unlink");
2316 prev_dir = g_get_current_dir();
2317 /* g_print("prev_dir = %s\n", prev_dir); */
2319 if (!path_cmp(prev_dir, dir)) {
2321 if (g_chdir("..") < 0) {
2322 FILE_OP_ERROR(dir, "chdir");
2325 prev_dir = g_get_current_dir();
2328 if (g_chdir(dir) < 0) {
2329 FILE_OP_ERROR(dir, "chdir");
2334 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2335 g_warning("failed to open directory: %s\n", dir);
2341 /* remove all files in the directory */
2342 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2343 /* g_print("removing %s\n", dir_name); */
2345 if (is_dir_exist(dir_name)) {
2346 if (remove_dir_recursive(dir_name) < 0) {
2347 g_warning("can't remove directory\n");
2351 if (g_unlink(dir_name) < 0)
2352 FILE_OP_ERROR(dir_name, "unlink");
2358 if (g_chdir(prev_dir) < 0) {
2359 FILE_OP_ERROR(prev_dir, "chdir");
2366 if (g_rmdir(dir) < 0) {
2367 FILE_OP_ERROR(dir, "rmdir");
2374 gint rename_force(const gchar *oldpath, const gchar *newpath)
2377 if (!is_file_entry_exist(oldpath)) {
2381 if (is_file_exist(newpath)) {
2382 if (g_unlink(newpath) < 0)
2383 FILE_OP_ERROR(newpath, "unlink");
2386 return g_rename(oldpath, newpath);
2390 * Append src file body to the tail of dest file.
2391 * Now keep_backup has no effects.
2393 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2395 FILE *src_fp, *dest_fp;
2399 gboolean err = FALSE;
2401 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2402 FILE_OP_ERROR(src, "fopen");
2406 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2407 FILE_OP_ERROR(dest, "fopen");
2412 if (change_file_mode_rw(dest_fp, dest) < 0) {
2413 FILE_OP_ERROR(dest, "chmod");
2414 g_warning("can't change file mode\n");
2417 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2418 if (n_read < sizeof(buf) && ferror(src_fp))
2420 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2421 g_warning("writing to %s failed.\n", dest);
2429 if (ferror(src_fp)) {
2430 FILE_OP_ERROR(src, "fread");
2434 if (fclose(dest_fp) == EOF) {
2435 FILE_OP_ERROR(dest, "fclose");
2447 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2449 FILE *src_fp, *dest_fp;
2452 gchar *dest_bak = NULL;
2453 gboolean err = FALSE;
2455 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2456 FILE_OP_ERROR(src, "fopen");
2459 if (is_file_exist(dest)) {
2460 dest_bak = g_strconcat(dest, ".bak", NULL);
2461 if (rename_force(dest, dest_bak) < 0) {
2462 FILE_OP_ERROR(dest, "rename");
2469 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2470 FILE_OP_ERROR(dest, "fopen");
2473 if (rename_force(dest_bak, dest) < 0)
2474 FILE_OP_ERROR(dest_bak, "rename");
2480 if (change_file_mode_rw(dest_fp, dest) < 0) {
2481 FILE_OP_ERROR(dest, "chmod");
2482 g_warning("can't change file mode\n");
2485 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2486 if (n_read < sizeof(buf) && ferror(src_fp))
2488 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2489 g_warning("writing to %s failed.\n", dest);
2494 if (rename_force(dest_bak, dest) < 0)
2495 FILE_OP_ERROR(dest_bak, "rename");
2502 if (ferror(src_fp)) {
2503 FILE_OP_ERROR(src, "fread");
2507 if (fclose(dest_fp) == EOF) {
2508 FILE_OP_ERROR(dest, "fclose");
2515 if (rename_force(dest_bak, dest) < 0)
2516 FILE_OP_ERROR(dest_bak, "rename");
2522 if (keep_backup == FALSE && dest_bak)
2530 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2532 if (overwrite == FALSE && is_file_exist(dest)) {
2533 g_warning("move_file(): file %s already exists.", dest);
2537 if (rename_force(src, dest) == 0) return 0;
2539 if (EXDEV != errno) {
2540 FILE_OP_ERROR(src, "rename");
2544 if (copy_file(src, dest, FALSE) < 0) return -1;
2551 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2554 gint bytes_left, to_read;
2557 if (fseek(fp, offset, SEEK_SET) < 0) {
2562 bytes_left = length;
2563 to_read = MIN(bytes_left, sizeof(buf));
2565 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2566 if (n_read < to_read && ferror(fp))
2568 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2571 bytes_left -= n_read;
2572 if (bytes_left == 0)
2574 to_read = MIN(bytes_left, sizeof(buf));
2585 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2588 gboolean err = FALSE;
2590 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2591 FILE_OP_ERROR(dest, "fopen");
2595 if (change_file_mode_rw(dest_fp, dest) < 0) {
2596 FILE_OP_ERROR(dest, "chmod");
2597 g_warning("can't change file mode\n");
2600 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2603 if (!err && fclose(dest_fp) == EOF) {
2604 FILE_OP_ERROR(dest, "fclose");
2609 g_warning("writing to %s failed.\n", dest);
2617 /* convert line endings into CRLF. If the last line doesn't end with
2618 * linebreak, add it.
2620 gchar *canonicalize_str(const gchar *str)
2626 for (p = str; *p != '\0'; ++p) {
2633 if (p == str || *(p - 1) != '\n')
2636 out = outp = g_malloc(new_len + 1);
2637 for (p = str; *p != '\0'; ++p) {
2644 if (p == str || *(p - 1) != '\n') {
2653 gint canonicalize_file(const gchar *src, const gchar *dest)
2655 FILE *src_fp, *dest_fp;
2656 gchar buf[BUFFSIZE];
2658 gboolean err = FALSE;
2659 gboolean last_linebreak = FALSE;
2661 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2662 FILE_OP_ERROR(src, "fopen");
2666 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2667 FILE_OP_ERROR(dest, "fopen");
2672 if (change_file_mode_rw(dest_fp, dest) < 0) {
2673 FILE_OP_ERROR(dest, "chmod");
2674 g_warning("can't change file mode\n");
2677 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2681 if (len == 0) break;
2682 last_linebreak = FALSE;
2684 if (buf[len - 1] != '\n') {
2685 last_linebreak = TRUE;
2686 r = fputs(buf, dest_fp);
2687 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2688 r = fputs(buf, dest_fp);
2691 r = fwrite(buf, 1, len - 1, dest_fp);
2696 r = fputs("\r\n", dest_fp);
2700 g_warning("writing to %s failed.\n", dest);
2708 if (last_linebreak == TRUE) {
2709 if (fputs("\r\n", dest_fp) == EOF)
2713 if (ferror(src_fp)) {
2714 FILE_OP_ERROR(src, "fgets");
2718 if (fclose(dest_fp) == EOF) {
2719 FILE_OP_ERROR(dest, "fclose");
2731 gint canonicalize_file_replace(const gchar *file)
2735 tmp_file = get_tmp_file();
2737 if (canonicalize_file(file, tmp_file) < 0) {
2742 if (move_file(tmp_file, file, TRUE) < 0) {
2743 g_warning("can't replace %s .\n", file);
2753 gchar *normalize_newlines(const gchar *str)
2755 const gchar *p = str;
2758 out = outp = g_malloc(strlen(str) + 1);
2759 for (p = str; *p != '\0'; ++p) {
2761 if (*(p + 1) != '\n')
2772 gchar *get_outgoing_rfc2822_str(FILE *fp)
2774 gchar buf[BUFFSIZE];
2778 str = g_string_new(NULL);
2780 /* output header part */
2781 while (fgets(buf, sizeof(buf), fp) != NULL) {
2783 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2790 else if (next != ' ' && next != '\t') {
2794 if (fgets(buf, sizeof(buf), fp) == NULL)
2798 g_string_append(str, buf);
2799 g_string_append(str, "\r\n");
2805 /* output body part */
2806 while (fgets(buf, sizeof(buf), fp) != NULL) {
2809 g_string_append_c(str, '.');
2810 g_string_append(str, buf);
2811 g_string_append(str, "\r\n");
2815 g_string_free(str, FALSE);
2821 * Create a new boundary in a way that it is very unlikely that this
2822 * will occur in the following text. It would be easy to ensure
2823 * uniqueness if everything is either quoted-printable or base64
2824 * encoded (note that conversion is allowed), but because MIME bodies
2825 * may be nested, it may happen that the same boundary has already
2828 * boundary := 0*69<bchars> bcharsnospace
2829 * bchars := bcharsnospace / " "
2830 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2831 * "+" / "_" / "," / "-" / "." /
2832 * "/" / ":" / "=" / "?"
2834 * some special characters removed because of buggy MTAs
2837 gchar *generate_mime_boundary(const gchar *prefix)
2839 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2840 "abcdefghijklmnopqrstuvwxyz"
2845 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2846 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2849 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2853 gint change_file_mode_rw(FILE *fp, const gchar *file)
2856 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2858 return g_chmod(file, S_IRUSR|S_IWUSR);
2862 FILE *my_tmpfile(void)
2864 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2865 const gchar suffix[] = ".XXXXXX";
2866 const gchar *tmpdir;
2868 const gchar *progname;
2875 tmpdir = get_tmp_dir();
2876 tmplen = strlen(tmpdir);
2877 progname = g_get_prgname();
2878 if (progname == NULL)
2879 progname = "claws-mail";
2880 proglen = strlen(progname);
2881 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2884 memcpy(fname, tmpdir, tmplen);
2885 fname[tmplen] = G_DIR_SEPARATOR;
2886 memcpy(fname + tmplen + 1, progname, proglen);
2887 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2889 fd = mkstemp(fname);
2896 /* verify that we can write in the file after unlinking */
2897 if (write(fd, buf, 1) < 0) {
2904 fp = fdopen(fd, "w+b");
2912 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2917 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2921 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2922 dir, G_DIR_SEPARATOR);
2923 fd = mkstemp_name(template, filename);
2926 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2927 fd = mkstemp(*filename);
2929 return fdopen(fd, "w+");
2932 FILE *str_open_as_stream(const gchar *str)
2937 g_return_val_if_fail(str != NULL, NULL);
2941 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2946 if (len == 0) return fp;
2948 if (fwrite(str, 1, len, fp) != len) {
2949 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2958 gint str_write_to_file(const gchar *str, const gchar *file)
2963 g_return_val_if_fail(str != NULL, -1);
2964 g_return_val_if_fail(file != NULL, -1);
2966 if ((fp = g_fopen(file, "wb")) == NULL) {
2967 FILE_OP_ERROR(file, "fopen");
2977 if (fwrite(str, 1, len, fp) != len) {
2978 FILE_OP_ERROR(file, "fwrite");
2984 if (fclose(fp) == EOF) {
2985 FILE_OP_ERROR(file, "fclose");
2993 gchar *file_read_to_str(const gchar *file)
2998 g_return_val_if_fail(file != NULL, NULL);
3000 if ((fp = g_fopen(file, "rb")) == NULL) {
3001 FILE_OP_ERROR(file, "fopen");
3005 str = file_read_stream_to_str(fp);
3012 gchar *file_read_stream_to_str(FILE *fp)
3019 g_return_val_if_fail(fp != NULL, NULL);
3021 array = g_byte_array_new();
3023 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3024 if (n_read < sizeof(buf) && ferror(fp))
3026 g_byte_array_append(array, buf, n_read);
3030 FILE_OP_ERROR("file stream", "fread");
3031 g_byte_array_free(array, TRUE);
3036 g_byte_array_append(array, buf, 1);
3037 str = (gchar *)array->data;
3038 g_byte_array_free(array, FALSE);
3040 if (!g_utf8_validate(str, -1, NULL)) {
3041 const gchar *src_codeset, *dest_codeset;
3043 src_codeset = conv_get_locale_charset_str();
3044 dest_codeset = CS_UTF_8;
3045 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3054 char *fgets_crlf(char *buf, int size, FILE *stream)
3056 gboolean is_cr = FALSE;
3057 gboolean last_was_cr = FALSE;
3062 while (--size > 0 && (c = getc(stream)) != EOF)
3065 is_cr = (c == '\r');
3075 last_was_cr = is_cr;
3077 if (c == EOF && cs == buf)
3085 static gint execute_async(gchar *const argv[])
3087 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3089 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3090 NULL, NULL, NULL, FALSE) == FALSE) {
3091 g_warning("Couldn't execute command: %s\n", argv[0]);
3098 static gint execute_sync(gchar *const argv[])
3102 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3104 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3105 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3106 g_warning("Couldn't execute command: %s\n", argv[0]);
3111 if (WIFEXITED(status))
3112 return WEXITSTATUS(status);
3120 gint execute_command_line(const gchar *cmdline, gboolean async)
3125 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3127 argv = strsplit_with_quote(cmdline, " ", 0);
3130 ret = execute_async(argv);
3132 ret = execute_sync(argv);
3139 gchar *get_command_output(const gchar *cmdline)
3141 gchar *child_stdout;
3144 g_return_val_if_fail(cmdline != NULL, NULL);
3146 debug_print("get_command_output(): executing: %s\n", cmdline);
3148 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3150 g_warning("Couldn't execute command: %s\n", cmdline);
3154 return child_stdout;
3157 static gint is_unchanged_uri_char(char c)
3168 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3174 for(i = 0; i < strlen(uri) ; i++) {
3175 if (is_unchanged_uri_char(uri[i])) {
3176 if (k + 2 >= bufsize)
3178 encoded_uri[k++] = uri[i];
3181 char * hexa = "0123456789ABCDEF";
3183 if (k + 4 >= bufsize)
3185 encoded_uri[k++] = '%';
3186 encoded_uri[k++] = hexa[uri[i] / 16];
3187 encoded_uri[k++] = hexa[uri[i] % 16];
3193 gint open_uri(const gchar *uri, const gchar *cmdline)
3196 gchar buf[BUFFSIZE];
3198 gchar encoded_uri[BUFFSIZE];
3199 g_return_val_if_fail(uri != NULL, -1);
3201 /* an option to choose whether to use encode_uri or not ? */
3202 encode_uri(encoded_uri, BUFFSIZE, uri);
3205 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3206 !strchr(p + 2, '%'))
3207 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3210 g_warning("Open URI command line is invalid "
3211 "(there must be only one '%%s'): %s",
3213 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3216 execute_command_line(buf, TRUE);
3218 extern osso_context_t *get_osso_context(void);
3219 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3220 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3221 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3226 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3228 gchar buf[BUFFSIZE];
3231 g_return_val_if_fail(filepath != NULL, -1);
3234 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3235 !strchr(p + 2, '%'))
3236 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3239 g_warning("Open Text Editor command line is invalid "
3240 "(there must be only one '%%s'): %s",
3242 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3245 execute_command_line(buf, TRUE);
3250 time_t remote_tzoffset_sec(const gchar *zone)
3252 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3258 time_t remoteoffset;
3260 strncpy(zone3, zone, 3);
3264 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3265 (c == '+' || c == '-')) {
3266 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3268 remoteoffset = -remoteoffset;
3269 } else if (!strncmp(zone, "UT" , 2) ||
3270 !strncmp(zone, "GMT", 2)) {
3272 } else if (strlen(zone3) == 3) {
3273 for (p = ustzstr; *p != '\0'; p += 3) {
3274 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3275 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3276 remoteoffset = iustz * 3600;
3282 } else if (strlen(zone3) == 1) {
3284 case 'Z': remoteoffset = 0; break;
3285 case 'A': remoteoffset = -1; break;
3286 case 'B': remoteoffset = -2; break;
3287 case 'C': remoteoffset = -3; break;
3288 case 'D': remoteoffset = -4; break;
3289 case 'E': remoteoffset = -5; break;
3290 case 'F': remoteoffset = -6; break;
3291 case 'G': remoteoffset = -7; break;
3292 case 'H': remoteoffset = -8; break;
3293 case 'I': remoteoffset = -9; break;
3294 case 'K': remoteoffset = -10; break; /* J is not used */
3295 case 'L': remoteoffset = -11; break;
3296 case 'M': remoteoffset = -12; break;
3297 case 'N': remoteoffset = 1; break;
3298 case 'O': remoteoffset = 2; break;
3299 case 'P': remoteoffset = 3; break;
3300 case 'Q': remoteoffset = 4; break;
3301 case 'R': remoteoffset = 5; break;
3302 case 'S': remoteoffset = 6; break;
3303 case 'T': remoteoffset = 7; break;
3304 case 'U': remoteoffset = 8; break;
3305 case 'V': remoteoffset = 9; break;
3306 case 'W': remoteoffset = 10; break;
3307 case 'X': remoteoffset = 11; break;
3308 case 'Y': remoteoffset = 12; break;
3309 default: remoteoffset = 0; break;
3311 remoteoffset = remoteoffset * 3600;
3315 return remoteoffset;
3318 time_t tzoffset_sec(time_t *now)
3322 struct tm buf1, buf2;
3324 gmt = *gmtime_r(now, &buf1);
3325 lt = localtime_r(now, &buf2);
3327 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3329 if (lt->tm_year < gmt.tm_year)
3331 else if (lt->tm_year > gmt.tm_year)
3333 else if (lt->tm_yday < gmt.tm_yday)
3335 else if (lt->tm_yday > gmt.tm_yday)
3338 if (off >= 24 * 60) /* should be impossible */
3339 off = 23 * 60 + 59; /* if not, insert silly value */
3340 if (off <= -24 * 60)
3341 off = -(23 * 60 + 59);
3346 /* calculate timezone offset */
3347 gchar *tzoffset(time_t *now)
3349 static gchar offset_string[6];
3353 struct tm buf1, buf2;
3355 gmt = *gmtime_r(now, &buf1);
3356 lt = localtime_r(now, &buf2);
3358 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3360 if (lt->tm_year < gmt.tm_year)
3362 else if (lt->tm_year > gmt.tm_year)
3364 else if (lt->tm_yday < gmt.tm_yday)
3366 else if (lt->tm_yday > gmt.tm_yday)
3374 if (off >= 24 * 60) /* should be impossible */
3375 off = 23 * 60 + 59; /* if not, insert silly value */
3377 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3379 return offset_string;
3382 void get_rfc822_date(gchar *buf, gint len)
3386 gchar day[4], mon[4];
3387 gint dd, hh, mm, ss, yyyy;
3389 gchar buf2[BUFFSIZE];
3392 lt = localtime_r(&t, &buf1);
3394 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3395 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3397 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3398 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3401 void debug_set_mode(gboolean mode)
3406 gboolean debug_get_mode(void)
3411 void debug_print_real(const gchar *format, ...)
3414 gchar buf[BUFFSIZE];
3416 if (!debug_mode) return;
3418 va_start(args, format);
3419 g_vsnprintf(buf, sizeof(buf), format, args);
3426 const char * debug_srcname(const char *file)
3428 const char *s = strrchr (file, '/');
3433 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3435 if (subject == NULL)
3438 subject += subject_get_prefix_length(subject);
3440 return g_hash_table_lookup(subject_table, subject);
3443 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3446 if (subject == NULL || *subject == 0)
3448 subject += subject_get_prefix_length(subject);
3449 g_hash_table_insert(subject_table, subject, data);
3452 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3454 if (subject == NULL)
3457 subject += subject_get_prefix_length(subject);
3458 g_hash_table_remove(subject_table, subject);
3462 *\brief Check if a string is prefixed with known (combinations)
3463 * of prefixes. The function assumes that each prefix
3464 * is terminated by zero or exactly _one_ space.
3466 *\param str String to check for a prefixes
3468 *\return int Number of chars in the prefix that should be skipped
3469 * for a "clean" subject line. If no prefix was found, 0
3472 int subject_get_prefix_length(const gchar *subject)
3474 /*!< Array with allowable reply prefixes regexps. */
3475 static const gchar * const prefixes[] = {
3476 "Re\\:", /* "Re:" */
3477 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3478 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3479 "Aw\\:", /* "Aw:" (German) */
3480 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3481 "Res\\:", /* "Res:" (Brazilian Outlook) */
3482 "Fw\\:", /* "Fw:" Forward */
3483 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3484 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3485 "Rif\\:", /* "Rif:" (Italian Outlook) */
3486 "Sv\\:", /* "Sv" (Norwegian) */
3487 "Vs\\:", /* "Vs" (Norwegian) */
3488 "Ad\\:", /* "Ad" (Norwegian) */
3489 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3492 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3495 static regex_t regex;
3496 static gboolean init_;
3498 if (!subject) return 0;
3499 if (!*subject) return 0;
3502 GString *s = g_string_new("");
3504 for (n = 0; n < PREFIXES; n++)
3505 /* Terminate each prefix regexpression by a
3506 * "\ ?" (zero or ONE space), and OR them */
3507 g_string_append_printf(s, "(%s\\ ?)%s",
3512 g_string_prepend(s, "(");
3513 g_string_append(s, ")+"); /* match at least once */
3514 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3517 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3518 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3519 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3520 debug_print("Error compiling regexp %s\n", s->str);
3521 g_string_free(s, TRUE);
3525 g_string_free(s, TRUE);
3529 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3535 static guint g_stricase_hash(gconstpointer gptr)
3537 guint hash_result = 0;
3540 for (str = gptr; str && *str; str++) {
3541 hash_result += toupper(*str);
3547 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3549 const char *str1 = gptr1;
3550 const char *str2 = gptr2;
3552 return !strcasecmp(str1, str2);
3555 gint g_int_compare(gconstpointer a, gconstpointer b)
3557 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3560 gchar *generate_msgid(gchar *buf, gint len)
3568 lt = localtime_r(&t, &buft);
3570 if (strcmp(buf, "") == 0) {
3571 addr = g_strconcat("@", get_domain_name(), NULL);
3574 addr = g_strconcat("@", buf, NULL);
3577 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3578 lt->tm_year + 1900, lt->tm_mon + 1,
3579 lt->tm_mday, lt->tm_hour,
3580 lt->tm_min, lt->tm_sec,
3581 (guint) rand(), addr);
3588 quote_cmd_argument()
3590 return a quoted string safely usable in argument of a command.
3592 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3595 gint quote_cmd_argument(gchar * result, guint size,
3605 for(p = path ; * p != '\0' ; p ++) {
3607 if (isalnum((guchar)*p) || (* p == '/')) {
3608 if (remaining > 0) {
3614 result[size - 1] = '\0';
3619 if (remaining >= 2) {
3627 result[size - 1] = '\0';
3632 if (remaining > 0) {
3636 result[size - 1] = '\0';
3650 static void g_node_map_recursive(GNode *node, gpointer data)
3652 GNodeMapData *mapdata = (GNodeMapData *) data;
3654 GNodeMapData newmapdata;
3657 newdata = mapdata->func(node->data, mapdata->data);
3658 if (newdata != NULL) {
3659 newnode = g_node_new(newdata);
3660 g_node_append(mapdata->parent, newnode);
3662 newmapdata.parent = newnode;
3663 newmapdata.func = mapdata->func;
3664 newmapdata.data = mapdata->data;
3666 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3670 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3673 GNodeMapData mapdata;
3675 g_return_val_if_fail(node != NULL, NULL);
3676 g_return_val_if_fail(func != NULL, NULL);
3678 root = g_node_new(func(node->data, data));
3680 mapdata.parent = root;
3681 mapdata.func = func;
3682 mapdata.data = data;
3684 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3689 #define HEX_TO_INT(val, hex) \
3693 if ('0' <= c && c <= '9') { \
3695 } else if ('a' <= c && c <= 'f') { \
3696 val = c - 'a' + 10; \
3697 } else if ('A' <= c && c <= 'F') { \
3698 val = c - 'A' + 10; \
3704 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3711 if (hi == -1 || lo == -1)
3714 *out = (hi << 4) + lo;
3718 #define INT_TO_HEX(hex, val) \
3721 hex = '0' + (val); \
3723 hex = 'A' + (val) - 10; \
3726 void get_hex_str(gchar *out, guchar ch)
3730 INT_TO_HEX(hex, ch >> 4);
3732 INT_TO_HEX(hex, ch & 0x0f);
3738 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3740 #define G_PRINT_REF g_print
3744 *\brief Register ref counted pointer. It is based on GBoxed, so should
3745 * work with anything that uses the GType system. The semantics
3746 * are similar to a C++ auto pointer, with the exception that
3747 * C doesn't have automatic closure (calling destructors) when
3748 * exiting a block scope.
3749 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3750 * function directly.
3752 *\return GType A GType type.
3754 GType g_auto_pointer_register(void)
3756 static GType auto_pointer_type;
3757 if (!auto_pointer_type)
3759 g_boxed_type_register_static
3760 ("G_TYPE_AUTO_POINTER",
3761 (GBoxedCopyFunc) g_auto_pointer_copy,
3762 (GBoxedFreeFunc) g_auto_pointer_free);
3763 return auto_pointer_type;
3767 *\brief Structure with g_new() allocated pointer guarded by the
3770 typedef struct AutoPointerRef {
3771 void (*free) (gpointer);
3777 *\brief The auto pointer opaque structure that references the
3778 * pointer guard block.
3780 typedef struct AutoPointer {
3781 AutoPointerRef *ref;
3782 gpointer ptr; /*!< access to protected pointer */
3786 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3790 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3791 * ... when assigning, copying and freeing storage elements
3793 * gtk_list_store_new(N_S_COLUMNS,
3794 * G_TYPE_AUTO_POINTER,
3798 * Template *precious_data = g_new0(Template, 1);
3799 * g_pointer protect = g_auto_pointer_new(precious_data);
3801 * gtk_list_store_set(container, &iter,
3805 * ... the gtk_list_store has copied the pointer and
3806 * ... incremented its reference count, we should free
3807 * ... the auto pointer (in C++ a destructor would do
3808 * ... this for us when leaving block scope)
3810 * g_auto_pointer_free(protect);
3812 * ... gtk_list_store_set() now manages the data. When
3813 * ... *explicitly* requesting a pointer from the list
3814 * ... store, don't forget you get a copy that should be
3815 * ... freed with g_auto_pointer_free() eventually.
3819 *\param pointer Pointer to be guarded.
3821 *\return GAuto * Pointer that should be used in containers with
3824 GAuto *g_auto_pointer_new(gpointer p)
3826 AutoPointerRef *ref;
3832 ref = g_new0(AutoPointerRef, 1);
3833 ptr = g_new0(AutoPointer, 1);
3843 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3849 *\brief Allocate an autopointer using the passed \a free function to
3850 * free the guarded pointer
3852 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3859 aptr = g_auto_pointer_new(p);
3860 aptr->ref->free = free_;
3864 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3866 if (auto_ptr == NULL)
3868 return ((AutoPointer *) auto_ptr)->ptr;
3872 *\brief Copies an auto pointer by. It's mostly not necessary
3873 * to call this function directly, unless you copy/assign
3874 * the guarded pointer.
3876 *\param auto_ptr Auto pointer returned by previous call to
3877 * g_auto_pointer_new_XXX()
3879 *\return gpointer An auto pointer
3881 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3884 AutoPointerRef *ref;
3887 if (auto_ptr == NULL)
3892 newp = g_new0(AutoPointer, 1);
3895 newp->ptr = ref->pointer;
3899 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3905 *\brief Free an auto pointer
3907 void g_auto_pointer_free(GAuto *auto_ptr)
3910 AutoPointerRef *ref;
3912 if (auto_ptr == NULL)
3918 if (--(ref->cnt) == 0) {
3920 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3922 ref->free(ref->pointer);
3927 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3932 void replace_returns(gchar *str)
3937 while (strstr(str, "\n")) {
3938 *strstr(str, "\n") = ' ';
3940 while (strstr(str, "\r")) {
3941 *strstr(str, "\r") = ' ';
3945 /* get_uri_part() - retrieves a URI starting from scanpos.
3946 Returns TRUE if succesful */
3947 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3948 const gchar **bp, const gchar **ep, gboolean hdr)
3951 gint parenthese_cnt = 0;
3953 g_return_val_if_fail(start != NULL, FALSE);
3954 g_return_val_if_fail(scanpos != NULL, FALSE);
3955 g_return_val_if_fail(bp != NULL, FALSE);
3956 g_return_val_if_fail(ep != NULL, FALSE);
3960 /* find end point of URI */
3961 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
3962 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
3963 !IS_ASCII(*(const guchar *)ep_) ||
3964 strchr("[]{}<>\"", *ep_)) {
3966 } else if (strchr("(", *ep_)) {
3968 } else if (strchr(")", *ep_)) {
3969 if (parenthese_cnt > 0)
3976 /* no punctuation at end of string */
3978 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
3979 * should pass some URI type to this function and decide on that whether
3980 * to perform punctuation stripping */
3982 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
3984 for (; ep_ - 1 > scanpos + 1 &&
3985 IS_REAL_PUNCT(*(ep_ - 1));
3989 #undef IS_REAL_PUNCT
3996 gchar *make_uri_string(const gchar *bp, const gchar *ep)
3998 while (bp && *bp && g_ascii_isspace(*bp))
4000 return g_strndup(bp, ep - bp);
4003 /* valid mail address characters */
4004 #define IS_RFC822_CHAR(ch) \
4008 !g_ascii_isspace(ch) && \
4009 !strchr("(),;<>\"", (ch)))
4011 /* alphabet and number within 7bit ASCII */
4012 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4013 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4015 static GHashTable *create_domain_tab(void)
4017 static const gchar *toplvl_domains [] = {
4019 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4020 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4021 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4022 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4023 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4024 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4025 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4026 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4027 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4028 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4029 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4030 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4031 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4032 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4033 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4034 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4035 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4036 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4037 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4038 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4039 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4040 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4041 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4042 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4043 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4044 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4047 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4049 g_return_val_if_fail(htab, NULL);
4050 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4051 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4055 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4057 const gint MAX_LVL_DOM_NAME_LEN = 6;
4058 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4059 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4062 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4065 for (p = buf; p < m && first < last; *p++ = *first++)
4069 return g_hash_table_lookup(tab, buf) != NULL;
4072 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4073 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4074 const gchar **bp, const gchar **ep, gboolean hdr)
4076 /* more complex than the uri part because we need to scan back and forward starting from
4077 * the scan position. */
4078 gboolean result = FALSE;
4079 const gchar *bp_ = NULL;
4080 const gchar *ep_ = NULL;
4081 static GHashTable *dom_tab;
4082 const gchar *last_dot = NULL;
4083 const gchar *prelast_dot = NULL;
4084 const gchar *last_tld_char = NULL;
4086 /* the informative part of the email address (describing the name
4087 * of the email address owner) may contain quoted parts. the
4088 * closure stack stores the last encountered quotes. */
4089 gchar closure_stack[128];
4090 gchar *ptr = closure_stack;
4092 g_return_val_if_fail(start != NULL, FALSE);
4093 g_return_val_if_fail(scanpos != NULL, FALSE);
4094 g_return_val_if_fail(bp != NULL, FALSE);
4095 g_return_val_if_fail(ep != NULL, FALSE);
4098 const gchar *start_quote = NULL;
4099 const gchar *end_quote = NULL;
4101 /* go to the real start */
4102 if (start[0] == ',')
4104 if (start[0] == ';')
4106 while (start[0] == '\n' || start[0] == '\r')
4108 while (start[0] == ' ' || start[0] == '\t')
4113 /* check if there are quotes (to skip , in them) */
4114 if (*start == '"') {
4115 start_quote = start;
4117 end_quote = strstr(start, "\"");
4123 /* skip anything between quotes */
4124 if (start_quote && end_quote) {
4129 /* find end (either , or ; or end of line) */
4130 if (strstr(start, ",") && strstr(start, ";"))
4131 *ep = strstr(start,",") < strstr(start, ";")
4132 ? strstr(start, ",") : strstr(start, ";");
4133 else if (strstr(start, ","))
4134 *ep = strstr(start, ",");
4135 else if (strstr(start, ";"))
4136 *ep = strstr(start, ";");
4138 *ep = start+strlen(start);
4140 /* go back to real start */
4141 if (start_quote && end_quote) {
4142 start = start_quote;
4145 /* check there's still an @ in that, or search
4146 * further if possible */
4147 if (strstr(start, "@") && strstr(start, "@") < *ep)
4149 else if (*ep < start+strlen(start)) {
4152 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4160 dom_tab = create_domain_tab();
4161 g_return_val_if_fail(dom_tab, FALSE);
4163 /* scan start of address */
4164 for (bp_ = scanpos - 1;
4165 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4168 /* TODO: should start with an alnum? */
4170 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4173 if (bp_ != scanpos) {
4174 /* scan end of address */
4175 for (ep_ = scanpos + 1;
4176 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4178 prelast_dot = last_dot;
4180 if (*(last_dot + 1) == '.') {
4181 if (prelast_dot == NULL)
4183 last_dot = prelast_dot;
4188 /* TODO: really should terminate with an alnum? */
4189 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4194 if (last_dot == NULL)
4196 if (last_dot >= ep_)
4197 last_dot = prelast_dot;
4198 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4202 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4203 if (*last_tld_char == '?')
4206 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4213 if (!result) return FALSE;
4215 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4216 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4217 && IS_RFC822_CHAR(*(ep_ + 3))) {
4218 /* this informative part with an @ in it is
4219 * followed by the email address */
4222 /* go to matching '>' (or next non-rfc822 char, like \n) */
4223 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4226 /* include the bracket */
4227 if (*ep_ == '>') ep_++;
4229 /* include the leading quote */
4237 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4238 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4241 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4242 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4245 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4246 #define IN_STACK() (ptr > closure_stack)
4247 /* has underrun check */
4248 #define POP_STACK() if(IN_STACK()) --ptr
4249 /* has overrun check */
4250 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4251 /* has underrun check */
4252 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4256 /* scan for the informative part. */
4257 for (bp_ -= 2; bp_ >= start; bp_--) {
4258 /* if closure on the stack keep scanning */
4259 if (PEEK_STACK() == *bp_) {
4263 if (*bp_ == '\'' || *bp_ == '"') {
4268 /* if nothing in the closure stack, do the special conditions
4269 * the following if..else expression simply checks whether
4270 * a token is acceptable. if not acceptable, the clause
4271 * should terminate the loop with a 'break' */
4272 if (!PEEK_STACK()) {
4274 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4275 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4276 /* hyphens are allowed, but only in
4278 } else if (strchr(" \"'", *bp_)) {
4279 /* but anything not being a punctiation
4282 break; /* anything else is rejected */
4289 /* scan forward (should start with an alnum) */
4290 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4306 #undef IS_ASCII_ALNUM
4307 #undef IS_RFC822_CHAR
4309 gchar *make_email_string(const gchar *bp, const gchar *ep)
4311 /* returns a mailto: URI; mailto: is also used to detect the
4312 * uri type later on in the button_pressed signal handler */
4316 tmp = g_strndup(bp, ep - bp);
4317 result = g_strconcat("mailto:", tmp, NULL);
4323 gchar *make_http_string(const gchar *bp, const gchar *ep)
4325 /* returns an http: URI; */
4329 while (bp && *bp && g_ascii_isspace(*bp))
4331 tmp = g_strndup(bp, ep - bp);
4332 result = g_strconcat("http://", tmp, NULL);
4338 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4340 FILE *fp = fopen(path, "rb");
4341 gchar buf[BUFFSIZE];
4342 gchar *result = NULL;
4345 while (fgets(buf, sizeof (buf), fp) != NULL) {
4346 gchar **parts = g_strsplit(buf, ";", 3);
4347 gchar *trimmed = parts[0];
4348 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4350 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4351 trimmed[strlen(trimmed)-1] = '\0';
4353 if (!strcmp(trimmed, type)) {
4354 gboolean needsterminal = FALSE;
4355 if (parts[2] && strstr(parts[2], "needsterminal")) {
4356 needsterminal = TRUE;
4358 if (parts[2] && strstr(parts[2], "test=")) {
4359 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4360 gchar *testcmd = orig_testcmd;
4361 if (strstr(testcmd,";"))
4362 *(strstr(testcmd,";")) = '\0';
4363 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4365 while (testcmd[strlen(testcmd)-1] == '\n')
4366 testcmd[strlen(testcmd)-1] = '\0';
4367 while (testcmd[strlen(testcmd)-1] == '\r')
4368 testcmd[strlen(testcmd)-1] = '\0';
4369 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4370 testcmd[strlen(testcmd)-1] = '\0';
4372 if (strstr(testcmd, "%s")) {
4373 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4374 gint res = system(tmp);
4376 g_free(orig_testcmd);
4383 gint res = system(testcmd);
4384 g_free(orig_testcmd);
4394 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4396 while (trimmed[strlen(trimmed)-1] == '\n')
4397 trimmed[strlen(trimmed)-1] = '\0';
4398 while (trimmed[strlen(trimmed)-1] == '\r')
4399 trimmed[strlen(trimmed)-1] = '\0';
4400 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4401 trimmed[strlen(trimmed)-1] = '\0';
4402 result = g_strdup(trimmed);
4405 /* if there are no single quotes around %s, add them.
4406 * '.*%s.*' is ok, as in display 'png:%s'
4408 if (strstr(result, "%s")
4409 && !(strstr(result, "'") < strstr(result,"%s") &&
4410 strstr(strstr(result,"%s"), "'"))) {
4411 gchar *start = g_strdup(result);
4412 gchar *end = g_strdup(strstr(result, "%s")+2);
4414 *strstr(start, "%s") = '\0';
4415 tmp = g_strconcat(start,"'%s'",end, NULL);
4421 if (needsterminal) {
4422 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4433 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4435 gchar *result = NULL;
4439 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4440 result = mailcap_get_command_in_file(path, type, file_to_open);
4444 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4448 void mailcap_update_default(const gchar *type, const gchar *command)
4450 gchar *path = NULL, *outpath = NULL;
4451 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4452 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4453 FILE *fp = fopen(path, "rb");
4454 FILE *outfp = fopen(outpath, "wb");
4455 gchar buf[BUFFSIZE];
4456 gboolean err = FALSE;
4464 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4465 gchar **parts = g_strsplit(buf, ";", 3);
4466 gchar *trimmed = parts[0];
4467 while (trimmed[0] == ' ')
4469 while (trimmed[strlen(trimmed)-1] == ' ')
4470 trimmed[strlen(trimmed)-1] = '\0';
4472 if (!strcmp(trimmed, type)) {
4477 if(fputs(buf, outfp) == EOF) {
4484 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4490 if (fclose(outfp) == EOF)
4494 g_rename(outpath, path);
4500 gint copy_dir(const gchar *src, const gchar *dst)
4505 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4506 g_warning("failed to open directory: %s\n", src);
4510 if (make_dir(dst) < 0)
4513 while ((name = g_dir_read_name(dir)) != NULL) {
4514 gchar *old_file, *new_file;
4515 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4516 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4517 debug_print("copying: %s -> %s\n", old_file, new_file);
4518 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4519 gint r = copy_file(old_file, new_file, TRUE);
4526 /* Windows has no symlinks. Or well, Vista seems to
4527 have something like this but the semantics might be
4528 different. Thus we don't use it under Windows. */
4529 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4532 gchar *target = g_file_read_link(old_file, &error);
4534 r = symlink(target, new_file);
4541 #endif /*G_OS_WIN32*/
4542 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4543 gint r = copy_dir(old_file, new_file);
4554 /* crude test to see if a file is an email. */
4555 gboolean file_is_email (const gchar *filename)
4561 if (filename == NULL)
4563 if ((fp = g_fopen(filename, "rb")) == NULL)
4565 while (i < 60 && score < 3
4566 && fgets(buffer, sizeof (buffer), fp) > 0) {
4567 if (!strncmp(buffer, "From:", strlen("From:")))
4569 if (!strncmp(buffer, "To:", strlen("To:")))
4571 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4576 return (score >= 3);
4579 gboolean sc_g_list_bigger(GList *list, gint max)
4583 while (cur && i <= max+1) {
4590 gboolean sc_g_slist_bigger(GSList *list, gint max)
4594 while (cur && i <= max+1) {
4601 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4602 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4603 NULL, NULL, NULL, NULL, NULL, NULL};
4604 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4605 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4606 NULL, NULL, NULL, NULL, NULL, NULL};
4607 const gchar *s_am_up = NULL;
4608 const gchar *s_pm_up = NULL;
4609 const gchar *s_am_low = NULL;
4610 const gchar *s_pm_low = NULL;
4611 const gchar *def_loc_format = NULL;
4612 const gchar *date_loc_format = NULL;
4613 const gchar *time_loc_format = NULL;
4614 const gchar *time_am_pm = NULL;
4616 static gboolean time_names_init_done = FALSE;
4618 static void init_time_names(void)
4620 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4621 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4622 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4623 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4624 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4625 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4626 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4628 monthnames[0] = Q_("Complete month name for use by strftime|January");
4629 monthnames[1] = Q_("Complete month name for use by strftime|February");
4630 monthnames[2] = Q_("Complete month name for use by strftime|March");
4631 monthnames[3] = Q_("Complete month name for use by strftime|April");
4632 monthnames[4] = Q_("Complete month name for use by strftime|May");
4633 monthnames[5] = Q_("Complete month name for use by strftime|June");
4634 monthnames[6] = Q_("Complete month name for use by strftime|July");
4635 monthnames[7] = Q_("Complete month name for use by strftime|August");
4636 monthnames[8] = Q_("Complete month name for use by strftime|September");
4637 monthnames[9] = Q_("Complete month name for use by strftime|October");
4638 monthnames[10] = Q_("Complete month name for use by strftime|November");
4639 monthnames[11] = Q_("Complete month name for use by strftime|December");
4641 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4642 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4643 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4644 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4645 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4646 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4647 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4649 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4650 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4651 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4652 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4653 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4654 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4655 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4656 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4657 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4658 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4659 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4660 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4662 s_am_up = Q_("For use by strftime (morning)|AM");
4663 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4664 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4665 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4667 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4668 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4669 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4671 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4673 time_names_init_done = TRUE;
4676 #define CHECK_SIZE() { \
4677 total_done += len; \
4678 if (total_done >= buflen) { \
4679 buf[buflen-1] = '\0'; \
4684 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4686 gchar *curpos = buf;
4687 gint total_done = 0;
4688 gchar subbuf[64], subfmt[64];
4689 static time_t last_tzset = (time_t)0;
4691 if (!time_names_init_done)
4694 if (format == NULL || lt == NULL)
4697 if (last_tzset != time(NULL)) {
4699 last_tzset = time(NULL);
4702 if (*format == '%') {
4703 gint len = 0, tmp = 0;
4707 len = 1; CHECK_SIZE();
4711 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4712 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4715 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4716 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4720 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4721 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4724 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4725 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4728 fast_strftime(subbuf, 64, def_loc_format, lt);
4729 len = strlen(subbuf); CHECK_SIZE();
4730 strncpy2(curpos, subbuf, buflen - total_done);
4733 total_done += 2; CHECK_SIZE();
4734 tmp = (lt->tm_year + 1900)/100;
4735 *curpos++ = '0'+(tmp / 10);
4736 *curpos++ = '0'+(tmp % 10);
4739 total_done += 2; CHECK_SIZE();
4740 *curpos++ = '0'+(lt->tm_mday / 10);
4741 *curpos++ = '0'+(lt->tm_mday % 10);
4744 total_done += 8; CHECK_SIZE();
4745 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4746 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4748 *curpos++ = '0'+(lt->tm_mday / 10);
4749 *curpos++ = '0'+(lt->tm_mday % 10);
4751 tmp = lt->tm_year%100;
4752 *curpos++ = '0'+(tmp / 10);
4753 *curpos++ = '0'+(tmp % 10);
4756 len = 2; CHECK_SIZE();
4757 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4760 len = 10; CHECK_SIZE();
4761 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4762 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4765 total_done += 2; CHECK_SIZE();
4766 *curpos++ = '0'+(lt->tm_hour / 10);
4767 *curpos++ = '0'+(lt->tm_hour % 10);
4770 total_done += 2; CHECK_SIZE();
4776 *curpos++ = '0'+(tmp / 10);
4777 *curpos++ = '0'+(tmp % 10);
4780 len = 3; CHECK_SIZE();
4781 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4784 len = 2; CHECK_SIZE();
4785 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4788 len = 2; CHECK_SIZE();
4794 snprintf(curpos, buflen - total_done, "%2d", tmp);
4797 total_done += 2; CHECK_SIZE();
4798 tmp = lt->tm_mon + 1;
4799 *curpos++ = '0'+(tmp / 10);
4800 *curpos++ = '0'+(tmp % 10);
4803 total_done += 2; CHECK_SIZE();
4804 *curpos++ = '0'+(lt->tm_min / 10);
4805 *curpos++ = '0'+(lt->tm_min % 10);
4808 len = 1; CHECK_SIZE();
4812 if (lt->tm_hour >= 12) {
4813 len = strlen(s_pm_up); CHECK_SIZE();
4814 snprintf(curpos, buflen-total_done, s_pm_up);
4816 len = strlen(s_am_up); CHECK_SIZE();
4817 snprintf(curpos, buflen-total_done, s_am_up);
4821 if (lt->tm_hour >= 12) {
4822 len = strlen(s_pm_low); CHECK_SIZE();
4823 snprintf(curpos, buflen-total_done, s_pm_low);
4825 len = strlen(s_am_low); CHECK_SIZE();
4826 snprintf(curpos, buflen-total_done, s_am_low);
4830 fast_strftime(subbuf, 64, time_am_pm, lt);
4831 len = strlen(subbuf); CHECK_SIZE();
4832 strncpy2(curpos, subbuf, buflen - total_done);
4835 total_done += 5; CHECK_SIZE();
4836 *curpos++ = '0'+(lt->tm_hour / 10);
4837 *curpos++ = '0'+(lt->tm_hour % 10);
4839 *curpos++ = '0'+(lt->tm_min / 10);
4840 *curpos++ = '0'+(lt->tm_min % 10);
4843 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4844 len = strlen(subbuf); CHECK_SIZE();
4845 strncpy2(curpos, subbuf, buflen - total_done);
4848 total_done += 2; CHECK_SIZE();
4849 *curpos++ = '0'+(lt->tm_sec / 10);
4850 *curpos++ = '0'+(lt->tm_sec % 10);
4853 len = 1; CHECK_SIZE();
4857 total_done += 8; CHECK_SIZE();
4858 *curpos++ = '0'+(lt->tm_hour / 10);
4859 *curpos++ = '0'+(lt->tm_hour % 10);
4861 *curpos++ = '0'+(lt->tm_min / 10);
4862 *curpos++ = '0'+(lt->tm_min % 10);
4864 *curpos++ = '0'+(lt->tm_sec / 10);
4865 *curpos++ = '0'+(lt->tm_sec % 10);
4868 len = 1; CHECK_SIZE();
4869 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4872 len = 1; CHECK_SIZE();
4873 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4876 fast_strftime(subbuf, 64, date_loc_format, lt);
4877 len = strlen(subbuf); CHECK_SIZE();
4878 strncpy2(curpos, subbuf, buflen - total_done);
4881 fast_strftime(subbuf, 64, time_loc_format, lt);
4882 len = strlen(subbuf); CHECK_SIZE();
4883 strncpy2(curpos, subbuf, buflen - total_done);
4886 total_done += 2; CHECK_SIZE();
4887 tmp = lt->tm_year%100;
4888 *curpos++ = '0'+(tmp / 10);
4889 *curpos++ = '0'+(tmp % 10);
4892 len = 4; CHECK_SIZE();
4893 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4903 /* let these complicated ones be done with the libc */
4904 snprintf(subfmt, 64, "%%%c", *format);
4905 strftime(subbuf, 64, subfmt, lt);
4906 len = strlen(subbuf); CHECK_SIZE();
4907 strncpy2(curpos, subbuf, buflen - total_done);
4911 /* let these complicated modifiers be done with the libc */
4912 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4913 strftime(subbuf, 64, subfmt, lt);
4914 len = strlen(subbuf); CHECK_SIZE();
4915 strncpy2(curpos, subbuf, buflen - total_done);
4919 g_warning("format error (%c)", *format);
4926 int len = 1; CHECK_SIZE();
4927 *curpos++ = *format++;