2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
46 #include <sys/types.h>
48 # include <sys/wait.h>
55 #include <sys/utsname.h>
66 #include "../codeconv.h"
70 static gboolean debug_mode = FALSE;
72 static GSList *tempfiles=NULL;
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
80 if (G_WIN32_HAVE_WIDECHAR_API()) {
85 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
91 retval = _wchdir(wpath);
103 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104 if (cp_path == NULL) {
109 retval = chdir(cp_path);
122 gint g_chmod(const gchar *path, gint mode)
125 if (G_WIN32_HAVE_WIDECHAR_API()) {
130 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
136 retval = _wchmod(wpath, mode);
148 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149 if (cp_path == NULL) {
154 retval = chmod(cp_path, mode);
163 return chmod(path, mode);
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
170 gint mkstemp_name(const gchar *template, gchar **name_used)
172 static gulong count=0; /* W32-_mktemp only supports up to 27
176 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
178 (S_IRUSR | S_IWUSR));
180 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
182 perror(g_strdup_printf("cant create %s",*name_used));
188 #endif /* G_OS_WIN32 */
191 gint mkstemp(const gchar *template)
194 gint res = mkstemp_name(template, &dummyname);
198 #endif /* G_OS_WIN32 */
200 void list_free_strings(GList *list)
202 list = g_list_first(list);
204 while (list != NULL) {
210 void slist_free_strings(GSList *list)
212 while (list != NULL) {
218 GSList *slist_concat_unique (GSList *first, GSList *second)
226 } else if (second == NULL)
229 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230 if (g_slist_find(ret, tmp->data) == NULL)
231 ret = g_slist_prepend(ret, tmp->data);
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
241 void hash_free_strings(GHashTable *table)
243 g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
252 void hash_free_value_mem(GHashTable *table)
254 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
259 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
262 guint str_case_hash(gconstpointer key)
264 const gchar *p = key;
268 h = g_ascii_tolower(h);
269 for (p += 1; *p != '\0'; p++)
270 h = (h << 5) - h + g_ascii_tolower(*p);
276 void ptr_array_free_strings(GPtrArray *array)
281 g_return_if_fail(array != NULL);
283 for (i = 0; i < array->len; i++) {
284 str = g_ptr_array_index(array, i);
289 gboolean str_find(const gchar *haystack, const gchar *needle)
291 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
296 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
301 return strcmp(haystack, needle) == 0;
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
306 return g_ascii_strcasecmp(haystack, needle) == 0;
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 #define divide(num,divisor,i,d) \
339 i = num >> divisor; \
340 d = num% (1 << divisor); \
341 d = (d*100) >> divisor; \
344 gchar *to_human_readable(off_t size)
346 static gchar str[14];
347 static gchar *b_format = NULL, *kb_format = NULL,
348 *mb_format = NULL, *gb_format = NULL;
349 register int t = 0, r = 0;
350 if (b_format == NULL) {
352 kb_format = _("%d.%02dKB");
353 mb_format = _("%d.%02dMB");
354 gb_format = _("%.2fGB");
358 g_snprintf(str, sizeof(str), b_format, (gint)size);
360 } else if (size >> 10 < 1024) {
361 divide(size, 10, t, r);
362 g_snprintf(str, sizeof(str), kb_format, t, r);
364 } else if (size >> 20 < 1024) {
365 divide(size, 20, t, r);
366 g_snprintf(str, sizeof(str), mb_format, t, r);
369 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
374 /* strcmp with NULL-checking */
375 gint strcmp2(const gchar *s1, const gchar *s2)
377 if (s1 == NULL || s2 == NULL)
380 return strcmp(s1, s2);
382 /* strstr with NULL-checking */
383 gchar *strstr2(const gchar *s1, const gchar *s2)
385 if (s1 == NULL || s2 == NULL)
388 return strstr(s1, s2);
391 gint path_cmp(const gchar *s1, const gchar *s2)
396 gchar *s1buf, *s2buf;
399 if (s1 == NULL || s2 == NULL) return -1;
400 if (*s1 == '\0' || *s2 == '\0') return -1;
403 s1buf = g_strdup (s1);
404 s2buf = g_strdup (s2);
405 subst_char (s1buf, '/', G_DIR_SEPARATOR);
406 subst_char (s2buf, '/', G_DIR_SEPARATOR);
409 #endif /* !G_OS_WIN32 */
414 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
415 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
417 rc = strncmp(s1, s2, MAX(len1, len2));
421 #endif /* !G_OS_WIN32 */
425 /* remove trailing return code */
426 gchar *strretchomp(gchar *str)
430 if (!*str) return str;
432 for (s = str + strlen(str) - 1;
433 s >= str && (*s == '\n' || *s == '\r');
440 /* remove trailing character */
441 gchar *strtailchomp(gchar *str, gchar tail_char)
445 if (!*str) return str;
446 if (tail_char == '\0') return str;
448 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
454 /* remove CR (carriage return) */
455 gchar *strcrchomp(gchar *str)
459 if (!*str) return str;
461 s = str + strlen(str) - 1;
462 if (*s == '\n' && s > str && *(s - 1) == '\r') {
470 void file_strip_crs(const gchar *file)
472 FILE *fp = NULL, *outfp = NULL;
474 gchar *out = get_tmp_file();
478 fp = fopen(file, "rb");
482 outfp = fopen(out, "wb");
488 while (fgets(buf, sizeof (buf), fp) != NULL) {
495 rename_force(out, file);
500 /* Similar to `strstr' but this function ignores the case of both strings. */
501 gchar *strcasestr(const gchar *haystack, const gchar *needle)
503 register size_t haystack_len, needle_len;
505 haystack_len = strlen(haystack);
506 needle_len = strlen(needle);
508 if (haystack_len < needle_len || needle_len == 0)
511 while (haystack_len >= needle_len) {
512 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
513 return (gchar *)haystack;
523 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
524 gconstpointer needle, size_t needlelen)
526 const gchar *haystack_ = (const gchar *)haystack;
527 const gchar *needle_ = (const gchar *)needle;
528 const gchar *haystack_cur = (const gchar *)haystack;
531 return memchr(haystack_, *needle_, haystacklen);
533 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
535 if (haystacklen - (haystack_cur - haystack_) < needlelen)
537 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
538 return (gpointer)haystack_cur;
546 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
547 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
549 register const gchar *s = src;
550 register gchar *d = dest;
560 /* Examine if next block is non-ASCII string */
561 gboolean is_next_nonascii(const gchar *s)
565 /* skip head space */
566 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
568 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
569 if (*(guchar *)p > 127 || *(guchar *)p < 32)
576 gint get_next_word_len(const gchar *s)
580 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
586 /* compare subjects */
587 gint subject_compare(const gchar *s1, const gchar *s2)
591 if (!s1 || !s2) return -1;
592 if (!*s1 || !*s2) return -1;
594 Xstrdup_a(str1, s1, return -1);
595 Xstrdup_a(str2, s2, return -1);
597 trim_subject_for_compare(str1);
598 trim_subject_for_compare(str2);
600 if (!*str1 || !*str2) return -1;
602 return strcmp(str1, str2);
605 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
609 if (!s1 || !s2) return -1;
611 Xstrdup_a(str1, s1, return -1);
612 Xstrdup_a(str2, s2, return -1);
614 trim_subject_for_sort(str1);
615 trim_subject_for_sort(str2);
617 return g_utf8_collate(str1, str2);
620 void trim_subject_for_compare(gchar *str)
624 eliminate_parenthesis(str, '[', ']');
625 eliminate_parenthesis(str, '(', ')');
628 srcp = str + subject_get_prefix_length(str);
630 memmove(str, srcp, strlen(srcp) + 1);
633 void trim_subject_for_sort(gchar *str)
639 srcp = str + subject_get_prefix_length(str);
641 memmove(str, srcp, strlen(srcp) + 1);
644 void trim_subject(gchar *str)
646 register gchar *srcp;
652 srcp = str + subject_get_prefix_length(str);
657 } else if (*srcp == '(') {
669 else if (*srcp == cl)
676 while (g_ascii_isspace(*srcp)) srcp++;
677 memmove(str, srcp, strlen(srcp) + 1);
680 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
682 register gchar *srcp, *destp;
687 while ((destp = strchr(destp, op))) {
693 else if (*srcp == cl)
699 while (g_ascii_isspace(*srcp)) srcp++;
700 memmove(destp, srcp, strlen(srcp) + 1);
704 void extract_parenthesis(gchar *str, gchar op, gchar cl)
706 register gchar *srcp, *destp;
711 while ((srcp = strchr(destp, op))) {
714 memmove(destp, srcp + 1, strlen(srcp));
719 else if (*destp == cl)
731 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
734 register gchar *srcp, *destp;
736 gboolean in_quote = FALSE;
740 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
746 if (*destp == op && !in_quote)
748 else if (*destp == cl && !in_quote)
750 else if (*destp == quote_chr)
762 void eliminate_quote(gchar *str, gchar quote_chr)
764 register gchar *srcp, *destp;
768 while ((destp = strchr(destp, quote_chr))) {
769 if ((srcp = strchr(destp + 1, quote_chr))) {
771 while (g_ascii_isspace(*srcp)) srcp++;
772 memmove(destp, srcp, strlen(srcp) + 1);
780 void extract_quote(gchar *str, gchar quote_chr)
784 if ((str = strchr(str, quote_chr))) {
786 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
787 memmove(p - 1, p, strlen(p) + 1);
792 memmove(str, str + 1, p - str);
797 void eliminate_address_comment(gchar *str)
799 register gchar *srcp, *destp;
804 while ((destp = strchr(destp, '"'))) {
805 if ((srcp = strchr(destp + 1, '"'))) {
810 while (g_ascii_isspace(*srcp)) srcp++;
811 memmove(destp, srcp, strlen(srcp) + 1);
821 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
827 else if (*srcp == ')')
833 while (g_ascii_isspace(*srcp)) srcp++;
834 memmove(destp, srcp, strlen(srcp) + 1);
838 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
840 gboolean in_quote = FALSE;
843 if (*str == c && !in_quote)
845 if (*str == quote_chr)
853 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
855 gboolean in_quote = FALSE;
858 p = str + strlen(str) - 1;
860 if (*p == c && !in_quote)
870 void extract_address(gchar *str)
872 eliminate_address_comment(str);
873 if (strchr_with_skip_quote(str, '"', '<'))
874 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
878 void extract_list_id_str(gchar *str)
880 if (strchr_with_skip_quote(str, '"', '<'))
881 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
885 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
890 if (!str) return addr_list;
892 Xstrdup_a(work, str, return addr_list);
895 eliminate_address_comment(work);
898 while (workp && *workp) {
901 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
907 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
908 extract_parenthesis_with_skip_quote
909 (workp, '"', '<', '>');
913 addr_list = g_slist_append(addr_list, g_strdup(workp));
921 GSList *address_list_append(GSList *addr_list, const gchar *str)
923 return address_list_append_real(addr_list, str, TRUE);
926 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
928 return address_list_append_real(addr_list, str, FALSE);
931 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
935 if (!str) return msgid_list;
938 while (strp && *strp) {
939 const gchar *start, *end;
942 if ((start = strchr(strp, '<')) != NULL) {
943 end = strchr(start + 1, '>');
948 msgid = g_strndup(start + 1, end - start - 1);
951 msgid_list = g_slist_prepend(msgid_list, msgid);
961 GSList *references_list_append(GSList *msgid_list, const gchar *str)
965 list = references_list_prepend(NULL, str);
966 list = g_slist_reverse(list);
967 msgid_list = g_slist_concat(msgid_list, list);
972 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
977 if (!str) return group_list;
979 Xstrdup_a(work, str, return group_list);
983 while (workp && *workp) {
986 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
994 group_list = g_slist_append(group_list,
1003 GList *add_history(GList *list, const gchar *str)
1007 g_return_val_if_fail(str != NULL, list);
1009 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1012 list = g_list_remove(list, old->data);
1013 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1016 last = g_list_last(list);
1019 list = g_list_remove(list, last->data);
1023 list = g_list_prepend(list, g_strdup(str));
1028 void remove_return(gchar *str)
1030 register gchar *p = str;
1033 if (*p == '\n' || *p == '\r')
1034 memmove(p, p + 1, strlen(p));
1040 void remove_space(gchar *str)
1042 register gchar *p = str;
1047 while (g_ascii_isspace(*(p + spc)))
1050 memmove(p, p + spc, strlen(p + spc) + 1);
1056 void unfold_line(gchar *str)
1058 register gchar *p = str;
1062 if (*p == '\n' || *p == '\r') {
1065 while (g_ascii_isspace(*(p + spc)))
1068 memmove(p, p + spc, strlen(p + spc) + 1);
1074 void subst_char(gchar *str, gchar orig, gchar subst)
1076 register gchar *p = str;
1085 void subst_chars(gchar *str, gchar *orig, gchar subst)
1087 register gchar *p = str;
1090 if (strchr(orig, *p) != NULL)
1096 void subst_for_filename(gchar *str)
1101 subst_chars(str, "\t\r\n\\/*:", '_');
1103 subst_chars(str, "\t\r\n\\/*", '_');
1107 void subst_for_shellsafe_filename(gchar *str)
1111 subst_for_filename(str);
1112 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1115 gboolean is_header_line(const gchar *str)
1117 if (str[0] == ':') return FALSE;
1119 while (*str != '\0' && *str != ' ') {
1128 gboolean is_ascii_str(const gchar *str)
1130 const guchar *p = (const guchar *)str;
1132 while (*p != '\0') {
1133 if (*p != '\t' && *p != ' ' &&
1134 *p != '\r' && *p != '\n' &&
1135 (*p < 32 || *p >= 127))
1143 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1145 const gchar *first_pos;
1146 const gchar *last_pos;
1147 const gchar *p = str;
1148 gint quote_level = -1;
1150 /* speed up line processing by only searching to the last '>' */
1151 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1152 /* skip a line if it contains a '<' before the initial '>' */
1153 if (memchr(str, '<', first_pos - str) != NULL)
1155 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1159 while (p <= last_pos) {
1160 while (p < last_pos) {
1161 if (g_ascii_isspace(*p))
1167 if (strchr(quote_chars, *p))
1169 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1170 /* any characters are allowed except '-' and space */
1172 && !strchr(quote_chars, *p)
1173 && !g_ascii_isspace(*p)
1176 if (strchr(quote_chars, *p))
1188 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1190 const gchar *p = str, *q;
1191 gint cur_line = 0, len;
1193 while ((q = strchr(p, '\n')) != NULL) {
1195 if (len > max_chars) {
1205 if (len > max_chars) {
1214 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1216 gchar * position = NULL;
1217 gchar * tmp_pos = NULL;
1220 if (quote_chars == NULL)
1223 for (i = 0; i < strlen(quote_chars); i++) {
1224 tmp_pos = strchr (str, quote_chars[i]);
1226 || (tmp_pos != NULL && position >= tmp_pos) )
1232 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1234 gchar * position = NULL;
1235 gchar * tmp_pos = NULL;
1238 if (quote_chars == NULL)
1241 for (i = 0; i < strlen(quote_chars); i++) {
1242 tmp_pos = strrchr (str, quote_chars[i]);
1244 || (tmp_pos != NULL && position <= tmp_pos) )
1250 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1252 register guint haystack_len, needle_len;
1253 gboolean in_squote = FALSE, in_dquote = FALSE;
1255 haystack_len = strlen(haystack);
1256 needle_len = strlen(needle);
1258 if (haystack_len < needle_len || needle_len == 0)
1261 while (haystack_len >= needle_len) {
1262 if (!in_squote && !in_dquote &&
1263 !strncmp(haystack, needle, needle_len))
1264 return (gchar *)haystack;
1266 /* 'foo"bar"' -> foo"bar"
1267 "foo'bar'" -> foo'bar' */
1268 if (*haystack == '\'') {
1271 else if (!in_dquote)
1273 } else if (*haystack == '\"') {
1276 else if (!in_squote)
1287 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1290 gchar quote_chr = '"';
1292 gboolean in_quote = FALSE;
1296 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1300 if (*p == op && !in_quote)
1302 else if (*p == cl && !in_quote)
1304 else if (*p == quote_chr)
1317 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1320 GSList *string_list = NULL, *slist;
1322 const gchar *s_op, *s_cl;
1325 g_return_val_if_fail(str != NULL, NULL);
1328 max_tokens = G_MAXINT;
1330 s_op = strchr_with_skip_quote(str, '"', op);
1331 if (!s_op) return NULL;
1333 s_cl = strchr_parenthesis_close(str, op, cl);
1341 new_string = g_new(gchar, len + 1);
1342 strncpy(new_string, str, len);
1343 new_string[len] = 0;
1344 string_list = g_slist_prepend(string_list, new_string);
1348 while (*str && g_ascii_isspace(*str)) str++;
1350 string_list = g_slist_prepend(string_list,
1353 s_op = strchr_with_skip_quote(str, '"', op);
1354 if (!--max_tokens || !s_op) break;
1358 s_cl = strchr_parenthesis_close(str, op, cl);
1359 } while (--max_tokens && s_cl);
1362 str_array = g_new(gchar*, n);
1366 str_array[i--] = NULL;
1367 for (slist = string_list; slist; slist = slist->next)
1368 str_array[i--] = slist->data;
1370 g_slist_free(string_list);
1375 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1378 GSList *string_list = NULL, *slist;
1379 gchar **str_array, *s, *new_str;
1380 guint i, n = 1, len;
1382 g_return_val_if_fail(str != NULL, NULL);
1383 g_return_val_if_fail(delim != NULL, NULL);
1386 max_tokens = G_MAXINT;
1388 s = strstr_with_skip_quote(str, delim);
1390 guint delimiter_len = strlen(delim);
1394 new_str = g_strndup(str, len);
1396 if (new_str[0] == '\'' || new_str[0] == '\"') {
1397 if (new_str[len - 1] == new_str[0]) {
1398 new_str[len - 1] = '\0';
1399 memmove(new_str, new_str + 1, len - 1);
1402 string_list = g_slist_prepend(string_list, new_str);
1404 str = s + delimiter_len;
1405 s = strstr_with_skip_quote(str, delim);
1406 } while (--max_tokens && s);
1410 new_str = g_strdup(str);
1411 if (new_str[0] == '\'' || new_str[0] == '\"') {
1413 if (new_str[len - 1] == new_str[0]) {
1414 new_str[len - 1] = '\0';
1415 memmove(new_str, new_str + 1, len - 1);
1418 string_list = g_slist_prepend(string_list, new_str);
1422 str_array = g_new(gchar*, n);
1426 str_array[i--] = NULL;
1427 for (slist = string_list; slist; slist = slist->next)
1428 str_array[i--] = slist->data;
1430 g_slist_free(string_list);
1435 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1437 gchar *abbrev_group;
1439 const gchar *p = group;
1442 g_return_val_if_fail(group != NULL, NULL);
1444 last = group + strlen(group);
1445 abbrev_group = ap = g_malloc(strlen(group) + 1);
1450 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1452 while (*p != '.') p++;
1455 return abbrev_group;
1460 return abbrev_group;
1463 gchar *trim_string(const gchar *str, gint len)
1465 const gchar *p = str;
1470 if (!str) return NULL;
1471 if (strlen(str) <= len)
1472 return g_strdup(str);
1473 if (g_utf8_validate(str, -1, NULL) == FALSE)
1474 return g_strdup(str);
1476 while (*p != '\0') {
1477 mb_len = g_utf8_skip[*(guchar *)p];
1480 else if (new_len + mb_len > len)
1487 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1488 return g_strconcat(new_str, "...", NULL);
1491 GList *uri_list_extract_filenames(const gchar *uri_list)
1493 GList *result = NULL;
1495 gchar *escaped_utf8uri;
1501 while (g_ascii_isspace(*p)) p++;
1502 if (!strncmp(p, "file:", 5)) {
1505 while (*q && *q != '\n' && *q != '\r') q++;
1508 gchar *file, *locale_file = NULL;
1510 while (q > p && g_ascii_isspace(*q))
1512 Xalloca(escaped_utf8uri, q - p + 2,
1514 Xalloca(file, q - p + 2,
1517 strncpy(escaped_utf8uri, p, q - p + 1);
1518 escaped_utf8uri[q - p + 1] = '\0';
1519 decode_uri(file, escaped_utf8uri);
1521 * g_filename_from_uri() rejects escaped/locale encoded uri
1522 * string which come from Nautilus.
1524 if (g_utf8_validate(file, -1, NULL))
1526 = conv_codeset_strdup(
1529 conv_get_locale_charset_str());
1531 locale_file = g_strdup(file + 5);
1532 result = g_list_append(result, locale_file);
1536 p = strchr(p, '\n');
1543 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1546 static gint axtoi(const gchar *hexstr)
1548 gint hi, lo, result;
1551 if ('0' <= hi && hi <= '9') {
1554 if ('a' <= hi && hi <= 'f') {
1557 if ('A' <= hi && hi <= 'F') {
1562 if ('0' <= lo && lo <= '9') {
1565 if ('a' <= lo && lo <= 'f') {
1568 if ('A' <= lo && lo <= 'F') {
1571 result = lo + (16 * hi);
1575 gboolean is_uri_string(const gchar *str)
1577 while (str && *str && g_ascii_isspace(*str))
1579 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1580 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1581 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1582 g_ascii_strncasecmp(str, "www.", 4) == 0);
1585 gchar *get_uri_path(const gchar *uri)
1587 while (uri && *uri && g_ascii_isspace(*uri))
1589 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1590 return (gchar *)(uri + 7);
1591 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1592 return (gchar *)(uri + 8);
1593 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1594 return (gchar *)(uri + 6);
1596 return (gchar *)uri;
1599 gint get_uri_len(const gchar *str)
1603 if (is_uri_string(str)) {
1604 for (p = str; *p != '\0'; p++) {
1605 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1614 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1615 * plusses, and escape characters are used)
1617 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1619 gchar *dec = decoded_uri;
1620 const gchar *enc = encoded_uri;
1625 if (isxdigit((guchar)enc[0]) &&
1626 isxdigit((guchar)enc[1])) {
1644 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1645 gchar **subject, gchar **body)
1650 Xstrdup_a(tmp_mailto, mailto, return -1);
1652 if (!strncmp(tmp_mailto, "mailto:", 7))
1655 p = strchr(tmp_mailto, '?');
1662 *to = g_strdup(tmp_mailto);
1665 gchar *field, *value;
1682 if (*value == '\0') continue;
1684 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1685 *cc = g_strdup(value);
1686 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1687 *bcc = g_strdup(value);
1688 } else if (subject && !*subject &&
1689 !g_ascii_strcasecmp(field, "subject")) {
1690 *subject = g_malloc(strlen(value) + 1);
1691 decode_uri(*subject, value);
1692 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1693 *body = g_malloc(strlen(value) + 1);
1694 decode_uri(*body, value);
1703 #include <windows.h>
1704 #ifndef CSIDL_APPDATA
1705 #define CSIDL_APPDATA 0x001a
1707 #ifndef CSIDL_LOCAL_APPDATA
1708 #define CSIDL_LOCAL_APPDATA 0x001c
1710 #ifndef CSIDL_FLAG_CREATE
1711 #define CSIDL_FLAG_CREATE 0x8000
1713 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1717 w32_strerror (int w32_errno)
1719 static char strerr[256];
1720 int ec = (int)GetLastError ();
1724 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1725 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1726 strerr, DIM (strerr)-1, NULL);
1730 static __inline__ void *
1731 dlopen (const char * name, int flag)
1733 void * hd = LoadLibrary (name);
1737 static __inline__ void *
1738 dlsym (void * hd, const char * sym)
1742 void * fnc = GetProcAddress (hd, sym);
1751 static __inline__ const char *
1754 return w32_strerror (0);
1758 static __inline__ int
1770 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1772 static int initialized;
1773 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1777 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1783 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1785 handle = dlopen (dllnames[i], RTLD_LAZY);
1788 func = dlsym (handle, "SHGetFolderPathA");
1799 return func (a,b,c,d,e);
1804 /* Returns a static string with the directroy from which the module
1805 has been loaded. Returns an empty string on error. */
1806 static char *w32_get_module_dir(void)
1808 static char *moddir;
1811 char name[MAX_PATH+10];
1814 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1817 p = strrchr (name, '\\');
1823 moddir = g_strdup (name);
1827 #endif /* G_OS_WIN32 */
1829 /* Return a static string with the locale dir. */
1830 const gchar *get_locale_dir(void)
1832 static gchar *loc_dir;
1836 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1837 "\\share\\locale", NULL);
1840 loc_dir = LOCALEDIR;
1846 const gchar *get_home_dir(void)
1849 static char home_dir[MAX_PATH] = "";
1851 if (home_dir[0] == '\0') {
1852 if (w32_shgetfolderpath
1853 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1854 NULL, 0, home_dir) < 0)
1855 strcpy (home_dir, "C:\\Sylpheed");
1859 static const gchar *homeenv = NULL;
1864 if (!homeenv && g_getenv("HOME") != NULL)
1865 homeenv = g_strdup(g_getenv("HOME"));
1867 homeenv = g_get_home_dir();
1873 const gchar *get_rc_dir(void)
1875 static gchar *rc_dir = NULL;
1878 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1884 const gchar *get_mail_base_dir(void)
1887 static gchar *mail_base_dir = NULL;
1890 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1893 return mail_base_dir;
1895 return get_home_dir();
1899 const gchar *get_news_cache_dir(void)
1901 static gchar *news_cache_dir = NULL;
1903 if (!news_cache_dir)
1904 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1905 NEWS_CACHE_DIR, NULL);
1907 return news_cache_dir;
1910 const gchar *get_imap_cache_dir(void)
1912 static gchar *imap_cache_dir = NULL;
1914 if (!imap_cache_dir)
1915 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 IMAP_CACHE_DIR, NULL);
1918 return imap_cache_dir;
1921 const gchar *get_mbox_cache_dir(void)
1923 static gchar *mbox_cache_dir = NULL;
1925 if (!mbox_cache_dir)
1926 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1927 MBOX_CACHE_DIR, NULL);
1929 return mbox_cache_dir;
1932 const gchar *get_mime_tmp_dir(void)
1934 static gchar *mime_tmp_dir = NULL;
1937 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1938 MIME_TMP_DIR, NULL);
1940 return mime_tmp_dir;
1943 const gchar *get_template_dir(void)
1945 static gchar *template_dir = NULL;
1948 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1949 TEMPLATE_DIR, NULL);
1951 return template_dir;
1954 const gchar *get_header_cache_dir(void)
1956 static gchar *header_dir = NULL;
1959 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1960 HEADER_CACHE_DIR, NULL);
1965 /* Return the default directory for Plugins. */
1966 const gchar *get_plugin_dir(void)
1969 static gchar *plugin_dir = NULL;
1972 plugin_dir = g_strconcat(w32_get_module_dir(),
1973 "\\lib\\sylpheed-claws\\plugins\\",
1977 if (is_dir_exist(PLUGINDIR))
1980 static gchar *plugin_dir = NULL;
1982 plugin_dir = g_strconcat(get_rc_dir(),
1983 G_DIR_SEPARATOR_S, "plugins",
1984 G_DIR_SEPARATOR_S, NULL);
1990 const gchar *get_tmp_dir(void)
1992 static gchar *tmp_dir = NULL;
1995 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2001 gchar *get_tmp_file(void)
2004 static guint32 id = 0;
2006 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2007 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2012 const gchar *get_domain_name(void)
2015 static gchar *domain_name = NULL;
2021 if (uname(&uts) < 0) {
2023 domain_name = "unknown";
2025 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2026 perror("gethostbyname");
2027 domain_name = g_strdup(uts.nodename);
2029 domain_name = g_strdup(hp->h_name);
2033 debug_print("domain name = %s\n", domain_name);
2042 off_t get_file_size(const gchar *file)
2046 if (g_stat(file, &s) < 0) {
2047 FILE_OP_ERROR(file, "stat");
2054 off_t get_file_size_as_crlf(const gchar *file)
2058 gchar buf[BUFFSIZE];
2060 if ((fp = g_fopen(file, "rb")) == NULL) {
2061 FILE_OP_ERROR(file, "fopen");
2065 while (fgets(buf, sizeof(buf), fp) != NULL) {
2067 size += strlen(buf) + 2;
2071 FILE_OP_ERROR(file, "fgets");
2080 off_t get_left_file_size(FILE *fp)
2086 if ((pos = ftell(fp)) < 0) {
2090 if (fseek(fp, 0L, SEEK_END) < 0) {
2094 if ((end = ftell(fp)) < 0) {
2099 if (fseek(fp, pos, SEEK_SET) < 0) {
2107 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2114 if (g_stat(file, &s) < 0) {
2115 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2119 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2126 /* Test on whether FILE is a relative file name. This is
2127 * straightforward for Unix but more complex for Windows. */
2128 gboolean is_relative_filename(const gchar *file)
2133 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2134 return FALSE; /* Prefixed with a hostname - this can't
2135 * be a relative name. */
2137 if ( ((*file >= 'a' && *file <= 'z')
2138 || (*file >= 'A' && *file <= 'Z'))
2140 file += 2; /* Skip drive letter. */
2142 return !(*file == '\\' || *file == '/');
2144 return !(*file == G_DIR_SEPARATOR);
2149 gboolean is_dir_exist(const gchar *dir)
2154 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2157 gboolean is_file_entry_exist(const gchar *file)
2162 return g_file_test(file, G_FILE_TEST_EXISTS);
2165 gboolean dirent_is_regular_file(struct dirent *d)
2167 #ifdef HAVE_DIRENT_D_TYPE
2168 if (d->d_type == DT_REG)
2170 else if (d->d_type != DT_UNKNOWN)
2174 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2177 gboolean dirent_is_directory(struct dirent *d)
2179 #ifdef HAVE_DIRENT_D_TYPE
2180 if (d->d_type == DT_DIR)
2182 else if (d->d_type != DT_UNKNOWN)
2186 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2189 gint change_dir(const gchar *dir)
2191 gchar *prevdir = NULL;
2194 prevdir = g_get_current_dir();
2196 if (g_chdir(dir) < 0) {
2197 FILE_OP_ERROR(dir, "chdir");
2198 if (debug_mode) g_free(prevdir);
2200 } else if (debug_mode) {
2203 cwd = g_get_current_dir();
2204 if (strcmp(prevdir, cwd) != 0)
2205 g_print("current dir: %s\n", cwd);
2213 gint make_dir(const gchar *dir)
2215 if (g_mkdir(dir, S_IRWXU) < 0) {
2216 FILE_OP_ERROR(dir, "mkdir");
2219 if (g_chmod(dir, S_IRWXU) < 0)
2220 FILE_OP_ERROR(dir, "chmod");
2225 gint make_dir_hier(const gchar *dir)
2230 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2231 parent_dir = g_strndup(dir, p - dir);
2232 if (*parent_dir != '\0') {
2233 if (!is_dir_exist(parent_dir)) {
2234 if (make_dir(parent_dir) < 0) {
2243 if (!is_dir_exist(dir)) {
2244 if (make_dir(dir) < 0)
2251 gint remove_all_files(const gchar *dir)
2254 const gchar *dir_name;
2257 prev_dir = g_get_current_dir();
2259 if (g_chdir(dir) < 0) {
2260 FILE_OP_ERROR(dir, "chdir");
2265 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2266 g_warning("failed to open directory: %s\n", dir);
2271 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2272 if (g_unlink(dir_name) < 0)
2273 FILE_OP_ERROR(dir_name, "unlink");
2278 if (g_chdir(prev_dir) < 0) {
2279 FILE_OP_ERROR(prev_dir, "chdir");
2289 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2292 const gchar *dir_name;
2296 prev_dir = g_get_current_dir();
2298 if (g_chdir(dir) < 0) {
2299 FILE_OP_ERROR(dir, "chdir");
2304 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2305 g_warning("failed to open directory: %s\n", dir);
2310 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2311 file_no = to_number(dir_name);
2312 if (file_no > 0 && first <= file_no && file_no <= last) {
2313 if (is_dir_exist(dir_name))
2315 if (g_unlink(dir_name) < 0)
2316 FILE_OP_ERROR(dir_name, "unlink");
2322 if (g_chdir(prev_dir) < 0) {
2323 FILE_OP_ERROR(prev_dir, "chdir");
2333 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2336 const gchar *dir_name;
2340 prev_dir = g_get_current_dir();
2342 if (g_chdir(dir) < 0) {
2343 FILE_OP_ERROR(dir, "chdir");
2348 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2349 FILE_OP_ERROR(dir, "opendir");
2354 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2355 file_no = to_number(dir_name);
2356 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2357 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2358 if (is_dir_exist(dir_name))
2360 if (g_unlink(dir_name) < 0)
2361 FILE_OP_ERROR(dir_name, "unlink");
2367 if (g_chdir(prev_dir) < 0) {
2368 FILE_OP_ERROR(prev_dir, "chdir");
2378 gint remove_all_numbered_files(const gchar *dir)
2380 return remove_numbered_files(dir, 0, UINT_MAX);
2383 gint remove_expired_files(const gchar *dir, guint hours)
2386 const gchar *dir_name;
2390 time_t mtime, now, expire_time;
2392 prev_dir = g_get_current_dir();
2394 if (g_chdir(dir) < 0) {
2395 FILE_OP_ERROR(dir, "chdir");
2400 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2401 g_warning("failed to open directory: %s\n", dir);
2407 expire_time = hours * 60 * 60;
2409 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2410 file_no = to_number(dir_name);
2412 if (g_stat(dir_name, &s) < 0) {
2413 FILE_OP_ERROR(dir_name, "stat");
2416 if (S_ISDIR(s.st_mode))
2418 mtime = MAX(s.st_mtime, s.st_atime);
2419 if (now - mtime > expire_time) {
2420 if (g_unlink(dir_name) < 0)
2421 FILE_OP_ERROR(dir_name, "unlink");
2428 if (g_chdir(prev_dir) < 0) {
2429 FILE_OP_ERROR(prev_dir, "chdir");
2439 gint remove_dir_recursive(const gchar *dir)
2443 const gchar *dir_name;
2446 if (g_stat(dir, &s) < 0) {
2447 FILE_OP_ERROR(dir, "stat");
2448 if (ENOENT == errno) return 0;
2452 if (!S_ISDIR(s.st_mode)) {
2453 if (g_unlink(dir) < 0) {
2454 FILE_OP_ERROR(dir, "unlink");
2461 prev_dir = g_get_current_dir();
2462 /* g_print("prev_dir = %s\n", prev_dir); */
2464 if (!path_cmp(prev_dir, dir)) {
2466 if (g_chdir("..") < 0) {
2467 FILE_OP_ERROR(dir, "chdir");
2470 prev_dir = g_get_current_dir();
2473 if (g_chdir(dir) < 0) {
2474 FILE_OP_ERROR(dir, "chdir");
2479 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2480 g_warning("failed to open directory: %s\n", dir);
2486 /* remove all files in the directory */
2487 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2488 /* g_print("removing %s\n", dir_name); */
2490 if (is_dir_exist(dir_name)) {
2491 if (remove_dir_recursive(dir_name) < 0) {
2492 g_warning("can't remove directory\n");
2496 if (g_unlink(dir_name) < 0)
2497 FILE_OP_ERROR(dir_name, "unlink");
2503 if (g_chdir(prev_dir) < 0) {
2504 FILE_OP_ERROR(prev_dir, "chdir");
2511 if (g_rmdir(dir) < 0) {
2512 FILE_OP_ERROR(dir, "rmdir");
2519 gint rename_force(const gchar *oldpath, const gchar *newpath)
2522 if (!is_file_entry_exist(oldpath)) {
2526 if (is_file_exist(newpath)) {
2527 if (g_unlink(newpath) < 0)
2528 FILE_OP_ERROR(newpath, "unlink");
2531 return g_rename(oldpath, newpath);
2535 * Append src file body to the tail of dest file.
2536 * Now keep_backup has no effects.
2538 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2540 FILE *src_fp, *dest_fp;
2544 gboolean err = FALSE;
2546 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2547 FILE_OP_ERROR(src, "fopen");
2551 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2552 FILE_OP_ERROR(dest, "fopen");
2557 if (change_file_mode_rw(dest_fp, dest) < 0) {
2558 FILE_OP_ERROR(dest, "chmod");
2559 g_warning("can't change file mode\n");
2562 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2563 if (n_read < sizeof(buf) && ferror(src_fp))
2565 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2566 g_warning("writing to %s failed.\n", dest);
2574 if (ferror(src_fp)) {
2575 FILE_OP_ERROR(src, "fread");
2579 if (fclose(dest_fp) == EOF) {
2580 FILE_OP_ERROR(dest, "fclose");
2592 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2594 FILE *src_fp, *dest_fp;
2597 gchar *dest_bak = NULL;
2598 gboolean err = FALSE;
2600 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2601 FILE_OP_ERROR(src, "fopen");
2604 if (is_file_exist(dest)) {
2605 dest_bak = g_strconcat(dest, ".bak", NULL);
2606 if (rename_force(dest, dest_bak) < 0) {
2607 FILE_OP_ERROR(dest, "rename");
2614 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2615 FILE_OP_ERROR(dest, "fopen");
2618 if (rename_force(dest_bak, dest) < 0)
2619 FILE_OP_ERROR(dest_bak, "rename");
2625 if (change_file_mode_rw(dest_fp, dest) < 0) {
2626 FILE_OP_ERROR(dest, "chmod");
2627 g_warning("can't change file mode\n");
2630 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2631 if (n_read < sizeof(buf) && ferror(src_fp))
2633 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2634 g_warning("writing to %s failed.\n", dest);
2639 if (rename_force(dest_bak, dest) < 0)
2640 FILE_OP_ERROR(dest_bak, "rename");
2647 if (ferror(src_fp)) {
2648 FILE_OP_ERROR(src, "fread");
2652 if (fclose(dest_fp) == EOF) {
2653 FILE_OP_ERROR(dest, "fclose");
2660 if (rename_force(dest_bak, dest) < 0)
2661 FILE_OP_ERROR(dest_bak, "rename");
2667 if (keep_backup == FALSE && dest_bak)
2675 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2677 if (overwrite == FALSE && is_file_exist(dest)) {
2678 g_warning("move_file(): file %s already exists.", dest);
2682 if (rename_force(src, dest) == 0) return 0;
2684 if (EXDEV != errno) {
2685 FILE_OP_ERROR(src, "rename");
2689 if (copy_file(src, dest, FALSE) < 0) return -1;
2696 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2699 gint bytes_left, to_read;
2702 if (fseek(fp, offset, SEEK_SET) < 0) {
2707 bytes_left = length;
2708 to_read = MIN(bytes_left, sizeof(buf));
2710 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2711 if (n_read < to_read && ferror(fp))
2713 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2716 bytes_left -= n_read;
2717 if (bytes_left == 0)
2719 to_read = MIN(bytes_left, sizeof(buf));
2730 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2733 gboolean err = FALSE;
2735 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2736 FILE_OP_ERROR(dest, "fopen");
2740 if (change_file_mode_rw(dest_fp, dest) < 0) {
2741 FILE_OP_ERROR(dest, "chmod");
2742 g_warning("can't change file mode\n");
2745 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2748 if (!err && fclose(dest_fp) == EOF) {
2749 FILE_OP_ERROR(dest, "fclose");
2754 g_warning("writing to %s failed.\n", dest);
2762 /* convert line endings into CRLF. If the last line doesn't end with
2763 * linebreak, add it.
2765 gchar *canonicalize_str(const gchar *str)
2771 for (p = str; *p != '\0'; ++p) {
2778 if (p == str || *(p - 1) != '\n')
2781 out = outp = g_malloc(new_len + 1);
2782 for (p = str; *p != '\0'; ++p) {
2789 if (p == str || *(p - 1) != '\n') {
2798 gint canonicalize_file(const gchar *src, const gchar *dest)
2800 FILE *src_fp, *dest_fp;
2801 gchar buf[BUFFSIZE];
2803 gboolean err = FALSE;
2804 gboolean last_linebreak = FALSE;
2806 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2807 FILE_OP_ERROR(src, "fopen");
2811 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2812 FILE_OP_ERROR(dest, "fopen");
2817 if (change_file_mode_rw(dest_fp, dest) < 0) {
2818 FILE_OP_ERROR(dest, "chmod");
2819 g_warning("can't change file mode\n");
2822 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2826 if (len == 0) break;
2827 last_linebreak = FALSE;
2829 if (buf[len - 1] != '\n') {
2830 last_linebreak = TRUE;
2831 r = fputs(buf, dest_fp);
2832 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2833 r = fputs(buf, dest_fp);
2836 r = fwrite(buf, 1, len - 1, dest_fp);
2841 r = fputs("\r\n", dest_fp);
2845 g_warning("writing to %s failed.\n", dest);
2853 if (last_linebreak == TRUE) {
2854 if (fputs("\r\n", dest_fp) == EOF)
2858 if (ferror(src_fp)) {
2859 FILE_OP_ERROR(src, "fgets");
2863 if (fclose(dest_fp) == EOF) {
2864 FILE_OP_ERROR(dest, "fclose");
2876 gint canonicalize_file_replace(const gchar *file)
2880 tmp_file = get_tmp_file();
2882 if (canonicalize_file(file, tmp_file) < 0) {
2887 if (move_file(tmp_file, file, TRUE) < 0) {
2888 g_warning("can't replace %s .\n", file);
2898 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2900 FILE *src_fp, *dest_fp;
2901 gchar buf[BUFFSIZE];
2902 gboolean err = FALSE;
2904 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2905 FILE_OP_ERROR(src, "fopen");
2909 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2910 FILE_OP_ERROR(dest, "fopen");
2915 if (change_file_mode_rw(dest_fp, dest) < 0) {
2916 FILE_OP_ERROR(dest, "chmod");
2917 g_warning("can't change file mode\n");
2920 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2922 if (fputs(buf, dest_fp) == EOF) {
2923 g_warning("writing to %s failed.\n", dest);
2931 if (ferror(src_fp)) {
2932 FILE_OP_ERROR(src, "fgets");
2936 if (fclose(dest_fp) == EOF) {
2937 FILE_OP_ERROR(dest, "fclose");
2949 gint uncanonicalize_file_replace(const gchar *file)
2953 tmp_file = get_tmp_file();
2955 if (uncanonicalize_file(file, tmp_file) < 0) {
2960 if (move_file(tmp_file, file, TRUE) < 0) {
2961 g_warning("can't replace %s .\n", file);
2971 gchar *normalize_newlines(const gchar *str)
2973 const gchar *p = str;
2976 out = outp = g_malloc(strlen(str) + 1);
2977 for (p = str; *p != '\0'; ++p) {
2979 if (*(p + 1) != '\n')
2990 gchar *get_outgoing_rfc2822_str(FILE *fp)
2992 gchar buf[BUFFSIZE];
2996 str = g_string_new(NULL);
2998 /* output header part */
2999 while (fgets(buf, sizeof(buf), fp) != NULL) {
3001 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3008 else if (next != ' ' && next != '\t') {
3012 if (fgets(buf, sizeof(buf), fp) == NULL)
3016 g_string_append(str, buf);
3017 g_string_append(str, "\r\n");
3023 /* output body part */
3024 while (fgets(buf, sizeof(buf), fp) != NULL) {
3027 g_string_append_c(str, '.');
3028 g_string_append(str, buf);
3029 g_string_append(str, "\r\n");
3033 g_string_free(str, FALSE);
3039 * Create a new boundary in a way that it is very unlikely that this
3040 * will occur in the following text. It would be easy to ensure
3041 * uniqueness if everything is either quoted-printable or base64
3042 * encoded (note that conversion is allowed), but because MIME bodies
3043 * may be nested, it may happen that the same boundary has already
3046 * boundary := 0*69<bchars> bcharsnospace
3047 * bchars := bcharsnospace / " "
3048 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3049 * "+" / "_" / "," / "-" / "." /
3050 * "/" / ":" / "=" / "?"
3052 * some special characters removed because of buggy MTAs
3055 gchar *generate_mime_boundary(const gchar *prefix)
3057 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3058 "abcdefghijklmnopqrstuvwxyz"
3063 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3064 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3067 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3071 gint change_file_mode_rw(FILE *fp, const gchar *file)
3074 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3076 return g_chmod(file, S_IRUSR|S_IWUSR);
3080 FILE *my_tmpfile(void)
3082 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3083 const gchar suffix[] = ".XXXXXX";
3084 const gchar *tmpdir;
3086 const gchar *progname;
3093 tmpdir = get_tmp_dir();
3094 tmplen = strlen(tmpdir);
3095 progname = g_get_prgname();
3096 if (progname == NULL)
3097 progname = "sylpheed-claws";
3098 proglen = strlen(progname);
3099 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3102 memcpy(fname, tmpdir, tmplen);
3103 fname[tmplen] = G_DIR_SEPARATOR;
3104 memcpy(fname + tmplen + 1, progname, proglen);
3105 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3107 fd = mkstemp(fname);
3114 /* verify that we can write in the file after unlinking */
3115 if (write(fd, buf, 1) < 0) {
3122 fp = fdopen(fd, "w+b");
3130 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3135 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3139 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3140 dir, G_DIR_SEPARATOR);
3141 fd = mkstemp_name(template, filename);
3144 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3145 fd = mkstemp(*filename);
3147 return fdopen(fd, "w+");
3150 FILE *str_open_as_stream(const gchar *str)
3155 g_return_val_if_fail(str != NULL, NULL);
3159 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3164 if (len == 0) return fp;
3166 if (fwrite(str, 1, len, fp) != len) {
3167 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3176 gint str_write_to_file(const gchar *str, const gchar *file)
3181 g_return_val_if_fail(str != NULL, -1);
3182 g_return_val_if_fail(file != NULL, -1);
3184 if ((fp = g_fopen(file, "wb")) == NULL) {
3185 FILE_OP_ERROR(file, "fopen");
3195 if (fwrite(str, 1, len, fp) != len) {
3196 FILE_OP_ERROR(file, "fwrite");
3202 if (fclose(fp) == EOF) {
3203 FILE_OP_ERROR(file, "fclose");
3211 gchar *file_read_to_str(const gchar *file)
3216 g_return_val_if_fail(file != NULL, NULL);
3218 if ((fp = g_fopen(file, "rb")) == NULL) {
3219 FILE_OP_ERROR(file, "fopen");
3223 str = file_read_stream_to_str(fp);
3230 gchar *file_read_stream_to_str(FILE *fp)
3237 g_return_val_if_fail(fp != NULL, NULL);
3239 array = g_byte_array_new();
3241 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3242 if (n_read < sizeof(buf) && ferror(fp))
3244 g_byte_array_append(array, buf, n_read);
3248 FILE_OP_ERROR("file stream", "fread");
3249 g_byte_array_free(array, TRUE);
3254 g_byte_array_append(array, buf, 1);
3255 str = (gchar *)array->data;
3256 g_byte_array_free(array, FALSE);
3258 if (!g_utf8_validate(str, -1, NULL)) {
3259 const gchar *src_codeset, *dest_codeset;
3261 src_codeset = conv_get_locale_charset_str();
3262 dest_codeset = CS_UTF_8;
3263 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3271 gint execute_async(gchar *const argv[])
3273 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3275 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3276 NULL, NULL, NULL, FALSE) == FALSE) {
3277 g_warning("Couldn't execute command: %s\n", argv[0]);
3284 gint execute_sync(gchar *const argv[])
3288 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3290 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3291 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3292 g_warning("Couldn't execute command: %s\n", argv[0]);
3297 if (WIFEXITED(status))
3298 return WEXITSTATUS(status);
3306 gint execute_command_line(const gchar *cmdline, gboolean async)
3311 debug_print("execute_command_line(): executing: %s\n", cmdline);
3313 argv = strsplit_with_quote(cmdline, " ", 0);
3316 ret = execute_async(argv);
3318 ret = execute_sync(argv);
3325 gchar *get_command_output(const gchar *cmdline)
3327 gchar *child_stdout;
3330 g_return_val_if_fail(cmdline != NULL, NULL);
3332 debug_print("get_command_output(): executing: %s\n", cmdline);
3334 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3336 g_warning("Couldn't execute command: %s\n", cmdline);
3340 return child_stdout;
3343 static gint is_unchanged_uri_char(char c)
3355 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3361 for(i = 0; i < strlen(uri) ; i++) {
3362 if (is_unchanged_uri_char(uri[i])) {
3363 if (k + 2 >= bufsize)
3365 encoded_uri[k++] = uri[i];
3368 char * hexa = "0123456789ABCDEF";
3370 if (k + 4 >= bufsize)
3372 encoded_uri[k++] = '%';
3373 encoded_uri[k++] = hexa[uri[i] / 16];
3374 encoded_uri[k++] = hexa[uri[i] % 16];
3380 gint open_uri(const gchar *uri, const gchar *cmdline)
3382 gchar buf[BUFFSIZE];
3384 gchar encoded_uri[BUFFSIZE];
3386 g_return_val_if_fail(uri != NULL, -1);
3388 /* an option to choose whether to use encode_uri or not ? */
3389 encode_uri(encoded_uri, BUFFSIZE, uri);
3392 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3393 !strchr(p + 2, '%'))
3394 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3397 g_warning("Open URI command line is invalid "
3398 "(there must be only one '%%s'): %s",
3400 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3403 execute_command_line(buf, TRUE);
3408 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3410 gchar buf[BUFFSIZE];
3413 g_return_val_if_fail(filepath != NULL, -1);
3416 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3417 !strchr(p + 2, '%'))
3418 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3421 g_warning("Open Text Editor command line is invalid "
3422 "(there must be only one '%%s'): %s",
3424 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3427 execute_command_line(buf, TRUE);
3432 time_t remote_tzoffset_sec(const gchar *zone)
3434 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3440 time_t remoteoffset;
3442 strncpy(zone3, zone, 3);
3446 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3447 (c == '+' || c == '-')) {
3448 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3450 remoteoffset = -remoteoffset;
3451 } else if (!strncmp(zone, "UT" , 2) ||
3452 !strncmp(zone, "GMT", 2)) {
3454 } else if (strlen(zone3) == 3) {
3455 for (p = ustzstr; *p != '\0'; p += 3) {
3456 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3457 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3458 remoteoffset = iustz * 3600;
3464 } else if (strlen(zone3) == 1) {
3466 case 'Z': remoteoffset = 0; break;
3467 case 'A': remoteoffset = -1; break;
3468 case 'B': remoteoffset = -2; break;
3469 case 'C': remoteoffset = -3; break;
3470 case 'D': remoteoffset = -4; break;
3471 case 'E': remoteoffset = -5; break;
3472 case 'F': remoteoffset = -6; break;
3473 case 'G': remoteoffset = -7; break;
3474 case 'H': remoteoffset = -8; break;
3475 case 'I': remoteoffset = -9; break;
3476 case 'K': remoteoffset = -10; break; /* J is not used */
3477 case 'L': remoteoffset = -11; break;
3478 case 'M': remoteoffset = -12; break;
3479 case 'N': remoteoffset = 1; break;
3480 case 'O': remoteoffset = 2; break;
3481 case 'P': remoteoffset = 3; break;
3482 case 'Q': remoteoffset = 4; break;
3483 case 'R': remoteoffset = 5; break;
3484 case 'S': remoteoffset = 6; break;
3485 case 'T': remoteoffset = 7; break;
3486 case 'U': remoteoffset = 8; break;
3487 case 'V': remoteoffset = 9; break;
3488 case 'W': remoteoffset = 10; break;
3489 case 'X': remoteoffset = 11; break;
3490 case 'Y': remoteoffset = 12; break;
3491 default: remoteoffset = 0; break;
3493 remoteoffset = remoteoffset * 3600;
3497 return remoteoffset;
3500 time_t tzoffset_sec(time_t *now)
3506 lt = localtime(now);
3508 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3510 if (lt->tm_year < gmt.tm_year)
3512 else if (lt->tm_year > gmt.tm_year)
3514 else if (lt->tm_yday < gmt.tm_yday)
3516 else if (lt->tm_yday > gmt.tm_yday)
3519 if (off >= 24 * 60) /* should be impossible */
3520 off = 23 * 60 + 59; /* if not, insert silly value */
3521 if (off <= -24 * 60)
3522 off = -(23 * 60 + 59);
3527 /* calculate timezone offset */
3528 gchar *tzoffset(time_t *now)
3530 static gchar offset_string[6];
3536 lt = localtime(now);
3538 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3540 if (lt->tm_year < gmt.tm_year)
3542 else if (lt->tm_year > gmt.tm_year)
3544 else if (lt->tm_yday < gmt.tm_yday)
3546 else if (lt->tm_yday > gmt.tm_yday)
3554 if (off >= 24 * 60) /* should be impossible */
3555 off = 23 * 60 + 59; /* if not, insert silly value */
3557 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3559 return offset_string;
3562 void get_rfc822_date(gchar *buf, gint len)
3566 gchar day[4], mon[4];
3567 gint dd, hh, mm, ss, yyyy;
3572 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3573 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3574 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3575 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3578 /* just a wrapper to suppress the warning of gcc about %c */
3579 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3580 const struct tm *tm)
3582 return strftime(s, max, format, tm);
3585 void debug_set_mode(gboolean mode)
3590 gboolean debug_get_mode(void)
3595 void debug_print_real(const gchar *format, ...)
3598 gchar buf[BUFFSIZE];
3600 if (!debug_mode) return;
3602 va_start(args, format);
3603 g_vsnprintf(buf, sizeof(buf), format, args);
3610 const char * debug_srcname(const char *file)
3612 const char *s = strrchr (file, '/');
3617 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3619 if (subject == NULL)
3622 subject += subject_get_prefix_length(subject);
3624 return g_hash_table_lookup(subject_table, subject);
3627 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3630 if (subject == NULL || *subject == 0)
3632 subject += subject_get_prefix_length(subject);
3633 g_hash_table_insert(subject_table, subject, data);
3636 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3638 if (subject == NULL)
3641 subject += subject_get_prefix_length(subject);
3642 g_hash_table_remove(subject_table, subject);
3646 *\brief Check if a string is prefixed with known (combinations)
3647 * of prefixes. The function assumes that each prefix
3648 * is terminated by zero or exactly _one_ space.
3650 *\param str String to check for a prefixes
3652 *\return int Number of chars in the prefix that should be skipped
3653 * for a "clean" subject line. If no prefix was found, 0
3656 int subject_get_prefix_length(const gchar *subject)
3658 /*!< Array with allowable reply prefixes regexps. */
3659 static const gchar * const prefixes[] = {
3660 "Re\\:", /* "Re:" */
3661 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3662 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3663 "Aw\\:", /* "Aw:" (German) */
3664 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3665 "Res\\:", /* "Res:" (Brazilian Outlook) */
3666 "Fw\\:", /* "Fw:" Forward */
3667 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3668 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3669 "Rif\\:", /* "Rif:" (Italian Outlook) */
3670 "Sv\\:", /* "Sv" (Norwegian) */
3671 "Vs\\:", /* "Vs" (Norwegian) */
3672 "Ad\\:", /* "Ad" (Norwegian) */
3673 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3676 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3679 static regex_t regex;
3680 static gboolean init_;
3682 if (!subject) return 0;
3683 if (!*subject) return 0;
3686 GString *s = g_string_new("");
3688 for (n = 0; n < PREFIXES; n++)
3689 /* Terminate each prefix regexpression by a
3690 * "\ ?" (zero or ONE space), and OR them */
3691 g_string_append_printf(s, "(%s\\ ?)%s",
3696 g_string_prepend(s, "(");
3697 g_string_append(s, ")+"); /* match at least once */
3698 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3701 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3702 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3703 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3704 debug_print("Error compiling regexp %s\n", s->str);
3705 g_string_free(s, TRUE);
3709 g_string_free(s, TRUE);
3713 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3719 guint g_stricase_hash(gconstpointer gptr)
3721 guint hash_result = 0;
3724 for (str = gptr; str && *str; str++) {
3725 hash_result += toupper(*str);
3731 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3733 const char *str1 = gptr1;
3734 const char *str2 = gptr2;
3736 return !strcasecmp(str1, str2);
3739 gint g_int_compare(gconstpointer a, gconstpointer b)
3741 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3744 gchar *generate_msgid(gchar *buf, gint len)
3753 addr = g_strconcat("@", get_domain_name(), NULL);
3755 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3756 lt->tm_year + 1900, lt->tm_mon + 1,
3757 lt->tm_mday, lt->tm_hour,
3758 lt->tm_min, lt->tm_sec,
3759 (guint) rand(), addr);
3766 quote_cmd_argument()
3768 return a quoted string safely usable in argument of a command.
3770 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3773 gint quote_cmd_argument(gchar * result, guint size,
3783 for(p = path ; * p != '\0' ; p ++) {
3785 if (isalnum((guchar)*p) || (* p == '/')) {
3786 if (remaining > 0) {
3792 result[size - 1] = '\0';
3797 if (remaining >= 2) {
3805 result[size - 1] = '\0';
3810 if (remaining > 0) {
3814 result[size - 1] = '\0';
3828 static void g_node_map_recursive(GNode *node, gpointer data)
3830 GNodeMapData *mapdata = (GNodeMapData *) data;
3832 GNodeMapData newmapdata;
3835 newdata = mapdata->func(node->data, mapdata->data);
3836 if (newdata != NULL) {
3837 newnode = g_node_new(newdata);
3838 g_node_append(mapdata->parent, newnode);
3840 newmapdata.parent = newnode;
3841 newmapdata.func = mapdata->func;
3842 newmapdata.data = mapdata->data;
3844 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3848 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3851 GNodeMapData mapdata;
3853 g_return_val_if_fail(node != NULL, NULL);
3854 g_return_val_if_fail(func != NULL, NULL);
3856 root = g_node_new(func(node->data, data));
3858 mapdata.parent = root;
3859 mapdata.func = func;
3860 mapdata.data = data;
3862 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3867 #define HEX_TO_INT(val, hex) \
3871 if ('0' <= c && c <= '9') { \
3873 } else if ('a' <= c && c <= 'f') { \
3874 val = c - 'a' + 10; \
3875 } else if ('A' <= c && c <= 'F') { \
3876 val = c - 'A' + 10; \
3882 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3889 if (hi == -1 || lo == -1)
3892 *out = (hi << 4) + lo;
3896 #define INT_TO_HEX(hex, val) \
3899 hex = '0' + (val); \
3901 hex = 'A' + (val) - 10; \
3904 void get_hex_str(gchar *out, guchar ch)
3908 INT_TO_HEX(hex, ch >> 4);
3910 INT_TO_HEX(hex, ch & 0x0f);
3916 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3918 #define G_PRINT_REF g_print
3922 *\brief Register ref counted pointer. It is based on GBoxed, so should
3923 * work with anything that uses the GType system. The semantics
3924 * are similar to a C++ auto pointer, with the exception that
3925 * C doesn't have automatic closure (calling destructors) when
3926 * exiting a block scope.
3927 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3928 * function directly.
3930 *\return GType A GType type.
3932 GType g_auto_pointer_register(void)
3934 static GType auto_pointer_type;
3935 if (!auto_pointer_type)
3937 g_boxed_type_register_static
3938 ("G_TYPE_AUTO_POINTER",
3939 (GBoxedCopyFunc) g_auto_pointer_copy,
3940 (GBoxedFreeFunc) g_auto_pointer_free);
3941 return auto_pointer_type;
3945 *\brief Structure with g_new() allocated pointer guarded by the
3948 typedef struct AutoPointerRef {
3949 void (*free) (gpointer);
3955 *\brief The auto pointer opaque structure that references the
3956 * pointer guard block.
3958 typedef struct AutoPointer {
3959 AutoPointerRef *ref;
3960 gpointer ptr; /*!< access to protected pointer */
3964 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3968 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3969 * ... when assigning, copying and freeing storage elements
3971 * gtk_list_store_new(N_S_COLUMNS,
3972 * G_TYPE_AUTO_POINTER,
3976 * Template *precious_data = g_new0(Template, 1);
3977 * g_pointer protect = g_auto_pointer_new(precious_data);
3979 * gtk_list_store_set(container, &iter,
3983 * ... the gtk_list_store has copied the pointer and
3984 * ... incremented its reference count, we should free
3985 * ... the auto pointer (in C++ a destructor would do
3986 * ... this for us when leaving block scope)
3988 * g_auto_pointer_free(protect);
3990 * ... gtk_list_store_set() now manages the data. When
3991 * ... *explicitly* requesting a pointer from the list
3992 * ... store, don't forget you get a copy that should be
3993 * ... freed with g_auto_pointer_free() eventually.
3997 *\param pointer Pointer to be guarded.
3999 *\return GAuto * Pointer that should be used in containers with
4002 GAuto *g_auto_pointer_new(gpointer p)
4004 AutoPointerRef *ref;
4010 ref = g_new0(AutoPointerRef, 1);
4011 ptr = g_new0(AutoPointer, 1);
4021 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4027 *\brief Allocate an autopointer using the passed \a free function to
4028 * free the guarded pointer
4030 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4037 aptr = g_auto_pointer_new(p);
4038 aptr->ref->free = free_;
4042 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4044 if (auto_ptr == NULL)
4046 return ((AutoPointer *) auto_ptr)->ptr;
4050 *\brief Copies an auto pointer by. It's mostly not necessary
4051 * to call this function directly, unless you copy/assign
4052 * the guarded pointer.
4054 *\param auto_ptr Auto pointer returned by previous call to
4055 * g_auto_pointer_new_XXX()
4057 *\return gpointer An auto pointer
4059 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4062 AutoPointerRef *ref;
4065 if (auto_ptr == NULL)
4070 newp = g_new0(AutoPointer, 1);
4073 newp->ptr = ref->pointer;
4077 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4083 *\brief Free an auto pointer
4085 void g_auto_pointer_free(GAuto *auto_ptr)
4088 AutoPointerRef *ref;
4090 if (auto_ptr == NULL)
4096 if (--(ref->cnt) == 0) {
4098 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4100 ref->free(ref->pointer);
4105 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4110 void replace_returns(gchar *str)
4115 while (strstr(str, "\n")) {
4116 *strstr(str, "\n") = ' ';
4118 while (strstr(str, "\r")) {
4119 *strstr(str, "\r") = ' ';
4123 /* get_uri_part() - retrieves a URI starting from scanpos.
4124 Returns TRUE if succesful */
4125 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4126 const gchar **bp, const gchar **ep, gboolean hdr)
4129 gint parenthese_cnt = 0;
4131 g_return_val_if_fail(start != NULL, FALSE);
4132 g_return_val_if_fail(scanpos != NULL, FALSE);
4133 g_return_val_if_fail(bp != NULL, FALSE);
4134 g_return_val_if_fail(ep != NULL, FALSE);
4138 /* find end point of URI */
4139 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4140 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4141 !IS_ASCII(*(const guchar *)ep_) ||
4142 strchr("[]{}<>\"", *ep_)) {
4144 } else if (strchr("(", *ep_)) {
4146 } else if (strchr(")", *ep_)) {
4147 if (parenthese_cnt > 0)
4154 /* no punctuation at end of string */
4156 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4157 * should pass some URI type to this function and decide on that whether
4158 * to perform punctuation stripping */
4160 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4162 for (; ep_ - 1 > scanpos + 1 &&
4163 IS_REAL_PUNCT(*(ep_ - 1));
4167 #undef IS_REAL_PUNCT
4174 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4176 while (bp && *bp && g_ascii_isspace(*bp))
4178 return g_strndup(bp, ep - bp);
4181 /* valid mail address characters */
4182 #define IS_RFC822_CHAR(ch) \
4186 !g_ascii_isspace(ch) && \
4187 !strchr("(),;<>\"", (ch)))
4189 /* alphabet and number within 7bit ASCII */
4190 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4191 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4193 static GHashTable *create_domain_tab(void)
4195 static const gchar *toplvl_domains [] = {
4197 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4198 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4199 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4200 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4201 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4202 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4203 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4204 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4205 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4206 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4207 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4208 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4209 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4210 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4211 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4212 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4213 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4214 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4215 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4216 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4217 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4218 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4219 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4220 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4221 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4222 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4225 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4227 g_return_val_if_fail(htab, NULL);
4228 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4229 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4233 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4235 const gint MAX_LVL_DOM_NAME_LEN = 6;
4236 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4237 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4240 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4243 for (p = buf; p < m && first < last; *p++ = *first++)
4247 return g_hash_table_lookup(tab, buf) != NULL;
4250 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4251 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4252 const gchar **bp, const gchar **ep, gboolean hdr)
4254 /* more complex than the uri part because we need to scan back and forward starting from
4255 * the scan position. */
4256 gboolean result = FALSE;
4257 const gchar *bp_ = NULL;
4258 const gchar *ep_ = NULL;
4259 static GHashTable *dom_tab;
4260 const gchar *last_dot = NULL;
4261 const gchar *prelast_dot = NULL;
4262 const gchar *last_tld_char = NULL;
4264 /* the informative part of the email address (describing the name
4265 * of the email address owner) may contain quoted parts. the
4266 * closure stack stores the last encountered quotes. */
4267 gchar closure_stack[128];
4268 gchar *ptr = closure_stack;
4270 g_return_val_if_fail(start != NULL, FALSE);
4271 g_return_val_if_fail(scanpos != NULL, FALSE);
4272 g_return_val_if_fail(bp != NULL, FALSE);
4273 g_return_val_if_fail(ep != NULL, FALSE);
4276 const gchar *start_quote = NULL;
4277 const gchar *end_quote = NULL;
4279 /* go to the real start */
4280 if (start[0] == ',')
4282 if (start[0] == ';')
4284 while (start[0] == '\n' || start[0] == '\r')
4286 while (start[0] == ' ' || start[0] == '\t')
4291 /* check if there are quotes (to skip , in them) */
4292 if (*start == '"') {
4293 start_quote = start;
4295 end_quote = strstr(start, "\"");
4301 /* skip anything between quotes */
4302 if (start_quote && end_quote) {
4307 /* find end (either , or ; or end of line) */
4308 if (strstr(start, ",") && strstr(start, ";"))
4309 *ep = strstr(start,",") < strstr(start, ";")
4310 ? strstr(start, ",") : strstr(start, ";");
4311 else if (strstr(start, ","))
4312 *ep = strstr(start, ",");
4313 else if (strstr(start, ";"))
4314 *ep = strstr(start, ";");
4316 *ep = start+strlen(start);
4318 /* go back to real start */
4319 if (start_quote && end_quote) {
4320 start = start_quote;
4323 /* check there's still an @ in that, or search
4324 * further if possible */
4325 if (strstr(start, "@") && strstr(start, "@") < *ep)
4327 else if (*ep < start+strlen(start)) {
4330 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4338 dom_tab = create_domain_tab();
4339 g_return_val_if_fail(dom_tab, FALSE);
4341 /* scan start of address */
4342 for (bp_ = scanpos - 1;
4343 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4346 /* TODO: should start with an alnum? */
4348 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4351 if (bp_ != scanpos) {
4352 /* scan end of address */
4353 for (ep_ = scanpos + 1;
4354 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4356 prelast_dot = last_dot;
4358 if (*(last_dot + 1) == '.') {
4359 if (prelast_dot == NULL)
4361 last_dot = prelast_dot;
4366 /* TODO: really should terminate with an alnum? */
4367 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4372 if (last_dot == NULL)
4374 if (last_dot >= ep_)
4375 last_dot = prelast_dot;
4376 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4380 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4381 if (*last_tld_char == '?')
4384 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4391 if (!result) return FALSE;
4393 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4394 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4395 && IS_RFC822_CHAR(*(ep_ + 3))) {
4396 /* this informative part with an @ in it is
4397 * followed by the email address */
4400 /* go to matching '>' (or next non-rfc822 char, like \n) */
4401 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4404 /* include the bracket */
4405 if (*ep_ == '>') ep_++;
4407 /* include the leading quote */
4415 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4416 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4419 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4420 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4423 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4424 #define IN_STACK() (ptr > closure_stack)
4425 /* has underrun check */
4426 #define POP_STACK() if(IN_STACK()) --ptr
4427 /* has overrun check */
4428 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4429 /* has underrun check */
4430 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4434 /* scan for the informative part. */
4435 for (bp_ -= 2; bp_ >= start; bp_--) {
4436 /* if closure on the stack keep scanning */
4437 if (PEEK_STACK() == *bp_) {
4441 if (*bp_ == '\'' || *bp_ == '"') {
4446 /* if nothing in the closure stack, do the special conditions
4447 * the following if..else expression simply checks whether
4448 * a token is acceptable. if not acceptable, the clause
4449 * should terminate the loop with a 'break' */
4450 if (!PEEK_STACK()) {
4452 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4453 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4454 /* hyphens are allowed, but only in
4456 } else if (strchr(" \"'", *bp_)) {
4457 /* but anything not being a punctiation
4460 break; /* anything else is rejected */
4467 /* scan forward (should start with an alnum) */
4468 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4484 #undef IS_ASCII_ALNUM
4485 #undef IS_RFC822_CHAR
4487 gchar *make_email_string(const gchar *bp, const gchar *ep)
4489 /* returns a mailto: URI; mailto: is also used to detect the
4490 * uri type later on in the button_pressed signal handler */
4494 tmp = g_strndup(bp, ep - bp);
4495 result = g_strconcat("mailto:", tmp, NULL);
4501 gchar *make_http_string(const gchar *bp, const gchar *ep)
4503 /* returns an http: URI; */
4507 while (bp && *bp && g_ascii_isspace(*bp))
4509 tmp = g_strndup(bp, ep - bp);
4510 result = g_strconcat("http://", tmp, NULL);
4516 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4518 FILE *fp = fopen(path, "rb");
4519 gchar buf[BUFFSIZE];
4520 gchar *result = NULL;
4523 while (fgets(buf, sizeof (buf), fp) != NULL) {
4524 gchar **parts = g_strsplit(buf, ";", 3);
4525 gchar *trimmed = parts[0];
4526 while (trimmed[0] == ' ')
4528 while (trimmed[strlen(trimmed)-1] == ' ')
4529 trimmed[strlen(trimmed)-1] = '\0';
4531 if (!strcmp(trimmed, type)) {
4532 gboolean needsterminal = FALSE;
4533 if (parts[2] && strstr(parts[2], "needsterminal")) {
4534 needsterminal = TRUE;
4536 if (parts[2] && strstr(parts[2], "test=")) {
4537 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4538 gchar *testcmd = orig_testcmd;
4539 if (strstr(testcmd,";"))
4540 *(strstr(testcmd,";")) = '\0';
4541 while (testcmd[0] == ' ')
4543 while (testcmd[strlen(testcmd)-1] == '\n')
4544 testcmd[strlen(testcmd)-1] = '\0';
4545 while (testcmd[strlen(testcmd)-1] == '\r')
4546 testcmd[strlen(testcmd)-1] = '\0';
4547 while (testcmd[strlen(testcmd)-1] == ' ')
4548 testcmd[strlen(testcmd)-1] = '\0';
4550 if (strstr(testcmd, "%s")) {
4551 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4552 gint res = system(tmp);
4554 g_free(orig_testcmd);
4561 gint res = system(testcmd);
4562 g_free(orig_testcmd);
4572 while (trimmed[0] == ' ')
4574 while (trimmed[strlen(trimmed)-1] == '\n')
4575 trimmed[strlen(trimmed)-1] = '\0';
4576 while (trimmed[strlen(trimmed)-1] == '\r')
4577 trimmed[strlen(trimmed)-1] = '\0';
4578 while (trimmed[strlen(trimmed)-1] == ' ')
4579 trimmed[strlen(trimmed)-1] = '\0';
4580 result = g_strdup(trimmed);
4583 /* if there are no single quotes around %s, add them.
4584 * '.*%s.*' is ok, as in display 'png:%s'
4586 if (strstr(result, "%s")
4587 && !(strstr(result, "'") < strstr(result,"%s") &&
4588 strstr(strstr(result,"%s"), "'"))) {
4589 gchar *start = g_strdup(result);
4590 gchar *end = g_strdup(strstr(result, "%s")+2);
4592 *strstr(start, "%s") = '\0';
4593 tmp = g_strconcat(start,"'%s'",end, NULL);
4599 if (needsterminal) {
4600 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4611 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4613 gchar *result = NULL;
4617 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4618 result = mailcap_get_command_in_file(path, type, file_to_open);
4622 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4626 void mailcap_update_default(const gchar *type, const gchar *command)
4628 gchar *path = NULL, *outpath = NULL;
4629 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4630 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4631 FILE *fp = fopen(path, "rb");
4632 FILE *outfp = fopen(outpath, "wb");
4633 gchar buf[BUFFSIZE];
4646 while (fgets(buf, sizeof (buf), fp) != NULL) {
4647 gchar **parts = g_strsplit(buf, ";", 3);
4648 gchar *trimmed = parts[0];
4649 while (trimmed[0] == ' ')
4651 while (trimmed[strlen(trimmed)-1] == ' ')
4652 trimmed[strlen(trimmed)-1] = '\0';
4654 if (!strcmp(trimmed, type)) {
4663 fprintf(outfp, "%s; %s\n", type, command);
4666 g_rename(outpath, path);
4669 gint copy_dir(const gchar *src, const gchar *dst)
4674 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4675 g_warning("failed to open directory: %s\n", src);
4679 if (make_dir(dst) < 0)
4682 while ((name = g_dir_read_name(dir)) != NULL) {
4683 gchar *old_file, *new_file;
4684 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4685 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4686 debug_print("copying: %s -> %s\n", old_file, new_file);
4687 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4688 gint r = copy_file(old_file, new_file, TRUE);
4691 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4692 gint r = copy_dir(old_file, new_file);
4700 /* crude test to see if a file is an email. */
4701 gboolean file_is_email (const gchar *filename)
4707 if (filename == NULL)
4709 if ((fp = g_fopen(filename, "rb")) == NULL)
4711 while (i < 60 && score < 3
4712 && fgets(buffer, sizeof (buffer), fp) > 0) {
4713 if (!strncmp(buffer, "From:", strlen("From:")))
4715 if (!strncmp(buffer, "To:", strlen("To:")))
4717 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4722 return (score >= 3);
4725 gboolean sc_g_list_bigger(GList *list, gint max)
4729 while (cur && i <= max+1) {
4736 gboolean sc_g_slist_bigger(GSList *list, gint max)
4740 while (cur && i <= max+1) {
4747 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4748 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4749 NULL, NULL, NULL, NULL, NULL, NULL};
4750 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4751 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4752 NULL, NULL, NULL, NULL, NULL, NULL};
4753 const gchar *s_am_up = NULL;
4754 const gchar *s_pm_up = NULL;
4755 const gchar *s_am_low = NULL;
4756 const gchar *s_pm_low = NULL;
4757 const gchar *def_loc_format = NULL;
4758 const gchar *date_loc_format = NULL;
4759 const gchar *time_loc_format = NULL;
4760 const gchar *time_am_pm = NULL;
4762 static gboolean time_names_init_done = FALSE;
4764 static void init_time_names(void)
4766 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4767 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4768 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4769 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4770 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4771 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4772 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4774 monthnames[0] = Q_("Complete month name for use by strftime|January");
4775 monthnames[1] = Q_("Complete month name for use by strftime|February");
4776 monthnames[2] = Q_("Complete month name for use by strftime|March");
4777 monthnames[3] = Q_("Complete month name for use by strftime|April");
4778 monthnames[4] = Q_("Complete month name for use by strftime|May");
4779 monthnames[5] = Q_("Complete month name for use by strftime|June");
4780 monthnames[6] = Q_("Complete month name for use by strftime|July");
4781 monthnames[7] = Q_("Complete month name for use by strftime|August");
4782 monthnames[8] = Q_("Complete month name for use by strftime|September");
4783 monthnames[9] = Q_("Complete month name for use by strftime|October");
4784 monthnames[10] = Q_("Complete month name for use by strftime|November");
4785 monthnames[11] = Q_("Complete month name for use by strftime|December");
4787 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4788 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4789 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4790 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4791 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4792 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4793 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4795 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4796 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4797 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4798 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4799 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4800 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4801 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4802 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4803 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4804 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4805 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4806 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4808 s_am_up = Q_("For use by strftime (morning)|AM");
4809 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4810 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4811 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4813 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4814 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4815 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4817 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4819 time_names_init_done = TRUE;
4823 time_t now = time(NULL), next;
4824 gchar buf_a[1024], buf_b[1024];
4826 printf("test strftime:\n");
4828 for (next = now; next < now + 86400*365*2; next++) {
4829 lt = localtime(&now);
4830 strftime(buf_a, 1024,
4831 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4832 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4833 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4834 fast_strftime(buf_b, 1024,
4835 "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
4836 "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
4837 "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
4838 if (strcmp(buf_a, buf_b)) {
4839 printf("diff: \n%s\n%s\n", buf_a, buf_b);
4846 #define CHECK_SIZE() { \
4847 total_done += len; \
4848 if (total_done >= buflen) { \
4849 buf[buflen-1] = '\0'; \
4854 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4856 gchar *curpos = buf;
4857 gint total_done = 0;
4858 gchar subbuf[64], subfmt[64];
4859 static time_t last_tzset = (time_t)0;
4861 if (!time_names_init_done)
4864 if (format == NULL || lt == NULL)
4867 if (last_tzset != time(NULL)) {
4869 last_tzset = time(NULL);
4872 if (*format == '%') {
4873 gint len = 0, tmp = 0;
4877 len = 1; CHECK_SIZE();
4881 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4882 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4885 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4886 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4890 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4891 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4894 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4895 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4898 fast_strftime(subbuf, 64, def_loc_format, lt);
4899 len = strlen(subbuf); CHECK_SIZE();
4900 strncpy2(curpos, subbuf, buflen - total_done);
4903 total_done += 2; CHECK_SIZE();
4904 tmp = (lt->tm_year + 1900)/100;
4905 *curpos++ = '0'+(tmp / 10);
4906 *curpos++ = '0'+(tmp % 10);
4909 total_done += 2; CHECK_SIZE();
4910 *curpos++ = '0'+(lt->tm_mday / 10);
4911 *curpos++ = '0'+(lt->tm_mday % 10);
4914 total_done += 8; CHECK_SIZE();
4915 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4916 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4918 *curpos++ = '0'+(lt->tm_mday / 10);
4919 *curpos++ = '0'+(lt->tm_mday % 10);
4921 tmp = lt->tm_year%100;
4922 *curpos++ = '0'+(tmp / 10);
4923 *curpos++ = '0'+(tmp % 10);
4926 len = 2; CHECK_SIZE();
4927 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4930 len = 10; CHECK_SIZE();
4931 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4932 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4935 total_done += 2; CHECK_SIZE();
4936 *curpos++ = '0'+(lt->tm_hour / 10);
4937 *curpos++ = '0'+(lt->tm_hour % 10);
4940 total_done += 2; CHECK_SIZE();
4946 *curpos++ = '0'+(tmp / 10);
4947 *curpos++ = '0'+(tmp % 10);
4950 len = 3; CHECK_SIZE();
4951 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4954 len = 2; CHECK_SIZE();
4955 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4958 len = 2; CHECK_SIZE();
4964 snprintf(curpos, buflen - total_done, "%2d", tmp);
4967 total_done += 2; CHECK_SIZE();
4968 tmp = lt->tm_mon + 1;
4969 *curpos++ = '0'+(tmp / 10);
4970 *curpos++ = '0'+(tmp % 10);
4973 total_done += 2; CHECK_SIZE();
4974 *curpos++ = '0'+(lt->tm_min / 10);
4975 *curpos++ = '0'+(lt->tm_min % 10);
4978 len = 1; CHECK_SIZE();
4982 if (lt->tm_hour >= 12) {
4983 len = strlen(s_pm_up); CHECK_SIZE();
4984 snprintf(curpos, buflen-total_done, s_pm_up);
4986 len = strlen(s_am_up); CHECK_SIZE();
4987 snprintf(curpos, buflen-total_done, s_am_up);
4991 if (lt->tm_hour >= 12) {
4992 len = strlen(s_pm_low); CHECK_SIZE();
4993 snprintf(curpos, buflen-total_done, s_pm_low);
4995 len = strlen(s_am_low); CHECK_SIZE();
4996 snprintf(curpos, buflen-total_done, s_am_low);
5000 fast_strftime(subbuf, 64, time_am_pm, lt);
5001 len = strlen(subbuf); CHECK_SIZE();
5002 strncpy2(curpos, subbuf, buflen - total_done);
5005 total_done += 5; CHECK_SIZE();
5006 *curpos++ = '0'+(lt->tm_hour / 10);
5007 *curpos++ = '0'+(lt->tm_hour % 10);
5009 *curpos++ = '0'+(lt->tm_min / 10);
5010 *curpos++ = '0'+(lt->tm_min % 10);
5013 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5014 len = strlen(subbuf); CHECK_SIZE();
5015 strncpy2(curpos, subbuf, buflen - total_done);
5018 total_done += 2; CHECK_SIZE();
5019 *curpos++ = '0'+(lt->tm_sec / 10);
5020 *curpos++ = '0'+(lt->tm_sec % 10);
5023 len = 1; CHECK_SIZE();
5027 total_done += 8; CHECK_SIZE();
5028 *curpos++ = '0'+(lt->tm_hour / 10);
5029 *curpos++ = '0'+(lt->tm_hour % 10);
5031 *curpos++ = '0'+(lt->tm_min / 10);
5032 *curpos++ = '0'+(lt->tm_min % 10);
5034 *curpos++ = '0'+(lt->tm_sec / 10);
5035 *curpos++ = '0'+(lt->tm_sec % 10);
5038 len = 1; CHECK_SIZE();
5039 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5042 len = 1; CHECK_SIZE();
5043 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5046 fast_strftime(subbuf, 64, date_loc_format, lt);
5047 len = strlen(subbuf); CHECK_SIZE();
5048 strncpy2(curpos, subbuf, buflen - total_done);
5051 fast_strftime(subbuf, 64, time_loc_format, lt);
5052 len = strlen(subbuf); CHECK_SIZE();
5053 strncpy2(curpos, subbuf, buflen - total_done);
5056 total_done += 2; CHECK_SIZE();
5057 tmp = lt->tm_year%100;
5058 *curpos++ = '0'+(tmp / 10);
5059 *curpos++ = '0'+(tmp % 10);
5062 len = 4; CHECK_SIZE();
5063 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5073 /* let these complicated ones be done with the libc */
5074 snprintf(subfmt, 64, "%%%c", *format);
5075 strftime(subbuf, 64, subfmt, lt);
5076 len = strlen(subbuf); CHECK_SIZE();
5077 strncpy2(curpos, subbuf, buflen - total_done);
5081 /* let these complicated modifiers be done with the libc */
5082 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5083 strftime(subbuf, 64, subfmt, lt);
5084 len = strlen(subbuf); CHECK_SIZE();
5085 strncpy2(curpos, subbuf, buflen - total_done);
5089 g_warning("format error (%c)", *format);
5096 int len = 1; CHECK_SIZE();
5097 *curpos++ = *format++;