sync with 0.7.5cvs14
[claws.git] / src / utils.c
index 1f4dc6abdea4f70b71f470ad318055546be3acd6..cf1fdd7aa201d69f0379757983752b789325cd87 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2002 Hiroyuki Yamamoto
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
+#include <netdb.h>
 
 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
 #  include <wchar.h>
@@ -211,6 +212,21 @@ gchar *strtailchomp(gchar *str, gchar tail_char)
        return str;
 }
 
+/* remove CR (carriage return) */
+gchar *strcrchomp(gchar *str)
+{
+       register gchar *s;
+
+       if (!*str) return str;
+
+       s = str + strlen(str) - 1;
+       if (*s == '\n' && s > str && *(s - 1) == '\r') {
+               *(s - 1) = '\n';
+               *s = '\0';
+       }
+
+       return str;
+}
 
 /* Similar to `strstr' but this function ignores the case of both strings.  */
 gchar *strcasestr(const gchar *haystack, const gchar *needle)
@@ -494,10 +510,8 @@ gint subject_compare(const gchar *s1, const gchar *s2)
        if (!s1 || !s2) return -1;
        if (!*s1 || !*s2) return -1;
 
-       Xalloca(str1, strlen(s1) + 1, return -1);
-       Xalloca(str2, strlen(s2) + 1, return -1);
-       strcpy(str1, s1);
-       strcpy(str2, s2);
+       Xstrdup_a(str1, s1, return -1);
+       Xstrdup_a(str2, s2, return -1);
 
        trim_subject(str1);
        trim_subject(str2);
@@ -656,7 +670,12 @@ void extract_quote(gchar *str, gchar quote_chr)
        register gchar *p;
 
        if ((str = strchr(str, quote_chr))) {
-               if ((p = strchr(str + 1, quote_chr))) {
+               p = str;
+               while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
+                       memmove(p - 1, p, strlen(p) + 1);
+                       p--;
+               }
+               if(p) {
                        *p = '\0';
                        memmove(str, str + 1, p - str);
                }
@@ -840,6 +859,31 @@ GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
        return group_list;
 }
 
+GList *add_history(GList *list, const gchar *str)
+{
+       GList *old;
+
+       g_return_val_if_fail(str != NULL, list);
+
+       old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
+       if (old) {
+               g_free(old->data);
+               list = g_list_remove(list, old->data);
+       } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
+               GList *last;
+
+               last = g_list_last(list);
+               if (last) {
+                       g_free(last->data);
+                       g_list_remove(list, last->data);
+               }
+       }
+
+       list = g_list_prepend(list, g_strdup(str));
+
+       return list;
+}
+
 void remove_return(gchar *str)
 {
        register gchar *p = str;
@@ -897,6 +941,22 @@ void subst_char(gchar *str, gchar orig, gchar subst)
        }
 }
 
+void subst_chars(gchar *str, gchar *orig, gchar subst)
+{
+       register gchar *p = str;
+
+       while (*p) {
+               if (strchr(orig, *p) != NULL)
+                       *p = subst;
+               p++;
+       }
+}
+
+void subst_for_filename(gchar *str)
+{
+       subst_chars(str, " \t\r\n\"/\\", '_');
+}
+
 gboolean is_header_line(const gchar *str)
 {
        if (str[0] == ':') return FALSE;
@@ -923,7 +983,7 @@ gboolean is_ascii_str(const guchar *str)
        return TRUE;
 }
 
-gint get_quote_level(const gchar *str)
+gint get_quote_level(const gchar *str, const gchar *quote_chars)
 {
        const gchar *first_pos;
        const gchar *last_pos;
@@ -931,11 +991,11 @@ gint get_quote_level(const gchar *str)
        gint quote_level = -1;
 
        /* speed up line processing by only searching to the last '>' */
-       if ((first_pos = strchr(str, '>')) != NULL) {
+       if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
                /* skip a line if it contains a '<' before the initial '>' */
                if (memchr(str, '<', first_pos - str) != NULL)
                        return -1;
-               last_pos = strrchr(first_pos, '>');
+               last_pos = line_has_quote_char_last(first_pos, quote_chars);
        } else
                return -1;
 
@@ -947,14 +1007,16 @@ gint get_quote_level(const gchar *str)
                                break;
                }
 
-               if (*p == '>')
+               if (strchr(quote_chars, *p))
                        quote_level++;
                else if (*p != '-' && !isspace(*p) && p <= last_pos) {
                        /* any characters are allowed except '-' and space */
-                       while (*p != '-' && *p != '>' && !isspace(*p) &&
-                              p < last_pos)
+                       while (*p != '-' 
+                              && !strchr(quote_chars, *p) 
+                              && !isspace(*p) 
+                              && p < last_pos)
                                p++;
-                       if (*p == '>')
+                       if (strchr(quote_chars, *p))
                                quote_level++;
                        else
                                break;
@@ -966,37 +1028,40 @@ gint get_quote_level(const gchar *str)
        return quote_level;
 }
 
-GList *uri_list_extract_filenames(const gchar *uri_list)
+const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars) 
 {
-       GList *result = NULL;
-       const gchar *p, *q;
-       gchar *file;
+       gchar * position = NULL;
+       gchar * tmp_pos = NULL;
+       int i;
 
-       p = uri_list;
+       if (quote_chars == NULL)
+               return FALSE;
+       
+       for (i = 0; i < strlen(quote_chars); i++) {
+               tmp_pos = strchr (str,  quote_chars[i]);
+               if(position == NULL 
+                  || (tmp_pos != NULL && position >= tmp_pos) )
+                       position = tmp_pos;
+       }
+       return position; 
+}
 
-       while (p) {
-               if (*p != '#') {
-                       while (isspace(*p)) p++;
-                       if (!strncmp(p, "file:", 5)) {
-                               p += 5;
-                               q = p;
-                               while (*q && *q != '\n' && *q != '\r') q++;
+const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars) 
+{
+       gchar * position = NULL;
+       gchar * tmp_pos = NULL;
+       int i;
 
-                               if (q > p) {
-                                       q--;
-                                       while (q > p && isspace(*q)) q--;
-                                       file = g_malloc(q - p + 2);
-                                       strncpy(file, p, q - p + 1);
-                                       file[q - p + 1] = '\0';
-                                       result = g_list_append(result,file);
-                               }
-                       }
-               }
-               p = strchr(p, '\n');
-               if (p) p++;
+       if (quote_chars == NULL)
+               return FALSE;
+       
+       for (i = 0; i < strlen(quote_chars); i++) {
+               tmp_pos = strrchr (str, quote_chars[i]);
+               if(position == NULL 
+                  || (tmp_pos != NULL && position <= tmp_pos) )
+                       position = tmp_pos;
        }
-
-       return result;
+       return position; 
 }
 
 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
@@ -1036,13 +1101,100 @@ gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
        return NULL;
 }
 
-/* this fuction was taken from gstrfuncs.c in glib. */
+gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
+{
+       const gchar *p;
+       gchar quote_chr = '"';
+       gint in_brace;
+       gboolean in_quote = FALSE;
+
+       p = str;
+
+       if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
+               p++;
+               in_brace = 1;
+               while (*p) {
+                       if (*p == op && !in_quote)
+                               in_brace++;
+                       else if (*p == cl && !in_quote)
+                               in_brace--;
+                       else if (*p == quote_chr)
+                               in_quote ^= TRUE;
+
+                       if (in_brace == 0)
+                               return (gchar *)p;
+
+                       p++;
+               }
+       }
+
+       return NULL;
+}
+
+gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
+                            gint max_tokens)
+{
+       GSList *string_list = NULL, *slist;
+       gchar **str_array;
+       const gchar *s_op, *s_cl;
+       guint i, n = 1;
+
+       g_return_val_if_fail(str != NULL, NULL);
+
+       if (max_tokens < 1)
+               max_tokens = G_MAXINT;
+
+       s_op = strchr_with_skip_quote(str, '"', op);
+       if (!s_op) return NULL;
+       str = s_op;
+       s_cl = strchr_parenthesis_close(str, op, cl);
+       if (s_cl) {
+               do {
+                       guint len;
+                       gchar *new_string;
+
+                       str++;
+                       len = s_cl - str;
+                       new_string = g_new(gchar, len + 1);
+                       strncpy(new_string, str, len);
+                       new_string[len] = 0;
+                       string_list = g_slist_prepend(string_list, new_string);
+                       n++;
+                       str = s_cl + 1;
+
+                       while (*str && isspace(*str)) str++;
+                       if (*str != op) {
+                               string_list = g_slist_prepend(string_list,
+                                                             g_strdup(""));
+                               n++;
+                               s_op = strchr_with_skip_quote(str, '"', op);
+                               if (!--max_tokens || !s_op) break;
+                               str = s_op;
+                       } else
+                               s_op = str;
+                       s_cl = strchr_parenthesis_close(str, op, cl);
+               } while (--max_tokens && s_cl);
+       }
+
+       str_array = g_new(gchar*, n);
+
+       i = n - 1;
+
+       str_array[i--] = NULL;
+       for (slist = string_list; slist; slist = slist->next)
+               str_array[i--] = slist->data;
+
+       g_slist_free(string_list);
+
+       return str_array;
+}
+
 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
                            gint max_tokens)
 {
        GSList *string_list = NULL, *slist;
-       gchar **str_array, *s;
-       guint i, n = 1;
+       gchar **str_array, *s, *new_str;
+       guint i, n = 1, len;
 
        g_return_val_if_fail(str != NULL, NULL);
        g_return_val_if_fail(delim != NULL, NULL);
@@ -1055,13 +1207,15 @@ gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
                guint delimiter_len = strlen(delim);
 
                do {
-                       guint len;
-                       gchar *new_str;
-
                        len = s - str;
-                       new_str = g_new(gchar, len + 1);
-                       strncpy(new_str, str, len);
-                       new_str[len] = 0;
+                       new_str = g_strndup(str, len);
+
+                       if (new_str[0] == '\'' || new_str[0] == '\"') {
+                               if (new_str[len - 1] == new_str[0]) {
+                                       new_str[len - 1] = '\0';
+                                       memmove(new_str, new_str + 1, len - 1);
+                               }
+                       }
                        string_list = g_slist_prepend(string_list, new_str);
                        n++;
                        str = s + delimiter_len;
@@ -1070,8 +1224,16 @@ gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
        }
 
        if (*str) {
+               new_str = g_strdup(str);
+               if (new_str[0] == '\'' || new_str[0] == '\"') {
+                       len = strlen(str);
+                       if (new_str[len - 1] == new_str[0]) {
+                               new_str[len - 1] = '\0';
+                               memmove(new_str, new_str + 1, len - 1);
+                       }
+               }
+               string_list = g_slist_prepend(string_list, new_str);
                n++;
-               string_list = g_slist_prepend(string_list, g_strdup(str));
        }
 
        str_array = g_new(gchar*, n);
@@ -1087,10 +1249,110 @@ gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
        return str_array;
 }
 
+gchar *get_abbrev_newsgroup_name(const gchar *group)
+{
+       gchar *abbrev_group;
+       gchar *ap;
+       const gchar *p = group;
+
+       abbrev_group = ap = g_malloc(strlen(group) + 1);
+
+       while (*p) {
+               while (*p == '.')
+                       *ap++ = *p++;
+               if (strchr(p, '.')) {
+                       *ap++ = *p++;
+                       while (*p != '.') p++;
+               } else {
+                       strcpy(ap, p);
+                       return abbrev_group;
+               }
+       }
+
+       *ap = '\0';
+       return abbrev_group;
+}
+
+gchar *trim_string(const gchar *str, gint len)
+{
+       const gchar *p = str;
+       gint mb_len;
+       gchar *new_str;
+       gint new_len = 0;
+
+       if (!str) return NULL;
+       if (strlen(str) <= len)
+               return g_strdup(str);
+
+       while (*p != '\0') {
+               mb_len = mblen(p, MB_LEN_MAX);
+               if (mb_len == 0)
+                       break;
+               else if (mb_len < 0)
+                       return g_strdup(str);
+               else if (new_len + mb_len > len)
+                       break;
+               else
+                       new_len += mb_len;
+               p += mb_len;
+       }
+
+       Xstrndup_a(new_str, str, new_len, return g_strdup(str));
+       return g_strconcat(new_str, "...", NULL);
+}
+
+GList *uri_list_extract_filenames(const gchar *uri_list)
+{
+       GList *result = NULL;
+       const gchar *p, *q;
+       gchar *file;
+
+       p = uri_list;
+
+       while (p) {
+               if (*p != '#') {
+                       while (isspace(*p)) p++;
+                       if (!strncmp(p, "file:", 5)) {
+                               p += 5;
+                               q = p;
+                               while (*q && *q != '\n' && *q != '\r') q++;
+
+                               if (q > p) {
+                                       q--;
+                                       while (q > p && isspace(*q)) q--;
+                                       file = g_malloc(q - p + 2);
+                                       strncpy(file, p, q - p + 1);
+                                       file[q - p + 1] = '\0';
+                                       result = g_list_append(result,file);
+                               }
+                       }
+               }
+               p = strchr(p, '\n');
+               if (p) p++;
+       }
+
+       return result;
+}
+
+#define HEX_TO_INT(val, hex) \
+{ \
+       gchar c = hex; \
+ \
+       if ('0' <= c && c <= '9') { \
+               val = c - '0'; \
+       } else if ('a' <= c && c <= 'f') { \
+               val = c - 'a' + 10; \
+       } else if ('A' <= c && c <= 'F') { \
+               val = c - 'A' + 10; \
+       } else { \
+               val = 0; \
+       } \
+}
+
 /*
  * We need this wrapper around g_get_home_dir(), so that
  * we can fix some Windoze things here.  Should be done in glibc of course
- * but as long as we are not able to do our own extensions to glibc, we do 
+ * but as long as we are not able to do our own extensions to glibc, we do
  * it here.
  */
 gchar *get_home_dir(void)
@@ -1173,6 +1435,28 @@ gchar *get_mime_tmp_dir(void)
        return mime_tmp_dir;
 }
 
+gchar *get_template_dir(void)
+{
+       static gchar *template_dir = NULL;
+
+       if (!template_dir)
+               template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                          TEMPLATE_DIR, NULL);
+
+       return template_dir;
+}
+
+gchar *get_header_cache_dir(void)
+{
+       static gchar *header_dir = NULL;
+
+       if (!header_dir)
+               header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                        HEADER_CACHE_DIR, NULL);
+
+       return header_dir;
+}
+
 gchar *get_tmp_file(void)
 {
        static gchar *tmp_file = NULL;
@@ -1187,6 +1471,7 @@ gchar *get_tmp_file(void)
 gchar *get_domain_name(void)
 {
        static gchar *domain_name = NULL;
+        struct hostent *myfqdn = NULL;
 
        if (!domain_name) {
                gchar buf[BUFFSIZE] = "";
@@ -1194,7 +1479,16 @@ gchar *get_domain_name(void)
                if (gethostname(buf, sizeof(buf)) < 0) {
                        perror("gethostname");
                        strcpy(buf, "unknown");
-               }
+               }  else  {
+                myfqdn = gethostbyname(buf);
+                if (myfqdn != NULL)  {
+                  memset(buf, '\0', strlen(buf));
+                  strcpy(buf, myfqdn->h_name);
+                  }  else  {
+                  perror("gethostbyname");
+                  strcpy(buf, "unknown");
+                  }
+                }
 
                domain_name = g_strdup(buf);
        }
@@ -1214,6 +1508,32 @@ off_t get_file_size(const gchar *file)
        return s.st_size;
 }
 
+off_t get_file_size_as_crlf(const gchar *file)
+{
+       FILE *fp;
+       off_t size = 0;
+       gchar buf[BUFFSIZE];
+
+       if ((fp = fopen(file, "rb")) == NULL) {
+               FILE_OP_ERROR(file, "fopen");
+               return -1;
+       }
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               strretchomp(buf);
+               size += strlen(buf) + 2;
+       }
+
+       if (ferror(fp)) {
+               FILE_OP_ERROR(file, "fgets");
+               size = -1;
+       }
+
+       fclose(fp);
+
+       return size;
+}
+
 off_t get_left_file_size(FILE *fp)
 {
        glong pos;
@@ -1271,6 +1591,18 @@ gboolean is_dir_exist(const gchar *dir)
        return FALSE;
 }
 
+gboolean is_file_entry_exist(const gchar *file)
+{
+       struct stat s;
+
+       if (stat(file, &s) < 0) {
+               if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 gint change_dir(const gchar *dir)
 {
        gchar *prevdir = NULL;
@@ -1589,7 +1921,7 @@ gint copy_file(const gchar *src, const gchar *dest)
        gchar *dest_bak = NULL;
        gboolean err = FALSE;
 
-       if ((src_fp = fopen(src, "r")) == NULL) {
+       if ((src_fp = fopen(src, "rb")) == NULL) {
                FILE_OP_ERROR(src, "fopen");
                return -1;
        }
@@ -1603,7 +1935,7 @@ gint copy_file(const gchar *src, const gchar *dest)
                }
        }
 
-       if ((dest_fp = fopen(dest, "w")) == NULL) {
+       if ((dest_fp = fopen(dest, "wb")) == NULL) {
                FILE_OP_ERROR(dest, "fopen");
                fclose(src_fp);
                if (dest_bak) {
@@ -1731,6 +2063,32 @@ FILE *my_tmpfile(void)
        return tmpfile();
 }
 
+FILE *str_open_as_stream(const gchar *str)
+{
+       FILE *fp;
+       size_t len;
+
+       g_return_val_if_fail(str != NULL, NULL);
+
+       fp = my_tmpfile();
+       if (!fp) {
+               FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
+               return NULL;
+       }
+
+       len = strlen(str);
+       if (len == 0) return fp;
+
+       if (fwrite(str, len, 1, fp) != 1) {
+               FILE_OP_ERROR("str_open_as_stream", "fwrite");
+               fclose(fp);
+               return NULL;
+       }
+
+       rewind(fp);
+       return fp;
+}
+
 gint execute_async(gchar *const argv[])
 {
        pid_t pid;
@@ -1787,25 +2145,10 @@ gint execute_sync(gchar *const argv[])
 gint execute_command_line(const gchar *cmdline, gboolean async)
 {
        gchar **argv;
-       gint i;
        gint ret;
 
        argv = strsplit_with_quote(cmdline, " ", 0);
 
-       for (i = 0; argv[i] != NULL; i++) {
-               gchar *str = argv[i];
-
-               if (str[0] == '\'' || str[0] == '\"') {
-                       gint len;
-
-                       len = strlen(str);
-                       if (str[len - 1] == str[0]) {
-                               str[len - 1] = '\0';
-                               memmove(str, str + 1, len - 1);
-                       }
-               }
-       }
-
        if (async)
                ret = execute_async(argv);
        else
@@ -1817,17 +2160,17 @@ gint execute_command_line(const gchar *cmdline, gboolean async)
 
 static gint is_unchanged_uri_char(char c)
 {
-  switch (c) {
-    case '(':
-    case ')':
-    case ',':
-      return 0;
-    default:
-      return 1;
-    }
+       switch (c) {
+               case '(':
+               case ')':
+               case ',':
+                       return 0;
+               default:
+                       return 1;
+       }
 }
 
-void encode_uri(gchar * encoded_uri, gint bufsize, const gchar * uri)
+void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
 {
        int i;
        int k;
@@ -1857,31 +2200,31 @@ void encode_uri(gchar * encoded_uri, gint bufsize, const gchar * uri)
  */
 static gint axtoi(const gchar *hexstr)
 {
-       gint Hi, Lo, Result;
+       gint hi, lo, result;
        
-       Hi = hexstr[0];
-       if ('0' <= Hi && Hi <= '9') {
-               Hi -= '0';
+       hi = hexstr[0];
+       if ('0' <= hi && hi <= '9') {
+               hi -= '0';
        } else
-               if ('a' <= Hi && Hi <= 'f') {
-                       Hi -=('a'-10);
+               if ('a' <= hi && hi <= 'f') {
+                       hi -= ('a' - 10);
                } else
-                       if ('A' <= Hi && Hi <= 'F') {
-                               Hi -= ('A'-10);
+                       if ('A' <= hi && hi <= 'F') {
+                               hi -= ('A' - 10);
                        }
 
-       Lo = hexstr[1];
-       if ('0' <= Lo && Lo <='9') {
-               Lo -= '0';
+       lo = hexstr[1];
+       if ('0' <= lo && lo <= '9') {
+               lo -= '0';
        } else
-               if ('a' <= Lo && Lo <= 'f') {
-                       Lo -= ('a'-10);
+               if ('a' <= lo && lo <= 'f') {
+                       lo -= ('a'-10);
                } else
-                       if ('A' <= Lo && Lo <= 'F') {
-                               Lo -= ('A'-10);
+                       if ('A' <= lo && lo <= 'F') {
+                               lo -= ('A' - 10);
                        }
-       Result = Lo + (16 * Hi);
-       return (Result);
+       result = lo + (16 * hi);
+       return result;
 }
 
 
@@ -1889,40 +2232,37 @@ static gint axtoi(const gchar *hexstr)
  * plusses, and escape characters are used)
  */
 
-void decode_uri(gchar * decoded_uri, const gchar * encoded_uri)
+void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
 {
-       const gchar * encoded;
-       gchar * decoded;
+       const gchar *encoded;
+       gchar *decoded;
 
-       //      strcpy(decoded_uri, encoded_uri);
-       //      subst_char(decoded_uri, '+', ' ');
-       
        encoded = encoded_uri;
        decoded = decoded_uri;
 
-       while (* encoded) {
-               if (* encoded == '%') {
-                       encoded ++;
+       while (*encoded) {
+               if (*encoded == '%') {
+                       encoded++;
                        if (isxdigit(encoded[0])
                            && isxdigit(encoded[1])) {
-                               * decoded = (gchar) axtoi(encoded);
-                               decoded ++;
+                               *decoded = (gchar) axtoi(encoded);
+                               decoded++;
                                encoded += 2;
                        }
                }
-               else if (* encoded == '+') {
-                       * decoded = ' ';
-                       decoded ++;
-                       encoded ++;
+               else if (*encoded == '+') {
+                       *decoded = ' ';
+                       decoded++;
+                       encoded++;
                }
                else {
-                       * decoded = * encoded;
-                       decoded ++;
-                       encoded ++;
+                       *decoded = *encoded;
+                       decoded++;
+                       encoded++;
                }
        }
 
-       * decoded = '\0';
+       *decoded = '\0';
 }
 
 
@@ -1961,16 +2301,16 @@ time_t remote_tzoffset_sec(const gchar *zone)
        gchar *p;
        gchar c;
        gint iustz;
-       gint h, m;
+       gint offset;
        time_t remoteoffset;
 
        strncpy(zone3, zone, 3);
        zone3[3] = '\0';
        remoteoffset = 0;
 
-       if (sscanf(zone, "%c%2d%2d", &c, &h, &m) == 3 &&
+       if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
            (c == '+' || c == '-')) {
-               remoteoffset = ((h * 60) + m) * 60;
+               remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
                if (c == '-')
                        remoteoffset = -remoteoffset;
        } else if (!strncmp(zone, "UT" , 2) ||
@@ -2103,7 +2443,7 @@ static FILE *log_fp = NULL;
 void set_log_file(const gchar *filename)
 {
        if (log_fp) return;
-       log_fp = fopen(filename, "w");
+       log_fp = fopen(filename, "wb");
        if (!log_fp)
                FILE_OP_ERROR(filename, "fopen");
 }
@@ -2221,3 +2561,53 @@ void log_error(const gchar *format, ...)
                fflush(log_fp);
        }
 }
+
+
+void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
+{
+       if (subject == NULL)
+               subject = "";
+
+       if (g_strncasecmp(subject, "Re: ", 4) == 0)
+               return g_hash_table_lookup(subject_table, subject + 4);
+       else
+               return g_hash_table_lookup(subject_table, subject);
+}
+
+void subject_table_insert(GHashTable *subject_table, gchar * subject,
+                         void * data)
+{
+       if (subject == NULL)
+               return;
+       if (* subject == 0)
+               return;
+       if (g_strcasecmp(subject, "Re:") == 0)
+               return;
+       if (g_strcasecmp(subject, "Re: ") == 0)
+               return;
+
+       if (g_strncasecmp(subject, "Re: ", 4) == 0)
+               g_hash_table_insert(subject_table, subject + 4, data);
+       else
+               g_hash_table_insert(subject_table, subject, data);
+}
+
+void subject_table_remove(GHashTable *subject_table, gchar * subject)
+{
+       if (subject == NULL)
+               return;
+
+       if (g_strncasecmp(subject, "Re: ", 4) == 0)
+               g_hash_table_remove(subject_table, subject + 4);
+       else
+               g_hash_table_remove(subject_table, subject);
+}
+
+gboolean subject_is_reply(const gchar *subject)
+{
+       /* XXX: just simply here so someone can handle really
+        * advanced Re: detection like "Re[4]", "ANTW:" or
+        * Re: Re: Re: Re: Re: Re: Re: Re:" stuff. */
+       if (subject == NULL) return FALSE;
+       else return 0 == g_strncasecmp(subject, "Re: ", 4);
+}