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 GSList *slist_concat_unique (GSList *first, GSList *second)
242 } else if (second == NULL)
245 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
246 if (g_slist_find(ret, tmp->data) == NULL)
247 ret = g_slist_prepend(ret, tmp->data);
252 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
257 void hash_free_strings(GHashTable *table)
259 g_hash_table_foreach(table, hash_free_strings_func, NULL);
262 static void hash_free_value_mem_func(gpointer key, gpointer value,
268 void hash_free_value_mem(GHashTable *table)
270 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
273 gint str_case_equal(gconstpointer v, gconstpointer v2)
275 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
278 guint str_case_hash(gconstpointer key)
280 const gchar *p = key;
284 h = g_ascii_tolower(h);
285 for (p += 1; *p != '\0'; p++)
286 h = (h << 5) - h + g_ascii_tolower(*p);
292 void ptr_array_free_strings(GPtrArray *array)
297 g_return_if_fail(array != NULL);
299 for (i = 0; i < array->len; i++) {
300 str = g_ptr_array_index(array, i);
305 gboolean str_find(const gchar *haystack, const gchar *needle)
307 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
310 gboolean str_case_find(const gchar *haystack, const gchar *needle)
312 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
315 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
317 return strcmp(haystack, needle) == 0;
320 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
322 return g_ascii_strcasecmp(haystack, needle) == 0;
325 gint to_number(const gchar *nstr)
327 register const gchar *p;
329 if (*nstr == '\0') return -1;
331 for (p = nstr; *p != '\0'; p++)
332 if (!g_ascii_isdigit(*p)) return -1;
337 /* convert integer into string,
338 nstr must be not lower than 11 characters length */
339 gchar *itos_buf(gchar *nstr, gint n)
341 g_snprintf(nstr, 11, "%d", n);
345 /* convert integer into string */
348 static gchar nstr[11];
350 return itos_buf(nstr, n);
353 #define divide(num,divisor,i,d) \
355 i = num >> divisor; \
356 d = num & ((1<<divisor)-1); \
357 d = (d*100) >> divisor; \
360 gchar *to_human_readable(off_t size)
362 static gchar str[14];
363 static gchar *b_format = NULL, *kb_format = NULL,
364 *mb_format = NULL, *gb_format = NULL;
365 register int t = 0, r = 0;
366 if (b_format == NULL) {
368 kb_format = _("%d.%02dKB");
369 mb_format = _("%d.%02dMB");
370 gb_format = _("%.2fGB");
373 if (size < (off_t)1024) {
374 g_snprintf(str, sizeof(str), b_format, (gint)size);
376 } else if (size >> 10 < (off_t)1024) {
377 divide(size, 10, t, r);
378 g_snprintf(str, sizeof(str), kb_format, t, r);
380 } else if (size >> 20 < (off_t)1024) {
381 divide(size, 20, t, r);
382 g_snprintf(str, sizeof(str), mb_format, t, r);
385 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
390 /* strcmp with NULL-checking */
391 gint strcmp2(const gchar *s1, const gchar *s2)
393 if (s1 == NULL || s2 == NULL)
396 return strcmp(s1, s2);
398 /* strstr with NULL-checking */
399 gchar *strstr2(const gchar *s1, const gchar *s2)
401 if (s1 == NULL || s2 == NULL)
404 return strstr(s1, s2);
407 gint path_cmp(const gchar *s1, const gchar *s2)
412 gchar *s1buf, *s2buf;
415 if (s1 == NULL || s2 == NULL) return -1;
416 if (*s1 == '\0' || *s2 == '\0') return -1;
419 s1buf = g_strdup (s1);
420 s2buf = g_strdup (s2);
421 subst_char (s1buf, '/', G_DIR_SEPARATOR);
422 subst_char (s2buf, '/', G_DIR_SEPARATOR);
425 #endif /* !G_OS_WIN32 */
430 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
431 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
433 rc = strncmp(s1, s2, MAX(len1, len2));
437 #endif /* !G_OS_WIN32 */
441 /* remove trailing return code */
442 gchar *strretchomp(gchar *str)
446 if (!*str) return str;
448 for (s = str + strlen(str) - 1;
449 s >= str && (*s == '\n' || *s == '\r');
456 /* remove trailing character */
457 gchar *strtailchomp(gchar *str, gchar tail_char)
461 if (!*str) return str;
462 if (tail_char == '\0') return str;
464 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
470 /* remove CR (carriage return) */
471 gchar *strcrchomp(gchar *str)
475 if (!*str) return str;
477 s = str + strlen(str) - 1;
478 if (*s == '\n' && s > str && *(s - 1) == '\r') {
486 gint file_strip_crs(const gchar *file)
488 FILE *fp = NULL, *outfp = NULL;
490 gchar *out = get_tmp_file();
494 fp = fopen(file, "rb");
498 outfp = fopen(out, "wb");
504 while (fgets(buf, sizeof (buf), fp) != NULL) {
506 if (fputs(buf, outfp) == EOF) {
514 if (fclose(outfp) == EOF) {
518 if (rename_force(out, file) < 0)
529 /* Similar to `strstr' but this function ignores the case of both strings. */
530 gchar *strcasestr(const gchar *haystack, const gchar *needle)
532 register size_t haystack_len, needle_len;
534 haystack_len = strlen(haystack);
535 needle_len = strlen(needle);
537 if (haystack_len < needle_len || needle_len == 0)
540 while (haystack_len >= needle_len) {
541 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
542 return (gchar *)haystack;
552 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
553 gconstpointer needle, size_t needlelen)
555 const gchar *haystack_ = (const gchar *)haystack;
556 const gchar *needle_ = (const gchar *)needle;
557 const gchar *haystack_cur = (const gchar *)haystack;
560 return memchr(haystack_, *needle_, haystacklen);
562 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
564 if (haystacklen - (haystack_cur - haystack_) < needlelen)
566 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
567 return (gpointer)haystack_cur;
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 /* compare subjects */
616 gint subject_compare(const gchar *s1, const gchar *s2)
620 if (!s1 || !s2) return -1;
621 if (!*s1 || !*s2) return -1;
623 Xstrdup_a(str1, s1, return -1);
624 Xstrdup_a(str2, s2, return -1);
626 trim_subject_for_compare(str1);
627 trim_subject_for_compare(str2);
629 if (!*str1 || !*str2) return -1;
631 return strcmp(str1, str2);
634 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
638 if (!s1 || !s2) return -1;
640 Xstrdup_a(str1, s1, return -1);
641 Xstrdup_a(str2, s2, return -1);
643 trim_subject_for_sort(str1);
644 trim_subject_for_sort(str2);
646 return g_utf8_collate(str1, str2);
649 void trim_subject_for_compare(gchar *str)
653 eliminate_parenthesis(str, '[', ']');
654 eliminate_parenthesis(str, '(', ')');
657 srcp = str + subject_get_prefix_length(str);
659 memmove(str, srcp, strlen(srcp) + 1);
662 void trim_subject_for_sort(gchar *str)
668 srcp = str + subject_get_prefix_length(str);
670 memmove(str, srcp, strlen(srcp) + 1);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void eliminate_quote(gchar *str, gchar quote_chr)
793 register gchar *srcp, *destp;
797 while ((destp = strchr(destp, quote_chr))) {
798 if ((srcp = strchr(destp + 1, quote_chr))) {
800 while (g_ascii_isspace(*srcp)) srcp++;
801 memmove(destp, srcp, strlen(srcp) + 1);
809 void extract_quote(gchar *str, gchar quote_chr)
813 if ((str = strchr(str, quote_chr))) {
815 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
816 memmove(p - 1, p, strlen(p) + 1);
821 memmove(str, str + 1, p - str);
826 void eliminate_address_comment(gchar *str)
828 register gchar *srcp, *destp;
833 while ((destp = strchr(destp, '"'))) {
834 if ((srcp = strchr(destp + 1, '"'))) {
839 while (g_ascii_isspace(*srcp)) srcp++;
840 memmove(destp, srcp, strlen(srcp) + 1);
850 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
856 else if (*srcp == ')')
862 while (g_ascii_isspace(*srcp)) srcp++;
863 memmove(destp, srcp, strlen(srcp) + 1);
867 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
869 gboolean in_quote = FALSE;
872 if (*str == c && !in_quote)
874 if (*str == quote_chr)
882 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
884 gboolean in_quote = FALSE;
887 p = str + strlen(str) - 1;
889 if (*p == c && !in_quote)
899 void extract_address(gchar *str)
901 eliminate_address_comment(str);
902 if (strchr_with_skip_quote(str, '"', '<'))
903 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
907 void extract_list_id_str(gchar *str)
909 if (strchr_with_skip_quote(str, '"', '<'))
910 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
914 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
919 if (!str) return addr_list;
921 Xstrdup_a(work, str, return addr_list);
924 eliminate_address_comment(work);
927 while (workp && *workp) {
930 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
936 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
937 extract_parenthesis_with_skip_quote
938 (workp, '"', '<', '>');
942 addr_list = g_slist_append(addr_list, g_strdup(workp));
950 GSList *address_list_append(GSList *addr_list, const gchar *str)
952 return address_list_append_real(addr_list, str, TRUE);
955 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
957 return address_list_append_real(addr_list, str, FALSE);
960 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
964 if (!str) return msgid_list;
967 while (strp && *strp) {
968 const gchar *start, *end;
971 if ((start = strchr(strp, '<')) != NULL) {
972 end = strchr(start + 1, '>');
977 msgid = g_strndup(start + 1, end - start - 1);
980 msgid_list = g_slist_prepend(msgid_list, msgid);
990 GSList *references_list_append(GSList *msgid_list, const gchar *str)
994 list = references_list_prepend(NULL, str);
995 list = g_slist_reverse(list);
996 msgid_list = g_slist_concat(msgid_list, list);
1001 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1006 if (!str) return group_list;
1008 Xstrdup_a(work, str, return group_list);
1012 while (workp && *workp) {
1015 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1023 group_list = g_slist_append(group_list,
1032 GList *add_history(GList *list, const gchar *str)
1036 g_return_val_if_fail(str != NULL, list);
1038 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1041 list = g_list_remove(list, old->data);
1042 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1045 last = g_list_last(list);
1048 list = g_list_remove(list, last->data);
1052 list = g_list_prepend(list, g_strdup(str));
1057 void remove_return(gchar *str)
1059 register gchar *p = str;
1062 if (*p == '\n' || *p == '\r')
1063 memmove(p, p + 1, strlen(p));
1069 void remove_space(gchar *str)
1071 register gchar *p = str;
1076 while (g_ascii_isspace(*(p + spc)))
1079 memmove(p, p + spc, strlen(p + spc) + 1);
1085 void unfold_line(gchar *str)
1087 register gchar *p = str;
1091 if (*p == '\n' || *p == '\r') {
1094 while (g_ascii_isspace(*(p + spc)))
1097 memmove(p, p + spc, strlen(p + spc) + 1);
1103 void subst_char(gchar *str, gchar orig, gchar subst)
1105 register gchar *p = str;
1114 void subst_chars(gchar *str, gchar *orig, gchar subst)
1116 register gchar *p = str;
1119 if (strchr(orig, *p) != NULL)
1125 void subst_for_filename(gchar *str)
1130 subst_chars(str, "\t\r\n\\/*:", '_');
1132 subst_chars(str, "\t\r\n\\/*", '_');
1136 void subst_for_shellsafe_filename(gchar *str)
1140 subst_for_filename(str);
1141 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1144 gboolean is_header_line(const gchar *str)
1146 if (str[0] == ':') return FALSE;
1148 while (*str != '\0' && *str != ' ') {
1157 gboolean is_ascii_str(const gchar *str)
1159 const guchar *p = (const guchar *)str;
1161 while (*p != '\0') {
1162 if (*p != '\t' && *p != ' ' &&
1163 *p != '\r' && *p != '\n' &&
1164 (*p < 32 || *p >= 127))
1172 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1174 const gchar *first_pos;
1175 const gchar *last_pos;
1176 const gchar *p = str;
1177 gint quote_level = -1;
1179 /* speed up line processing by only searching to the last '>' */
1180 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1181 /* skip a line if it contains a '<' before the initial '>' */
1182 if (memchr(str, '<', first_pos - str) != NULL)
1184 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1188 while (p <= last_pos) {
1189 while (p < last_pos) {
1190 if (g_ascii_isspace(*p))
1196 if (strchr(quote_chars, *p))
1198 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1199 /* any characters are allowed except '-' and space */
1201 && !strchr(quote_chars, *p)
1202 && !g_ascii_isspace(*p)
1205 if (strchr(quote_chars, *p))
1217 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1219 const gchar *p = str, *q;
1220 gint cur_line = 0, len;
1222 while ((q = strchr(p, '\n')) != NULL) {
1224 if (len > max_chars) {
1234 if (len > max_chars) {
1243 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1245 gchar * position = NULL;
1246 gchar * tmp_pos = NULL;
1249 if (quote_chars == NULL)
1252 for (i = 0; i < strlen(quote_chars); i++) {
1253 tmp_pos = strchr (str, quote_chars[i]);
1255 || (tmp_pos != NULL && position >= tmp_pos) )
1261 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1263 gchar * position = NULL;
1264 gchar * tmp_pos = NULL;
1267 if (quote_chars == NULL)
1270 for (i = 0; i < strlen(quote_chars); i++) {
1271 tmp_pos = strrchr (str, quote_chars[i]);
1273 || (tmp_pos != NULL && position <= tmp_pos) )
1279 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1281 register guint haystack_len, needle_len;
1282 gboolean in_squote = FALSE, in_dquote = FALSE;
1284 haystack_len = strlen(haystack);
1285 needle_len = strlen(needle);
1287 if (haystack_len < needle_len || needle_len == 0)
1290 while (haystack_len >= needle_len) {
1291 if (!in_squote && !in_dquote &&
1292 !strncmp(haystack, needle, needle_len))
1293 return (gchar *)haystack;
1295 /* 'foo"bar"' -> foo"bar"
1296 "foo'bar'" -> foo'bar' */
1297 if (*haystack == '\'') {
1300 else if (!in_dquote)
1302 } else if (*haystack == '\"') {
1305 else if (!in_squote)
1316 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1319 gchar quote_chr = '"';
1321 gboolean in_quote = FALSE;
1325 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1329 if (*p == op && !in_quote)
1331 else if (*p == cl && !in_quote)
1333 else if (*p == quote_chr)
1346 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1349 GSList *string_list = NULL, *slist;
1351 const gchar *s_op, *s_cl;
1354 g_return_val_if_fail(str != NULL, NULL);
1357 max_tokens = G_MAXINT;
1359 s_op = strchr_with_skip_quote(str, '"', op);
1360 if (!s_op) return NULL;
1362 s_cl = strchr_parenthesis_close(str, op, cl);
1370 new_string = g_new(gchar, len + 1);
1371 strncpy(new_string, str, len);
1372 new_string[len] = 0;
1373 string_list = g_slist_prepend(string_list, new_string);
1377 while (*str && g_ascii_isspace(*str)) str++;
1379 string_list = g_slist_prepend(string_list,
1382 s_op = strchr_with_skip_quote(str, '"', op);
1383 if (!--max_tokens || !s_op) break;
1387 s_cl = strchr_parenthesis_close(str, op, cl);
1388 } while (--max_tokens && s_cl);
1391 str_array = g_new(gchar*, n);
1395 str_array[i--] = NULL;
1396 for (slist = string_list; slist; slist = slist->next)
1397 str_array[i--] = slist->data;
1399 g_slist_free(string_list);
1404 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1407 GSList *string_list = NULL, *slist;
1408 gchar **str_array, *s, *new_str;
1409 guint i, n = 1, len;
1411 g_return_val_if_fail(str != NULL, NULL);
1412 g_return_val_if_fail(delim != NULL, NULL);
1415 max_tokens = G_MAXINT;
1417 s = strstr_with_skip_quote(str, delim);
1419 guint delimiter_len = strlen(delim);
1423 new_str = g_strndup(str, len);
1425 if (new_str[0] == '\'' || new_str[0] == '\"') {
1426 if (new_str[len - 1] == new_str[0]) {
1427 new_str[len - 1] = '\0';
1428 memmove(new_str, new_str + 1, len - 1);
1431 string_list = g_slist_prepend(string_list, new_str);
1433 str = s + delimiter_len;
1434 s = strstr_with_skip_quote(str, delim);
1435 } while (--max_tokens && s);
1439 new_str = g_strdup(str);
1440 if (new_str[0] == '\'' || new_str[0] == '\"') {
1442 if (new_str[len - 1] == new_str[0]) {
1443 new_str[len - 1] = '\0';
1444 memmove(new_str, new_str + 1, len - 1);
1447 string_list = g_slist_prepend(string_list, new_str);
1451 str_array = g_new(gchar*, n);
1455 str_array[i--] = NULL;
1456 for (slist = string_list; slist; slist = slist->next)
1457 str_array[i--] = slist->data;
1459 g_slist_free(string_list);
1464 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1466 gchar *abbrev_group;
1468 const gchar *p = group;
1471 g_return_val_if_fail(group != NULL, NULL);
1473 last = group + strlen(group);
1474 abbrev_group = ap = g_malloc(strlen(group) + 1);
1479 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1481 while (*p != '.') p++;
1484 return abbrev_group;
1489 return abbrev_group;
1492 gchar *trim_string(const gchar *str, gint len)
1494 const gchar *p = str;
1499 if (!str) return NULL;
1500 if (strlen(str) <= len)
1501 return g_strdup(str);
1502 if (g_utf8_validate(str, -1, NULL) == FALSE)
1503 return g_strdup(str);
1505 while (*p != '\0') {
1506 mb_len = g_utf8_skip[*(guchar *)p];
1509 else if (new_len + mb_len > len)
1516 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1517 return g_strconcat(new_str, "...", NULL);
1520 GList *uri_list_extract_filenames(const gchar *uri_list)
1522 GList *result = NULL;
1524 gchar *escaped_utf8uri;
1530 while (g_ascii_isspace(*p)) p++;
1531 if (!strncmp(p, "file:", 5)) {
1534 while (*q && *q != '\n' && *q != '\r') q++;
1537 gchar *file, *locale_file = NULL;
1539 while (q > p && g_ascii_isspace(*q))
1541 Xalloca(escaped_utf8uri, q - p + 2,
1543 Xalloca(file, q - p + 2,
1546 strncpy(escaped_utf8uri, p, q - p + 1);
1547 escaped_utf8uri[q - p + 1] = '\0';
1548 decode_uri(file, escaped_utf8uri);
1550 * g_filename_from_uri() rejects escaped/locale encoded uri
1551 * string which come from Nautilus.
1553 if (g_utf8_validate(file, -1, NULL))
1555 = conv_codeset_strdup(
1558 conv_get_locale_charset_str());
1560 locale_file = g_strdup(file + 5);
1561 result = g_list_append(result, locale_file);
1565 p = strchr(p, '\n');
1572 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1575 static gint axtoi(const gchar *hexstr)
1577 gint hi, lo, result;
1580 if ('0' <= hi && hi <= '9') {
1583 if ('a' <= hi && hi <= 'f') {
1586 if ('A' <= hi && hi <= 'F') {
1591 if ('0' <= lo && lo <= '9') {
1594 if ('a' <= lo && lo <= 'f') {
1597 if ('A' <= lo && lo <= 'F') {
1600 result = lo + (16 * hi);
1604 gboolean is_uri_string(const gchar *str)
1606 while (str && *str && g_ascii_isspace(*str))
1608 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1609 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1610 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1611 g_ascii_strncasecmp(str, "www.", 4) == 0);
1614 gchar *get_uri_path(const gchar *uri)
1616 while (uri && *uri && g_ascii_isspace(*uri))
1618 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1619 return (gchar *)(uri + 7);
1620 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1621 return (gchar *)(uri + 8);
1622 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1623 return (gchar *)(uri + 6);
1625 return (gchar *)uri;
1628 gint get_uri_len(const gchar *str)
1632 if (is_uri_string(str)) {
1633 for (p = str; *p != '\0'; p++) {
1634 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1643 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1644 * plusses, and escape characters are used)
1646 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1648 gchar *dec = decoded_uri;
1649 const gchar *enc = encoded_uri;
1654 if (isxdigit((guchar)enc[0]) &&
1655 isxdigit((guchar)enc[1])) {
1661 if (with_plus && *enc == '+')
1673 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1675 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1678 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1679 gchar **subject, gchar **body, gchar **attach)
1683 const gchar *forbidden_uris[] = { ".gnupg/",
1688 Xstrdup_a(tmp_mailto, mailto, return -1);
1690 if (!strncmp(tmp_mailto, "mailto:", 7))
1693 p = strchr(tmp_mailto, '?');
1700 *to = g_strdup(tmp_mailto);
1703 gchar *field, *value;
1720 if (*value == '\0') continue;
1722 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1723 *cc = g_strdup(value);
1724 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1725 *bcc = g_strdup(value);
1726 } else if (subject && !*subject &&
1727 !g_ascii_strcasecmp(field, "subject")) {
1728 *subject = g_malloc(strlen(value) + 1);
1729 decode_uri(*subject, value);
1730 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1731 *body = g_malloc(strlen(value) + 1);
1732 decode_uri(*body, value);
1733 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1735 *attach = g_malloc(strlen(value) + 1);
1736 decode_uri(*attach, value);
1737 for (; forbidden_uris[i]; i++) {
1738 if (strstr(*attach, forbidden_uris[i])) {
1739 g_print("Refusing to attach '%s', potential private data leak\n",
1754 #include <windows.h>
1755 #ifndef CSIDL_APPDATA
1756 #define CSIDL_APPDATA 0x001a
1758 #ifndef CSIDL_LOCAL_APPDATA
1759 #define CSIDL_LOCAL_APPDATA 0x001c
1761 #ifndef CSIDL_FLAG_CREATE
1762 #define CSIDL_FLAG_CREATE 0x8000
1764 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1768 w32_strerror (int w32_errno)
1770 static char strerr[256];
1771 int ec = (int)GetLastError ();
1775 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1776 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1777 strerr, DIM (strerr)-1, NULL);
1781 static __inline__ void *
1782 dlopen (const char * name, int flag)
1784 void * hd = LoadLibrary (name);
1788 static __inline__ void *
1789 dlsym (void * hd, const char * sym)
1793 void * fnc = GetProcAddress (hd, sym);
1802 static __inline__ const char *
1805 return w32_strerror (0);
1809 static __inline__ int
1821 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1823 static int initialized;
1824 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1828 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1834 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1836 handle = dlopen (dllnames[i], RTLD_LAZY);
1839 func = dlsym (handle, "SHGetFolderPathA");
1850 return func (a,b,c,d,e);
1855 /* Returns a static string with the directroy from which the module
1856 has been loaded. Returns an empty string on error. */
1857 static char *w32_get_module_dir(void)
1859 static char *moddir;
1862 char name[MAX_PATH+10];
1865 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1868 p = strrchr (name, '\\');
1874 moddir = g_strdup (name);
1878 #endif /* G_OS_WIN32 */
1880 /* Return a static string with the locale dir. */
1881 const gchar *get_locale_dir(void)
1883 static gchar *loc_dir;
1887 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1888 "\\share\\locale", NULL);
1891 loc_dir = LOCALEDIR;
1897 const gchar *get_home_dir(void)
1900 static char home_dir[MAX_PATH] = "";
1902 if (home_dir[0] == '\0') {
1903 if (w32_shgetfolderpath
1904 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1905 NULL, 0, home_dir) < 0)
1906 strcpy (home_dir, "C:\\Sylpheed");
1910 static const gchar *homeenv = NULL;
1915 if (!homeenv && g_getenv("HOME") != NULL)
1916 homeenv = g_strdup(g_getenv("HOME"));
1918 homeenv = g_get_home_dir();
1924 static gchar *claws_rc_dir = NULL;
1925 static gboolean rc_dir_alt = FALSE;
1926 const gchar *get_rc_dir(void)
1930 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1933 return claws_rc_dir;
1936 void set_rc_dir(const gchar *dir)
1938 if (claws_rc_dir != NULL) {
1939 g_print("Error: rc_dir already set\n");
1942 if (g_path_is_absolute(dir))
1943 claws_rc_dir = g_strdup(dir);
1945 claws_rc_dir = g_strconcat(g_get_current_dir(),
1946 G_DIR_SEPARATOR_S, dir, NULL);
1948 debug_print("set rc_dir to %s\n", claws_rc_dir);
1949 if (!is_dir_exist(claws_rc_dir)) {
1950 if (make_dir_hier(claws_rc_dir) != 0) {
1951 g_print("Error: can't create %s\n",
1958 gboolean rc_dir_is_alt(void) {
1962 const gchar *get_mail_base_dir(void)
1965 static gchar *mail_base_dir = NULL;
1968 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1971 return mail_base_dir;
1973 return get_home_dir();
1978 const gchar *prefs_common_get_data_root(void);
1979 gchar *last_data_root = NULL;
1982 const gchar *get_news_cache_dir(void)
1984 static gchar *news_cache_dir = NULL;
1986 const gchar *data_root = prefs_common_get_data_root();
1987 if (strcmp2(data_root, last_data_root)) {
1988 g_free(news_cache_dir);
1989 news_cache_dir = NULL;
1992 if (!news_cache_dir)
1994 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1995 NEWS_CACHE_DIR, NULL);
1999 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2000 "Claws", G_DIR_SEPARATOR_S,
2001 g_get_user_name(), G_DIR_SEPARATOR_S,
2002 NEWS_CACHE_DIR, NULL);
2003 g_free(last_data_root);
2004 last_data_root = g_strdup(last_data_root);
2006 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2007 NEWS_CACHE_DIR, NULL);
2008 g_free(last_data_root);
2009 last_data_root = NULL;
2013 return news_cache_dir;
2016 const gchar *get_imap_cache_dir(void)
2018 static gchar *imap_cache_dir = NULL;
2020 const gchar *data_root = prefs_common_get_data_root();
2021 if (strcmp2(data_root, last_data_root)) {
2022 g_free(imap_cache_dir);
2023 imap_cache_dir = NULL;
2027 if (!imap_cache_dir)
2029 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2030 IMAP_CACHE_DIR, NULL);
2034 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2035 "Claws", G_DIR_SEPARATOR_S,
2036 g_get_user_name(), G_DIR_SEPARATOR_S,
2037 IMAP_CACHE_DIR, NULL);
2038 g_free(last_data_root);
2039 last_data_root = g_strdup(last_data_root);
2041 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2042 IMAP_CACHE_DIR, NULL);
2043 g_free(last_data_root);
2044 last_data_root = NULL;
2049 return imap_cache_dir;
2052 const gchar *get_mbox_cache_dir(void)
2054 static gchar *mbox_cache_dir = NULL;
2056 const gchar *data_root = prefs_common_get_data_root();
2057 if (strcmp2(data_root, last_data_root)) {
2058 g_free(mbox_cache_dir);
2059 mbox_cache_dir = NULL;
2062 if (!mbox_cache_dir)
2064 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2065 MBOX_CACHE_DIR, NULL);
2069 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2070 "Claws", G_DIR_SEPARATOR_S,
2071 g_get_user_name(), G_DIR_SEPARATOR_S,
2072 MBOX_CACHE_DIR, NULL);
2073 g_free(last_data_root);
2074 last_data_root = g_strdup(last_data_root);
2076 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2077 MBOX_CACHE_DIR, NULL);
2078 g_free(last_data_root);
2079 last_data_root = NULL;
2084 return mbox_cache_dir;
2087 const gchar *get_mime_tmp_dir(void)
2089 static gchar *mime_tmp_dir = NULL;
2092 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2093 MIME_TMP_DIR, NULL);
2095 return mime_tmp_dir;
2098 const gchar *get_template_dir(void)
2100 static gchar *template_dir = NULL;
2103 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2104 TEMPLATE_DIR, NULL);
2106 return template_dir;
2109 const gchar *get_header_cache_dir(void)
2111 static gchar *header_dir = NULL;
2114 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2115 HEADER_CACHE_DIR, NULL);
2120 /* Return the default directory for Plugins. */
2121 const gchar *get_plugin_dir(void)
2124 static gchar *plugin_dir = NULL;
2127 plugin_dir = g_strconcat(w32_get_module_dir(),
2128 "\\lib\\claws-mail\\plugins\\",
2132 if (is_dir_exist(PLUGINDIR))
2135 static gchar *plugin_dir = NULL;
2137 plugin_dir = g_strconcat(get_rc_dir(),
2138 G_DIR_SEPARATOR_S, "plugins",
2139 G_DIR_SEPARATOR_S, NULL);
2145 const gchar *get_tmp_dir(void)
2147 static gchar *tmp_dir = NULL;
2150 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2156 gchar *get_tmp_file(void)
2159 static guint32 id = 0;
2161 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2162 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2167 const gchar *get_domain_name(void)
2170 static gchar *domain_name = NULL;
2176 if (gethostname(hostname, sizeof(hostname)) != 0) {
2177 perror("gethostname");
2178 domain_name = "unknown";
2180 hostname[sizeof(hostname) - 1] = '\0';
2181 if ((hp = my_gethostbyname(hostname)) == NULL) {
2182 perror("gethostbyname");
2183 domain_name = g_strdup(hostname);
2185 domain_name = g_strdup(hp->h_name);
2188 debug_print("domain name = %s\n", domain_name);
2197 off_t get_file_size(const gchar *file)
2201 if (g_stat(file, &s) < 0) {
2202 FILE_OP_ERROR(file, "stat");
2209 time_t get_file_mtime(const gchar *file)
2213 if (g_stat(file, &s) < 0) {
2214 FILE_OP_ERROR(file, "stat");
2221 off_t get_file_size_as_crlf(const gchar *file)
2225 gchar buf[BUFFSIZE];
2227 if ((fp = g_fopen(file, "rb")) == NULL) {
2228 FILE_OP_ERROR(file, "fopen");
2232 while (fgets(buf, sizeof(buf), fp) != NULL) {
2234 size += strlen(buf) + 2;
2238 FILE_OP_ERROR(file, "fgets");
2247 off_t get_left_file_size(FILE *fp)
2253 if ((pos = ftell(fp)) < 0) {
2257 if (fseek(fp, 0L, SEEK_END) < 0) {
2261 if ((end = ftell(fp)) < 0) {
2266 if (fseek(fp, pos, SEEK_SET) < 0) {
2274 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2281 if (g_stat(file, &s) < 0) {
2282 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2286 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2293 /* Test on whether FILE is a relative file name. This is
2294 * straightforward for Unix but more complex for Windows. */
2295 gboolean is_relative_filename(const gchar *file)
2300 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2301 return FALSE; /* Prefixed with a hostname - this can't
2302 * be a relative name. */
2304 if ( ((*file >= 'a' && *file <= 'z')
2305 || (*file >= 'A' && *file <= 'Z'))
2307 file += 2; /* Skip drive letter. */
2309 return !(*file == '\\' || *file == '/');
2311 return !(*file == G_DIR_SEPARATOR);
2316 gboolean is_dir_exist(const gchar *dir)
2321 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2324 gboolean is_file_entry_exist(const gchar *file)
2329 return g_file_test(file, G_FILE_TEST_EXISTS);
2332 gboolean dirent_is_regular_file(struct dirent *d)
2334 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2335 if (d->d_type == DT_REG)
2337 else if (d->d_type != DT_UNKNOWN)
2341 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2344 gboolean dirent_is_directory(struct dirent *d)
2346 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2347 if (d->d_type == DT_DIR)
2349 else if (d->d_type != DT_UNKNOWN)
2353 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2356 gint change_dir(const gchar *dir)
2358 gchar *prevdir = NULL;
2361 prevdir = g_get_current_dir();
2363 if (g_chdir(dir) < 0) {
2364 FILE_OP_ERROR(dir, "chdir");
2365 if (debug_mode) g_free(prevdir);
2367 } else if (debug_mode) {
2370 cwd = g_get_current_dir();
2371 if (strcmp(prevdir, cwd) != 0)
2372 g_print("current dir: %s\n", cwd);
2380 gint make_dir(const gchar *dir)
2382 if (g_mkdir(dir, S_IRWXU) < 0) {
2383 FILE_OP_ERROR(dir, "mkdir");
2386 if (g_chmod(dir, S_IRWXU) < 0)
2387 FILE_OP_ERROR(dir, "chmod");
2392 gint make_dir_hier(const gchar *dir)
2397 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2398 parent_dir = g_strndup(dir, p - dir);
2399 if (*parent_dir != '\0') {
2400 if (!is_dir_exist(parent_dir)) {
2401 if (make_dir(parent_dir) < 0) {
2410 if (!is_dir_exist(dir)) {
2411 if (make_dir(dir) < 0)
2418 gint remove_all_files(const gchar *dir)
2421 const gchar *dir_name;
2424 prev_dir = g_get_current_dir();
2426 if (g_chdir(dir) < 0) {
2427 FILE_OP_ERROR(dir, "chdir");
2432 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2433 g_warning("failed to open directory: %s\n", dir);
2438 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2439 if (g_unlink(dir_name) < 0)
2440 FILE_OP_ERROR(dir_name, "unlink");
2445 if (g_chdir(prev_dir) < 0) {
2446 FILE_OP_ERROR(prev_dir, "chdir");
2456 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2459 const gchar *dir_name;
2463 prev_dir = g_get_current_dir();
2465 if (g_chdir(dir) < 0) {
2466 FILE_OP_ERROR(dir, "chdir");
2471 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2472 g_warning("failed to open directory: %s\n", dir);
2477 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2478 file_no = to_number(dir_name);
2479 if (file_no > 0 && first <= file_no && file_no <= last) {
2480 if (is_dir_exist(dir_name))
2482 if (g_unlink(dir_name) < 0)
2483 FILE_OP_ERROR(dir_name, "unlink");
2489 if (g_chdir(prev_dir) < 0) {
2490 FILE_OP_ERROR(prev_dir, "chdir");
2500 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2503 const gchar *dir_name;
2507 prev_dir = g_get_current_dir();
2509 if (g_chdir(dir) < 0) {
2510 FILE_OP_ERROR(dir, "chdir");
2515 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2516 FILE_OP_ERROR(dir, "opendir");
2521 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2522 file_no = to_number(dir_name);
2523 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2524 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2525 if (is_dir_exist(dir_name))
2527 if (g_unlink(dir_name) < 0)
2528 FILE_OP_ERROR(dir_name, "unlink");
2534 if (g_chdir(prev_dir) < 0) {
2535 FILE_OP_ERROR(prev_dir, "chdir");
2545 gint remove_all_numbered_files(const gchar *dir)
2547 return remove_numbered_files(dir, 0, UINT_MAX);
2550 gint remove_expired_files(const gchar *dir, guint hours)
2553 const gchar *dir_name;
2557 time_t mtime, now, expire_time;
2559 prev_dir = g_get_current_dir();
2561 if (g_chdir(dir) < 0) {
2562 FILE_OP_ERROR(dir, "chdir");
2567 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2568 g_warning("failed to open directory: %s\n", dir);
2574 expire_time = hours * 60 * 60;
2576 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2577 file_no = to_number(dir_name);
2579 if (g_stat(dir_name, &s) < 0) {
2580 FILE_OP_ERROR(dir_name, "stat");
2583 if (S_ISDIR(s.st_mode))
2585 mtime = MAX(s.st_mtime, s.st_atime);
2586 if (now - mtime > expire_time) {
2587 if (g_unlink(dir_name) < 0)
2588 FILE_OP_ERROR(dir_name, "unlink");
2595 if (g_chdir(prev_dir) < 0) {
2596 FILE_OP_ERROR(prev_dir, "chdir");
2606 gint remove_dir_recursive(const gchar *dir)
2610 const gchar *dir_name;
2613 if (g_stat(dir, &s) < 0) {
2614 FILE_OP_ERROR(dir, "stat");
2615 if (ENOENT == errno) return 0;
2619 if (!S_ISDIR(s.st_mode)) {
2620 if (g_unlink(dir) < 0) {
2621 FILE_OP_ERROR(dir, "unlink");
2628 prev_dir = g_get_current_dir();
2629 /* g_print("prev_dir = %s\n", prev_dir); */
2631 if (!path_cmp(prev_dir, dir)) {
2633 if (g_chdir("..") < 0) {
2634 FILE_OP_ERROR(dir, "chdir");
2637 prev_dir = g_get_current_dir();
2640 if (g_chdir(dir) < 0) {
2641 FILE_OP_ERROR(dir, "chdir");
2646 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2647 g_warning("failed to open directory: %s\n", dir);
2653 /* remove all files in the directory */
2654 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2655 /* g_print("removing %s\n", dir_name); */
2657 if (is_dir_exist(dir_name)) {
2658 if (remove_dir_recursive(dir_name) < 0) {
2659 g_warning("can't remove directory\n");
2663 if (g_unlink(dir_name) < 0)
2664 FILE_OP_ERROR(dir_name, "unlink");
2670 if (g_chdir(prev_dir) < 0) {
2671 FILE_OP_ERROR(prev_dir, "chdir");
2678 if (g_rmdir(dir) < 0) {
2679 FILE_OP_ERROR(dir, "rmdir");
2686 gint rename_force(const gchar *oldpath, const gchar *newpath)
2689 if (!is_file_entry_exist(oldpath)) {
2693 if (is_file_exist(newpath)) {
2694 if (g_unlink(newpath) < 0)
2695 FILE_OP_ERROR(newpath, "unlink");
2698 return g_rename(oldpath, newpath);
2702 * Append src file body to the tail of dest file.
2703 * Now keep_backup has no effects.
2705 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2707 FILE *src_fp, *dest_fp;
2711 gboolean err = FALSE;
2713 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2714 FILE_OP_ERROR(src, "fopen");
2718 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2719 FILE_OP_ERROR(dest, "fopen");
2724 if (change_file_mode_rw(dest_fp, dest) < 0) {
2725 FILE_OP_ERROR(dest, "chmod");
2726 g_warning("can't change file mode\n");
2729 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2730 if (n_read < sizeof(buf) && ferror(src_fp))
2732 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2733 g_warning("writing to %s failed.\n", dest);
2741 if (ferror(src_fp)) {
2742 FILE_OP_ERROR(src, "fread");
2746 if (fclose(dest_fp) == EOF) {
2747 FILE_OP_ERROR(dest, "fclose");
2759 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2761 FILE *src_fp, *dest_fp;
2764 gchar *dest_bak = NULL;
2765 gboolean err = FALSE;
2767 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2768 FILE_OP_ERROR(src, "fopen");
2771 if (is_file_exist(dest)) {
2772 dest_bak = g_strconcat(dest, ".bak", NULL);
2773 if (rename_force(dest, dest_bak) < 0) {
2774 FILE_OP_ERROR(dest, "rename");
2781 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2782 FILE_OP_ERROR(dest, "fopen");
2785 if (rename_force(dest_bak, dest) < 0)
2786 FILE_OP_ERROR(dest_bak, "rename");
2792 if (change_file_mode_rw(dest_fp, dest) < 0) {
2793 FILE_OP_ERROR(dest, "chmod");
2794 g_warning("can't change file mode\n");
2797 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2798 if (n_read < sizeof(buf) && ferror(src_fp))
2800 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2801 g_warning("writing to %s failed.\n", dest);
2806 if (rename_force(dest_bak, dest) < 0)
2807 FILE_OP_ERROR(dest_bak, "rename");
2814 if (ferror(src_fp)) {
2815 FILE_OP_ERROR(src, "fread");
2819 if (fclose(dest_fp) == EOF) {
2820 FILE_OP_ERROR(dest, "fclose");
2827 if (rename_force(dest_bak, dest) < 0)
2828 FILE_OP_ERROR(dest_bak, "rename");
2834 if (keep_backup == FALSE && dest_bak)
2842 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2844 if (overwrite == FALSE && is_file_exist(dest)) {
2845 g_warning("move_file(): file %s already exists.", dest);
2849 if (rename_force(src, dest) == 0) return 0;
2851 if (EXDEV != errno) {
2852 FILE_OP_ERROR(src, "rename");
2856 if (copy_file(src, dest, FALSE) < 0) return -1;
2863 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2866 gint bytes_left, to_read;
2869 if (fseek(fp, offset, SEEK_SET) < 0) {
2874 bytes_left = length;
2875 to_read = MIN(bytes_left, sizeof(buf));
2877 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2878 if (n_read < to_read && ferror(fp))
2880 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2883 bytes_left -= n_read;
2884 if (bytes_left == 0)
2886 to_read = MIN(bytes_left, sizeof(buf));
2897 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2900 gboolean err = FALSE;
2902 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2903 FILE_OP_ERROR(dest, "fopen");
2907 if (change_file_mode_rw(dest_fp, dest) < 0) {
2908 FILE_OP_ERROR(dest, "chmod");
2909 g_warning("can't change file mode\n");
2912 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2915 if (!err && fclose(dest_fp) == EOF) {
2916 FILE_OP_ERROR(dest, "fclose");
2921 g_warning("writing to %s failed.\n", dest);
2929 /* convert line endings into CRLF. If the last line doesn't end with
2930 * linebreak, add it.
2932 gchar *canonicalize_str(const gchar *str)
2938 for (p = str; *p != '\0'; ++p) {
2945 if (p == str || *(p - 1) != '\n')
2948 out = outp = g_malloc(new_len + 1);
2949 for (p = str; *p != '\0'; ++p) {
2956 if (p == str || *(p - 1) != '\n') {
2965 gint canonicalize_file(const gchar *src, const gchar *dest)
2967 FILE *src_fp, *dest_fp;
2968 gchar buf[BUFFSIZE];
2970 gboolean err = FALSE;
2971 gboolean last_linebreak = FALSE;
2973 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2974 FILE_OP_ERROR(src, "fopen");
2978 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2979 FILE_OP_ERROR(dest, "fopen");
2984 if (change_file_mode_rw(dest_fp, dest) < 0) {
2985 FILE_OP_ERROR(dest, "chmod");
2986 g_warning("can't change file mode\n");
2989 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2993 if (len == 0) break;
2994 last_linebreak = FALSE;
2996 if (buf[len - 1] != '\n') {
2997 last_linebreak = TRUE;
2998 r = fputs(buf, dest_fp);
2999 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3000 r = fputs(buf, dest_fp);
3003 r = fwrite(buf, 1, len - 1, dest_fp);
3008 r = fputs("\r\n", dest_fp);
3012 g_warning("writing to %s failed.\n", dest);
3020 if (last_linebreak == TRUE) {
3021 if (fputs("\r\n", dest_fp) == EOF)
3025 if (ferror(src_fp)) {
3026 FILE_OP_ERROR(src, "fgets");
3030 if (fclose(dest_fp) == EOF) {
3031 FILE_OP_ERROR(dest, "fclose");
3043 gint canonicalize_file_replace(const gchar *file)
3047 tmp_file = get_tmp_file();
3049 if (canonicalize_file(file, tmp_file) < 0) {
3054 if (move_file(tmp_file, file, TRUE) < 0) {
3055 g_warning("can't replace %s .\n", file);
3065 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3067 FILE *src_fp, *dest_fp;
3068 gchar buf[BUFFSIZE];
3069 gboolean err = FALSE;
3071 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3072 FILE_OP_ERROR(src, "fopen");
3076 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3077 FILE_OP_ERROR(dest, "fopen");
3082 if (change_file_mode_rw(dest_fp, dest) < 0) {
3083 FILE_OP_ERROR(dest, "chmod");
3084 g_warning("can't change file mode\n");
3087 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3089 if (fputs(buf, dest_fp) == EOF) {
3090 g_warning("writing to %s failed.\n", dest);
3098 if (ferror(src_fp)) {
3099 FILE_OP_ERROR(src, "fgets");
3103 if (fclose(dest_fp) == EOF) {
3104 FILE_OP_ERROR(dest, "fclose");
3116 gint uncanonicalize_file_replace(const gchar *file)
3120 tmp_file = get_tmp_file();
3122 if (uncanonicalize_file(file, tmp_file) < 0) {
3127 if (move_file(tmp_file, file, TRUE) < 0) {
3128 g_warning("can't replace %s .\n", file);
3138 gchar *normalize_newlines(const gchar *str)
3140 const gchar *p = str;
3143 out = outp = g_malloc(strlen(str) + 1);
3144 for (p = str; *p != '\0'; ++p) {
3146 if (*(p + 1) != '\n')
3157 gchar *get_outgoing_rfc2822_str(FILE *fp)
3159 gchar buf[BUFFSIZE];
3163 str = g_string_new(NULL);
3165 /* output header part */
3166 while (fgets(buf, sizeof(buf), fp) != NULL) {
3168 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3175 else if (next != ' ' && next != '\t') {
3179 if (fgets(buf, sizeof(buf), fp) == NULL)
3183 g_string_append(str, buf);
3184 g_string_append(str, "\r\n");
3190 /* output body part */
3191 while (fgets(buf, sizeof(buf), fp) != NULL) {
3194 g_string_append_c(str, '.');
3195 g_string_append(str, buf);
3196 g_string_append(str, "\r\n");
3200 g_string_free(str, FALSE);
3206 * Create a new boundary in a way that it is very unlikely that this
3207 * will occur in the following text. It would be easy to ensure
3208 * uniqueness if everything is either quoted-printable or base64
3209 * encoded (note that conversion is allowed), but because MIME bodies
3210 * may be nested, it may happen that the same boundary has already
3213 * boundary := 0*69<bchars> bcharsnospace
3214 * bchars := bcharsnospace / " "
3215 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3216 * "+" / "_" / "," / "-" / "." /
3217 * "/" / ":" / "=" / "?"
3219 * some special characters removed because of buggy MTAs
3222 gchar *generate_mime_boundary(const gchar *prefix)
3224 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3225 "abcdefghijklmnopqrstuvwxyz"
3230 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3231 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3234 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
3238 gint change_file_mode_rw(FILE *fp, const gchar *file)
3241 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3243 return g_chmod(file, S_IRUSR|S_IWUSR);
3247 FILE *my_tmpfile(void)
3249 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3250 const gchar suffix[] = ".XXXXXX";
3251 const gchar *tmpdir;
3253 const gchar *progname;
3260 tmpdir = get_tmp_dir();
3261 tmplen = strlen(tmpdir);
3262 progname = g_get_prgname();
3263 if (progname == NULL)
3264 progname = "claws-mail";
3265 proglen = strlen(progname);
3266 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3269 memcpy(fname, tmpdir, tmplen);
3270 fname[tmplen] = G_DIR_SEPARATOR;
3271 memcpy(fname + tmplen + 1, progname, proglen);
3272 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3274 fd = mkstemp(fname);
3281 /* verify that we can write in the file after unlinking */
3282 if (write(fd, buf, 1) < 0) {
3289 fp = fdopen(fd, "w+b");
3297 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3302 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3306 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3307 dir, G_DIR_SEPARATOR);
3308 fd = mkstemp_name(template, filename);
3311 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3312 fd = mkstemp(*filename);
3314 return fdopen(fd, "w+");
3317 FILE *str_open_as_stream(const gchar *str)
3322 g_return_val_if_fail(str != NULL, NULL);
3326 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3331 if (len == 0) return fp;
3333 if (fwrite(str, 1, len, fp) != len) {
3334 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3343 gint str_write_to_file(const gchar *str, const gchar *file)
3348 g_return_val_if_fail(str != NULL, -1);
3349 g_return_val_if_fail(file != NULL, -1);
3351 if ((fp = g_fopen(file, "wb")) == NULL) {
3352 FILE_OP_ERROR(file, "fopen");
3362 if (fwrite(str, 1, len, fp) != len) {
3363 FILE_OP_ERROR(file, "fwrite");
3369 if (fclose(fp) == EOF) {
3370 FILE_OP_ERROR(file, "fclose");
3378 gchar *file_read_to_str(const gchar *file)
3383 g_return_val_if_fail(file != NULL, NULL);
3385 if ((fp = g_fopen(file, "rb")) == NULL) {
3386 FILE_OP_ERROR(file, "fopen");
3390 str = file_read_stream_to_str(fp);
3397 gchar *file_read_stream_to_str(FILE *fp)
3404 g_return_val_if_fail(fp != NULL, NULL);
3406 array = g_byte_array_new();
3408 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3409 if (n_read < sizeof(buf) && ferror(fp))
3411 g_byte_array_append(array, buf, n_read);
3415 FILE_OP_ERROR("file stream", "fread");
3416 g_byte_array_free(array, TRUE);
3421 g_byte_array_append(array, buf, 1);
3422 str = (gchar *)array->data;
3423 g_byte_array_free(array, FALSE);
3425 if (!g_utf8_validate(str, -1, NULL)) {
3426 const gchar *src_codeset, *dest_codeset;
3428 src_codeset = conv_get_locale_charset_str();
3429 dest_codeset = CS_UTF_8;
3430 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3439 char *fgets_crlf(char *buf, int size, FILE *stream)
3441 gboolean is_cr = FALSE;
3442 gboolean last_was_cr = FALSE;
3447 while (--size > 0 && (c = getc(stream)) != EOF)
3450 is_cr = (c == '\r');
3460 last_was_cr = is_cr;
3462 if (c == EOF && cs == buf)
3470 static gint execute_async(gchar *const argv[])
3472 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3474 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3475 NULL, NULL, NULL, FALSE) == FALSE) {
3476 g_warning("Couldn't execute command: %s\n", argv[0]);
3483 static gint execute_sync(gchar *const argv[])
3487 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3489 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3490 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3491 g_warning("Couldn't execute command: %s\n", argv[0]);
3496 if (WIFEXITED(status))
3497 return WEXITSTATUS(status);
3505 gint execute_command_line(const gchar *cmdline, gboolean async)
3510 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3512 argv = strsplit_with_quote(cmdline, " ", 0);
3515 ret = execute_async(argv);
3517 ret = execute_sync(argv);
3524 gchar *get_command_output(const gchar *cmdline)
3526 gchar *child_stdout;
3529 g_return_val_if_fail(cmdline != NULL, NULL);
3531 debug_print("get_command_output(): executing: %s\n", cmdline);
3533 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3535 g_warning("Couldn't execute command: %s\n", cmdline);
3539 return child_stdout;
3542 static gint is_unchanged_uri_char(char c)
3553 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3559 for(i = 0; i < strlen(uri) ; i++) {
3560 if (is_unchanged_uri_char(uri[i])) {
3561 if (k + 2 >= bufsize)
3563 encoded_uri[k++] = uri[i];
3566 char * hexa = "0123456789ABCDEF";
3568 if (k + 4 >= bufsize)
3570 encoded_uri[k++] = '%';
3571 encoded_uri[k++] = hexa[uri[i] / 16];
3572 encoded_uri[k++] = hexa[uri[i] % 16];
3578 gint open_uri(const gchar *uri, const gchar *cmdline)
3581 gchar buf[BUFFSIZE];
3583 gchar encoded_uri[BUFFSIZE];
3584 g_return_val_if_fail(uri != NULL, -1);
3586 /* an option to choose whether to use encode_uri or not ? */
3587 encode_uri(encoded_uri, BUFFSIZE, uri);
3590 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3591 !strchr(p + 2, '%'))
3592 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3595 g_warning("Open URI command line is invalid "
3596 "(there must be only one '%%s'): %s",
3598 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3601 execute_command_line(buf, TRUE);
3603 extern osso_context_t *get_osso_context(void);
3604 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3605 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3606 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3611 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3613 gchar buf[BUFFSIZE];
3616 g_return_val_if_fail(filepath != NULL, -1);
3619 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3620 !strchr(p + 2, '%'))
3621 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3624 g_warning("Open Text Editor command line is invalid "
3625 "(there must be only one '%%s'): %s",
3627 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3630 execute_command_line(buf, TRUE);
3635 time_t remote_tzoffset_sec(const gchar *zone)
3637 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3643 time_t remoteoffset;
3645 strncpy(zone3, zone, 3);
3649 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3650 (c == '+' || c == '-')) {
3651 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3653 remoteoffset = -remoteoffset;
3654 } else if (!strncmp(zone, "UT" , 2) ||
3655 !strncmp(zone, "GMT", 2)) {
3657 } else if (strlen(zone3) == 3) {
3658 for (p = ustzstr; *p != '\0'; p += 3) {
3659 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3660 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3661 remoteoffset = iustz * 3600;
3667 } else if (strlen(zone3) == 1) {
3669 case 'Z': remoteoffset = 0; break;
3670 case 'A': remoteoffset = -1; break;
3671 case 'B': remoteoffset = -2; break;
3672 case 'C': remoteoffset = -3; break;
3673 case 'D': remoteoffset = -4; break;
3674 case 'E': remoteoffset = -5; break;
3675 case 'F': remoteoffset = -6; break;
3676 case 'G': remoteoffset = -7; break;
3677 case 'H': remoteoffset = -8; break;
3678 case 'I': remoteoffset = -9; break;
3679 case 'K': remoteoffset = -10; break; /* J is not used */
3680 case 'L': remoteoffset = -11; break;
3681 case 'M': remoteoffset = -12; break;
3682 case 'N': remoteoffset = 1; break;
3683 case 'O': remoteoffset = 2; break;
3684 case 'P': remoteoffset = 3; break;
3685 case 'Q': remoteoffset = 4; break;
3686 case 'R': remoteoffset = 5; break;
3687 case 'S': remoteoffset = 6; break;
3688 case 'T': remoteoffset = 7; break;
3689 case 'U': remoteoffset = 8; break;
3690 case 'V': remoteoffset = 9; break;
3691 case 'W': remoteoffset = 10; break;
3692 case 'X': remoteoffset = 11; break;
3693 case 'Y': remoteoffset = 12; break;
3694 default: remoteoffset = 0; break;
3696 remoteoffset = remoteoffset * 3600;
3700 return remoteoffset;
3703 time_t tzoffset_sec(time_t *now)
3707 struct tm buf1, buf2;
3709 gmt = *gmtime_r(now, &buf1);
3710 lt = localtime_r(now, &buf2);
3712 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3714 if (lt->tm_year < gmt.tm_year)
3716 else if (lt->tm_year > gmt.tm_year)
3718 else if (lt->tm_yday < gmt.tm_yday)
3720 else if (lt->tm_yday > gmt.tm_yday)
3723 if (off >= 24 * 60) /* should be impossible */
3724 off = 23 * 60 + 59; /* if not, insert silly value */
3725 if (off <= -24 * 60)
3726 off = -(23 * 60 + 59);
3731 /* calculate timezone offset */
3732 gchar *tzoffset(time_t *now)
3734 static gchar offset_string[6];
3738 struct tm buf1, buf2;
3740 gmt = *gmtime_r(now, &buf1);
3741 lt = localtime_r(now, &buf2);
3743 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3745 if (lt->tm_year < gmt.tm_year)
3747 else if (lt->tm_year > gmt.tm_year)
3749 else if (lt->tm_yday < gmt.tm_yday)
3751 else if (lt->tm_yday > gmt.tm_yday)
3759 if (off >= 24 * 60) /* should be impossible */
3760 off = 23 * 60 + 59; /* if not, insert silly value */
3762 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3764 return offset_string;
3767 void get_rfc822_date(gchar *buf, gint len)
3771 gchar day[4], mon[4];
3772 gint dd, hh, mm, ss, yyyy;
3774 gchar buf2[BUFFSIZE];
3777 lt = localtime_r(&t, &buf1);
3780 sscanf(asctime_r(lt, buf2, sizeof(buf2)), "%3s %3s %d %d:%d:%d %d\n",
3781 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3783 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3784 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3786 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3787 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3790 /* just a wrapper to suppress the warning of gcc about %c */
3791 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3792 const struct tm *tm)
3794 return strftime(s, max, format, tm);
3797 void debug_set_mode(gboolean mode)
3802 gboolean debug_get_mode(void)
3807 void debug_print_real(const gchar *format, ...)
3810 gchar buf[BUFFSIZE];
3812 if (!debug_mode) return;
3814 va_start(args, format);
3815 g_vsnprintf(buf, sizeof(buf), format, args);
3822 const char * debug_srcname(const char *file)
3824 const char *s = strrchr (file, '/');
3829 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3831 if (subject == NULL)
3834 subject += subject_get_prefix_length(subject);
3836 return g_hash_table_lookup(subject_table, subject);
3839 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3842 if (subject == NULL || *subject == 0)
3844 subject += subject_get_prefix_length(subject);
3845 g_hash_table_insert(subject_table, subject, data);
3848 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3850 if (subject == NULL)
3853 subject += subject_get_prefix_length(subject);
3854 g_hash_table_remove(subject_table, subject);
3858 *\brief Check if a string is prefixed with known (combinations)
3859 * of prefixes. The function assumes that each prefix
3860 * is terminated by zero or exactly _one_ space.
3862 *\param str String to check for a prefixes
3864 *\return int Number of chars in the prefix that should be skipped
3865 * for a "clean" subject line. If no prefix was found, 0
3868 int subject_get_prefix_length(const gchar *subject)
3870 /*!< Array with allowable reply prefixes regexps. */
3871 static const gchar * const prefixes[] = {
3872 "Re\\:", /* "Re:" */
3873 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3874 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3875 "Aw\\:", /* "Aw:" (German) */
3876 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3877 "Res\\:", /* "Res:" (Brazilian Outlook) */
3878 "Fw\\:", /* "Fw:" Forward */
3879 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3880 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3881 "Rif\\:", /* "Rif:" (Italian Outlook) */
3882 "Sv\\:", /* "Sv" (Norwegian) */
3883 "Vs\\:", /* "Vs" (Norwegian) */
3884 "Ad\\:", /* "Ad" (Norwegian) */
3885 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3888 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3891 static regex_t regex;
3892 static gboolean init_;
3894 if (!subject) return 0;
3895 if (!*subject) return 0;
3898 GString *s = g_string_new("");
3900 for (n = 0; n < PREFIXES; n++)
3901 /* Terminate each prefix regexpression by a
3902 * "\ ?" (zero or ONE space), and OR them */
3903 g_string_append_printf(s, "(%s\\ ?)%s",
3908 g_string_prepend(s, "(");
3909 g_string_append(s, ")+"); /* match at least once */
3910 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3913 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3914 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3915 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3916 debug_print("Error compiling regexp %s\n", s->str);
3917 g_string_free(s, TRUE);
3921 g_string_free(s, TRUE);
3925 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3931 guint g_stricase_hash(gconstpointer gptr)
3933 guint hash_result = 0;
3936 for (str = gptr; str && *str; str++) {
3937 hash_result += toupper(*str);
3943 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3945 const char *str1 = gptr1;
3946 const char *str2 = gptr2;
3948 return !strcasecmp(str1, str2);
3951 gint g_int_compare(gconstpointer a, gconstpointer b)
3953 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3956 gchar *generate_msgid(gchar *buf, gint len)
3964 lt = localtime_r(&t, &buft);
3966 if (strcmp(buf, "") == 0) {
3967 addr = g_strconcat("@", get_domain_name(), NULL);
3970 addr = g_strconcat("@", buf, NULL);
3973 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3974 lt->tm_year + 1900, lt->tm_mon + 1,
3975 lt->tm_mday, lt->tm_hour,
3976 lt->tm_min, lt->tm_sec,
3977 (guint) rand(), addr);
3984 quote_cmd_argument()
3986 return a quoted string safely usable in argument of a command.
3988 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3991 gint quote_cmd_argument(gchar * result, guint size,
4001 for(p = path ; * p != '\0' ; p ++) {
4003 if (isalnum((guchar)*p) || (* p == '/')) {
4004 if (remaining > 0) {
4010 result[size - 1] = '\0';
4015 if (remaining >= 2) {
4023 result[size - 1] = '\0';
4028 if (remaining > 0) {
4032 result[size - 1] = '\0';
4046 static void g_node_map_recursive(GNode *node, gpointer data)
4048 GNodeMapData *mapdata = (GNodeMapData *) data;
4050 GNodeMapData newmapdata;
4053 newdata = mapdata->func(node->data, mapdata->data);
4054 if (newdata != NULL) {
4055 newnode = g_node_new(newdata);
4056 g_node_append(mapdata->parent, newnode);
4058 newmapdata.parent = newnode;
4059 newmapdata.func = mapdata->func;
4060 newmapdata.data = mapdata->data;
4062 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4066 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4069 GNodeMapData mapdata;
4071 g_return_val_if_fail(node != NULL, NULL);
4072 g_return_val_if_fail(func != NULL, NULL);
4074 root = g_node_new(func(node->data, data));
4076 mapdata.parent = root;
4077 mapdata.func = func;
4078 mapdata.data = data;
4080 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4085 #define HEX_TO_INT(val, hex) \
4089 if ('0' <= c && c <= '9') { \
4091 } else if ('a' <= c && c <= 'f') { \
4092 val = c - 'a' + 10; \
4093 } else if ('A' <= c && c <= 'F') { \
4094 val = c - 'A' + 10; \
4100 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4107 if (hi == -1 || lo == -1)
4110 *out = (hi << 4) + lo;
4114 #define INT_TO_HEX(hex, val) \
4117 hex = '0' + (val); \
4119 hex = 'A' + (val) - 10; \
4122 void get_hex_str(gchar *out, guchar ch)
4126 INT_TO_HEX(hex, ch >> 4);
4128 INT_TO_HEX(hex, ch & 0x0f);
4134 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4136 #define G_PRINT_REF g_print
4140 *\brief Register ref counted pointer. It is based on GBoxed, so should
4141 * work with anything that uses the GType system. The semantics
4142 * are similar to a C++ auto pointer, with the exception that
4143 * C doesn't have automatic closure (calling destructors) when
4144 * exiting a block scope.
4145 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4146 * function directly.
4148 *\return GType A GType type.
4150 GType g_auto_pointer_register(void)
4152 static GType auto_pointer_type;
4153 if (!auto_pointer_type)
4155 g_boxed_type_register_static
4156 ("G_TYPE_AUTO_POINTER",
4157 (GBoxedCopyFunc) g_auto_pointer_copy,
4158 (GBoxedFreeFunc) g_auto_pointer_free);
4159 return auto_pointer_type;
4163 *\brief Structure with g_new() allocated pointer guarded by the
4166 typedef struct AutoPointerRef {
4167 void (*free) (gpointer);
4173 *\brief The auto pointer opaque structure that references the
4174 * pointer guard block.
4176 typedef struct AutoPointer {
4177 AutoPointerRef *ref;
4178 gpointer ptr; /*!< access to protected pointer */
4182 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4186 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4187 * ... when assigning, copying and freeing storage elements
4189 * gtk_list_store_new(N_S_COLUMNS,
4190 * G_TYPE_AUTO_POINTER,
4194 * Template *precious_data = g_new0(Template, 1);
4195 * g_pointer protect = g_auto_pointer_new(precious_data);
4197 * gtk_list_store_set(container, &iter,
4201 * ... the gtk_list_store has copied the pointer and
4202 * ... incremented its reference count, we should free
4203 * ... the auto pointer (in C++ a destructor would do
4204 * ... this for us when leaving block scope)
4206 * g_auto_pointer_free(protect);
4208 * ... gtk_list_store_set() now manages the data. When
4209 * ... *explicitly* requesting a pointer from the list
4210 * ... store, don't forget you get a copy that should be
4211 * ... freed with g_auto_pointer_free() eventually.
4215 *\param pointer Pointer to be guarded.
4217 *\return GAuto * Pointer that should be used in containers with
4220 GAuto *g_auto_pointer_new(gpointer p)
4222 AutoPointerRef *ref;
4228 ref = g_new0(AutoPointerRef, 1);
4229 ptr = g_new0(AutoPointer, 1);
4239 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4245 *\brief Allocate an autopointer using the passed \a free function to
4246 * free the guarded pointer
4248 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4255 aptr = g_auto_pointer_new(p);
4256 aptr->ref->free = free_;
4260 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4262 if (auto_ptr == NULL)
4264 return ((AutoPointer *) auto_ptr)->ptr;
4268 *\brief Copies an auto pointer by. It's mostly not necessary
4269 * to call this function directly, unless you copy/assign
4270 * the guarded pointer.
4272 *\param auto_ptr Auto pointer returned by previous call to
4273 * g_auto_pointer_new_XXX()
4275 *\return gpointer An auto pointer
4277 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4280 AutoPointerRef *ref;
4283 if (auto_ptr == NULL)
4288 newp = g_new0(AutoPointer, 1);
4291 newp->ptr = ref->pointer;
4295 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4301 *\brief Free an auto pointer
4303 void g_auto_pointer_free(GAuto *auto_ptr)
4306 AutoPointerRef *ref;
4308 if (auto_ptr == NULL)
4314 if (--(ref->cnt) == 0) {
4316 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4318 ref->free(ref->pointer);
4323 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4328 void replace_returns(gchar *str)
4333 while (strstr(str, "\n")) {
4334 *strstr(str, "\n") = ' ';
4336 while (strstr(str, "\r")) {
4337 *strstr(str, "\r") = ' ';
4341 /* get_uri_part() - retrieves a URI starting from scanpos.
4342 Returns TRUE if succesful */
4343 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4344 const gchar **bp, const gchar **ep, gboolean hdr)
4347 gint parenthese_cnt = 0;
4349 g_return_val_if_fail(start != NULL, FALSE);
4350 g_return_val_if_fail(scanpos != NULL, FALSE);
4351 g_return_val_if_fail(bp != NULL, FALSE);
4352 g_return_val_if_fail(ep != NULL, FALSE);
4356 /* find end point of URI */
4357 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4358 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4359 !IS_ASCII(*(const guchar *)ep_) ||
4360 strchr("[]{}<>\"", *ep_)) {
4362 } else if (strchr("(", *ep_)) {
4364 } else if (strchr(")", *ep_)) {
4365 if (parenthese_cnt > 0)
4372 /* no punctuation at end of string */
4374 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4375 * should pass some URI type to this function and decide on that whether
4376 * to perform punctuation stripping */
4378 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4380 for (; ep_ - 1 > scanpos + 1 &&
4381 IS_REAL_PUNCT(*(ep_ - 1));
4385 #undef IS_REAL_PUNCT
4392 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4394 while (bp && *bp && g_ascii_isspace(*bp))
4396 return g_strndup(bp, ep - bp);
4399 /* valid mail address characters */
4400 #define IS_RFC822_CHAR(ch) \
4404 !g_ascii_isspace(ch) && \
4405 !strchr("(),;<>\"", (ch)))
4407 /* alphabet and number within 7bit ASCII */
4408 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4409 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4411 static GHashTable *create_domain_tab(void)
4413 static const gchar *toplvl_domains [] = {
4415 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4416 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4417 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4418 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4419 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4420 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4421 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4422 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4423 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4424 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4425 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4426 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4427 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4428 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4429 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4430 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4431 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4432 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4433 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4434 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4435 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4436 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4437 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4438 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4439 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4440 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4443 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4445 g_return_val_if_fail(htab, NULL);
4446 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4447 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4451 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4453 const gint MAX_LVL_DOM_NAME_LEN = 6;
4454 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4455 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4458 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4461 for (p = buf; p < m && first < last; *p++ = *first++)
4465 return g_hash_table_lookup(tab, buf) != NULL;
4468 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4469 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4470 const gchar **bp, const gchar **ep, gboolean hdr)
4472 /* more complex than the uri part because we need to scan back and forward starting from
4473 * the scan position. */
4474 gboolean result = FALSE;
4475 const gchar *bp_ = NULL;
4476 const gchar *ep_ = NULL;
4477 static GHashTable *dom_tab;
4478 const gchar *last_dot = NULL;
4479 const gchar *prelast_dot = NULL;
4480 const gchar *last_tld_char = NULL;
4482 /* the informative part of the email address (describing the name
4483 * of the email address owner) may contain quoted parts. the
4484 * closure stack stores the last encountered quotes. */
4485 gchar closure_stack[128];
4486 gchar *ptr = closure_stack;
4488 g_return_val_if_fail(start != NULL, FALSE);
4489 g_return_val_if_fail(scanpos != NULL, FALSE);
4490 g_return_val_if_fail(bp != NULL, FALSE);
4491 g_return_val_if_fail(ep != NULL, FALSE);
4494 const gchar *start_quote = NULL;
4495 const gchar *end_quote = NULL;
4497 /* go to the real start */
4498 if (start[0] == ',')
4500 if (start[0] == ';')
4502 while (start[0] == '\n' || start[0] == '\r')
4504 while (start[0] == ' ' || start[0] == '\t')
4509 /* check if there are quotes (to skip , in them) */
4510 if (*start == '"') {
4511 start_quote = start;
4513 end_quote = strstr(start, "\"");
4519 /* skip anything between quotes */
4520 if (start_quote && end_quote) {
4525 /* find end (either , or ; or end of line) */
4526 if (strstr(start, ",") && strstr(start, ";"))
4527 *ep = strstr(start,",") < strstr(start, ";")
4528 ? strstr(start, ",") : strstr(start, ";");
4529 else if (strstr(start, ","))
4530 *ep = strstr(start, ",");
4531 else if (strstr(start, ";"))
4532 *ep = strstr(start, ";");
4534 *ep = start+strlen(start);
4536 /* go back to real start */
4537 if (start_quote && end_quote) {
4538 start = start_quote;
4541 /* check there's still an @ in that, or search
4542 * further if possible */
4543 if (strstr(start, "@") && strstr(start, "@") < *ep)
4545 else if (*ep < start+strlen(start)) {
4548 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4556 dom_tab = create_domain_tab();
4557 g_return_val_if_fail(dom_tab, FALSE);
4559 /* scan start of address */
4560 for (bp_ = scanpos - 1;
4561 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4564 /* TODO: should start with an alnum? */
4566 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4569 if (bp_ != scanpos) {
4570 /* scan end of address */
4571 for (ep_ = scanpos + 1;
4572 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4574 prelast_dot = last_dot;
4576 if (*(last_dot + 1) == '.') {
4577 if (prelast_dot == NULL)
4579 last_dot = prelast_dot;
4584 /* TODO: really should terminate with an alnum? */
4585 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4590 if (last_dot == NULL)
4592 if (last_dot >= ep_)
4593 last_dot = prelast_dot;
4594 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4598 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4599 if (*last_tld_char == '?')
4602 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4609 if (!result) return FALSE;
4611 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4612 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4613 && IS_RFC822_CHAR(*(ep_ + 3))) {
4614 /* this informative part with an @ in it is
4615 * followed by the email address */
4618 /* go to matching '>' (or next non-rfc822 char, like \n) */
4619 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4622 /* include the bracket */
4623 if (*ep_ == '>') ep_++;
4625 /* include the leading quote */
4633 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4634 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4637 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4638 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4641 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4642 #define IN_STACK() (ptr > closure_stack)
4643 /* has underrun check */
4644 #define POP_STACK() if(IN_STACK()) --ptr
4645 /* has overrun check */
4646 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4647 /* has underrun check */
4648 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4652 /* scan for the informative part. */
4653 for (bp_ -= 2; bp_ >= start; bp_--) {
4654 /* if closure on the stack keep scanning */
4655 if (PEEK_STACK() == *bp_) {
4659 if (*bp_ == '\'' || *bp_ == '"') {
4664 /* if nothing in the closure stack, do the special conditions
4665 * the following if..else expression simply checks whether
4666 * a token is acceptable. if not acceptable, the clause
4667 * should terminate the loop with a 'break' */
4668 if (!PEEK_STACK()) {
4670 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4671 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4672 /* hyphens are allowed, but only in
4674 } else if (strchr(" \"'", *bp_)) {
4675 /* but anything not being a punctiation
4678 break; /* anything else is rejected */
4685 /* scan forward (should start with an alnum) */
4686 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4702 #undef IS_ASCII_ALNUM
4703 #undef IS_RFC822_CHAR
4705 gchar *make_email_string(const gchar *bp, const gchar *ep)
4707 /* returns a mailto: URI; mailto: is also used to detect the
4708 * uri type later on in the button_pressed signal handler */
4712 tmp = g_strndup(bp, ep - bp);
4713 result = g_strconcat("mailto:", tmp, NULL);
4719 gchar *make_http_string(const gchar *bp, const gchar *ep)
4721 /* returns an http: URI; */
4725 while (bp && *bp && g_ascii_isspace(*bp))
4727 tmp = g_strndup(bp, ep - bp);
4728 result = g_strconcat("http://", tmp, NULL);
4734 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4736 FILE *fp = fopen(path, "rb");
4737 gchar buf[BUFFSIZE];
4738 gchar *result = NULL;
4741 while (fgets(buf, sizeof (buf), fp) != NULL) {
4742 gchar **parts = g_strsplit(buf, ";", 3);
4743 gchar *trimmed = parts[0];
4744 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4746 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4747 trimmed[strlen(trimmed)-1] = '\0';
4749 if (!strcmp(trimmed, type)) {
4750 gboolean needsterminal = FALSE;
4751 if (parts[2] && strstr(parts[2], "needsterminal")) {
4752 needsterminal = TRUE;
4754 if (parts[2] && strstr(parts[2], "test=")) {
4755 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4756 gchar *testcmd = orig_testcmd;
4757 if (strstr(testcmd,";"))
4758 *(strstr(testcmd,";")) = '\0';
4759 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4761 while (testcmd[strlen(testcmd)-1] == '\n')
4762 testcmd[strlen(testcmd)-1] = '\0';
4763 while (testcmd[strlen(testcmd)-1] == '\r')
4764 testcmd[strlen(testcmd)-1] = '\0';
4765 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4766 testcmd[strlen(testcmd)-1] = '\0';
4768 if (strstr(testcmd, "%s")) {
4769 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4770 gint res = system(tmp);
4772 g_free(orig_testcmd);
4779 gint res = system(testcmd);
4780 g_free(orig_testcmd);
4790 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4792 while (trimmed[strlen(trimmed)-1] == '\n')
4793 trimmed[strlen(trimmed)-1] = '\0';
4794 while (trimmed[strlen(trimmed)-1] == '\r')
4795 trimmed[strlen(trimmed)-1] = '\0';
4796 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4797 trimmed[strlen(trimmed)-1] = '\0';
4798 result = g_strdup(trimmed);
4801 /* if there are no single quotes around %s, add them.
4802 * '.*%s.*' is ok, as in display 'png:%s'
4804 if (strstr(result, "%s")
4805 && !(strstr(result, "'") < strstr(result,"%s") &&
4806 strstr(strstr(result,"%s"), "'"))) {
4807 gchar *start = g_strdup(result);
4808 gchar *end = g_strdup(strstr(result, "%s")+2);
4810 *strstr(start, "%s") = '\0';
4811 tmp = g_strconcat(start,"'%s'",end, NULL);
4817 if (needsterminal) {
4818 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4829 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4831 gchar *result = NULL;
4835 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4836 result = mailcap_get_command_in_file(path, type, file_to_open);
4840 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4844 void mailcap_update_default(const gchar *type, const gchar *command)
4846 gchar *path = NULL, *outpath = NULL;
4847 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4848 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4849 FILE *fp = fopen(path, "rb");
4850 FILE *outfp = fopen(outpath, "wb");
4851 gchar buf[BUFFSIZE];
4859 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4860 gchar **parts = g_strsplit(buf, ";", 3);
4861 gchar *trimmed = parts[0];
4862 while (trimmed[0] == ' ')
4864 while (trimmed[strlen(trimmed)-1] == ' ')
4865 trimmed[strlen(trimmed)-1] = '\0';
4867 if (!strcmp(trimmed, type)) {
4876 fprintf(outfp, "%s; %s\n", type, command);
4882 g_rename(outpath, path);
4885 gint copy_dir(const gchar *src, const gchar *dst)
4890 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4891 g_warning("failed to open directory: %s\n", src);
4895 if (make_dir(dst) < 0)
4898 while ((name = g_dir_read_name(dir)) != NULL) {
4899 gchar *old_file, *new_file;
4900 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4901 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4902 debug_print("copying: %s -> %s\n", old_file, new_file);
4903 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4904 gint r = copy_file(old_file, new_file, TRUE);
4911 /* Windows has no symlinks. Or well, Vista seems to
4912 have something like this but the semantics might be
4913 different. Thus we don't use it under Windows. */
4914 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4917 gchar *target = g_file_read_link(old_file, &error);
4919 r = symlink(target, new_file);
4926 #endif /*G_OS_WIN32*/
4927 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4928 gint r = copy_dir(old_file, new_file);
4939 /* crude test to see if a file is an email. */
4940 gboolean file_is_email (const gchar *filename)
4946 if (filename == NULL)
4948 if ((fp = g_fopen(filename, "rb")) == NULL)
4950 while (i < 60 && score < 3
4951 && fgets(buffer, sizeof (buffer), fp) > 0) {
4952 if (!strncmp(buffer, "From:", strlen("From:")))
4954 if (!strncmp(buffer, "To:", strlen("To:")))
4956 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4961 return (score >= 3);
4964 gboolean sc_g_list_bigger(GList *list, gint max)
4968 while (cur && i <= max+1) {
4975 gboolean sc_g_slist_bigger(GSList *list, gint max)
4979 while (cur && i <= max+1) {
4986 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4987 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4988 NULL, NULL, NULL, NULL, NULL, NULL};
4989 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4990 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4991 NULL, NULL, NULL, NULL, NULL, NULL};
4992 const gchar *s_am_up = NULL;
4993 const gchar *s_pm_up = NULL;
4994 const gchar *s_am_low = NULL;
4995 const gchar *s_pm_low = NULL;
4996 const gchar *def_loc_format = NULL;
4997 const gchar *date_loc_format = NULL;
4998 const gchar *time_loc_format = NULL;
4999 const gchar *time_am_pm = NULL;
5001 static gboolean time_names_init_done = FALSE;
5003 static void init_time_names(void)
5005 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
5006 daynames[1] = Q_("Complete day name for use by strftime|Monday");
5007 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
5008 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
5009 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
5010 daynames[5] = Q_("Complete day name for use by strftime|Friday");
5011 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
5013 monthnames[0] = Q_("Complete month name for use by strftime|January");
5014 monthnames[1] = Q_("Complete month name for use by strftime|February");
5015 monthnames[2] = Q_("Complete month name for use by strftime|March");
5016 monthnames[3] = Q_("Complete month name for use by strftime|April");
5017 monthnames[4] = Q_("Complete month name for use by strftime|May");
5018 monthnames[5] = Q_("Complete month name for use by strftime|June");
5019 monthnames[6] = Q_("Complete month name for use by strftime|July");
5020 monthnames[7] = Q_("Complete month name for use by strftime|August");
5021 monthnames[8] = Q_("Complete month name for use by strftime|September");
5022 monthnames[9] = Q_("Complete month name for use by strftime|October");
5023 monthnames[10] = Q_("Complete month name for use by strftime|November");
5024 monthnames[11] = Q_("Complete month name for use by strftime|December");
5026 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
5027 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
5028 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
5029 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
5030 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
5031 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
5032 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
5034 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
5035 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
5036 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
5037 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
5038 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
5039 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
5040 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
5041 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
5042 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
5043 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
5044 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5045 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5047 s_am_up = Q_("For use by strftime (morning)|AM");
5048 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5049 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5050 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5052 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5053 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5054 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5056 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5058 time_names_init_done = TRUE;
5061 #define CHECK_SIZE() { \
5062 total_done += len; \
5063 if (total_done >= buflen) { \
5064 buf[buflen-1] = '\0'; \
5069 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5071 gchar *curpos = buf;
5072 gint total_done = 0;
5073 gchar subbuf[64], subfmt[64];
5074 static time_t last_tzset = (time_t)0;
5076 if (!time_names_init_done)
5079 if (format == NULL || lt == NULL)
5082 if (last_tzset != time(NULL)) {
5084 last_tzset = time(NULL);
5087 if (*format == '%') {
5088 gint len = 0, tmp = 0;
5092 len = 1; CHECK_SIZE();
5096 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5097 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5100 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5101 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5105 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5106 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5109 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5110 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5113 fast_strftime(subbuf, 64, def_loc_format, lt);
5114 len = strlen(subbuf); CHECK_SIZE();
5115 strncpy2(curpos, subbuf, buflen - total_done);
5118 total_done += 2; CHECK_SIZE();
5119 tmp = (lt->tm_year + 1900)/100;
5120 *curpos++ = '0'+(tmp / 10);
5121 *curpos++ = '0'+(tmp % 10);
5124 total_done += 2; CHECK_SIZE();
5125 *curpos++ = '0'+(lt->tm_mday / 10);
5126 *curpos++ = '0'+(lt->tm_mday % 10);
5129 total_done += 8; CHECK_SIZE();
5130 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5131 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5133 *curpos++ = '0'+(lt->tm_mday / 10);
5134 *curpos++ = '0'+(lt->tm_mday % 10);
5136 tmp = lt->tm_year%100;
5137 *curpos++ = '0'+(tmp / 10);
5138 *curpos++ = '0'+(tmp % 10);
5141 len = 2; CHECK_SIZE();
5142 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5145 len = 10; CHECK_SIZE();
5146 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5147 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5150 total_done += 2; CHECK_SIZE();
5151 *curpos++ = '0'+(lt->tm_hour / 10);
5152 *curpos++ = '0'+(lt->tm_hour % 10);
5155 total_done += 2; CHECK_SIZE();
5161 *curpos++ = '0'+(tmp / 10);
5162 *curpos++ = '0'+(tmp % 10);
5165 len = 3; CHECK_SIZE();
5166 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5169 len = 2; CHECK_SIZE();
5170 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5173 len = 2; CHECK_SIZE();
5179 snprintf(curpos, buflen - total_done, "%2d", tmp);
5182 total_done += 2; CHECK_SIZE();
5183 tmp = lt->tm_mon + 1;
5184 *curpos++ = '0'+(tmp / 10);
5185 *curpos++ = '0'+(tmp % 10);
5188 total_done += 2; CHECK_SIZE();
5189 *curpos++ = '0'+(lt->tm_min / 10);
5190 *curpos++ = '0'+(lt->tm_min % 10);
5193 len = 1; CHECK_SIZE();
5197 if (lt->tm_hour >= 12) {
5198 len = strlen(s_pm_up); CHECK_SIZE();
5199 snprintf(curpos, buflen-total_done, s_pm_up);
5201 len = strlen(s_am_up); CHECK_SIZE();
5202 snprintf(curpos, buflen-total_done, s_am_up);
5206 if (lt->tm_hour >= 12) {
5207 len = strlen(s_pm_low); CHECK_SIZE();
5208 snprintf(curpos, buflen-total_done, s_pm_low);
5210 len = strlen(s_am_low); CHECK_SIZE();
5211 snprintf(curpos, buflen-total_done, s_am_low);
5215 fast_strftime(subbuf, 64, time_am_pm, lt);
5216 len = strlen(subbuf); CHECK_SIZE();
5217 strncpy2(curpos, subbuf, buflen - total_done);
5220 total_done += 5; CHECK_SIZE();
5221 *curpos++ = '0'+(lt->tm_hour / 10);
5222 *curpos++ = '0'+(lt->tm_hour % 10);
5224 *curpos++ = '0'+(lt->tm_min / 10);
5225 *curpos++ = '0'+(lt->tm_min % 10);
5228 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5229 len = strlen(subbuf); CHECK_SIZE();
5230 strncpy2(curpos, subbuf, buflen - total_done);
5233 total_done += 2; CHECK_SIZE();
5234 *curpos++ = '0'+(lt->tm_sec / 10);
5235 *curpos++ = '0'+(lt->tm_sec % 10);
5238 len = 1; CHECK_SIZE();
5242 total_done += 8; CHECK_SIZE();
5243 *curpos++ = '0'+(lt->tm_hour / 10);
5244 *curpos++ = '0'+(lt->tm_hour % 10);
5246 *curpos++ = '0'+(lt->tm_min / 10);
5247 *curpos++ = '0'+(lt->tm_min % 10);
5249 *curpos++ = '0'+(lt->tm_sec / 10);
5250 *curpos++ = '0'+(lt->tm_sec % 10);
5253 len = 1; CHECK_SIZE();
5254 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5257 len = 1; CHECK_SIZE();
5258 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5261 fast_strftime(subbuf, 64, date_loc_format, lt);
5262 len = strlen(subbuf); CHECK_SIZE();
5263 strncpy2(curpos, subbuf, buflen - total_done);
5266 fast_strftime(subbuf, 64, time_loc_format, lt);
5267 len = strlen(subbuf); CHECK_SIZE();
5268 strncpy2(curpos, subbuf, buflen - total_done);
5271 total_done += 2; CHECK_SIZE();
5272 tmp = lt->tm_year%100;
5273 *curpos++ = '0'+(tmp / 10);
5274 *curpos++ = '0'+(tmp % 10);
5277 len = 4; CHECK_SIZE();
5278 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5288 /* let these complicated ones be done with the libc */
5289 snprintf(subfmt, 64, "%%%c", *format);
5290 strftime(subbuf, 64, subfmt, lt);
5291 len = strlen(subbuf); CHECK_SIZE();
5292 strncpy2(curpos, subbuf, buflen - total_done);
5296 /* let these complicated modifiers be done with the libc */
5297 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5298 strftime(subbuf, 64, subfmt, lt);
5299 len = strlen(subbuf); CHECK_SIZE();
5300 strncpy2(curpos, subbuf, buflen - total_done);
5304 g_warning("format error (%c)", *format);
5311 int len = 1; CHECK_SIZE();
5312 *curpos++ = *format++;