/*
* grn_misc.c - Implementation of miscellaneous project-wide functions
*
* $Id: grn_misc.c,v 1.39 2000/06/28 11:28:28 sc Exp $
*/
/* Copyright (C) 1999-2000 Sergey Chernikov (sc@ivvs.ul.ru)
*
* Authors: Sergey Chernikov <sc@ivvs.ul.ru>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*/
#include "grn_consts.h"
#include "grn_vars.h"
#include "grn_misc.h"
#include "grn_util.h"
#include "grn_news.h"
#include "mutt.h"
t_msgheaders *t_msgheaders_alloc(void)
{
t_msgheaders *mh = g_new(t_msgheaders, 1);
mh->number = 0; mh->lines = 0;
mh->size = 0; mh->parsed_date = 0;
mh->score = 0;
mh->subject = NULL; mh->from = NULL;
mh->date = NULL; mh->id = NULL;
mh->ref = NULL; mh->xref = NULL;
mh->path = NULL; mh->newsgroups = NULL;
mh->to = NULL;
mh->grp = NULL;
mh->extra_hdrs = NULL;
return mh;
}
static void cp_extra_hdrs(gpointer key, gpointer data, GHashTable *ht)
{
gchar *new_key = g_strdup(key);
gchar *new_data = str_copy(data);
g_hash_table_insert(ht, new_key, new_data);
}
t_msgheaders *t_msgheaders_copy(t_msgheaders *src)
{
t_msgheaders *mh = t_msgheaders_alloc();
mh->number = src->number; mh->lines = src->lines;
mh->size = src->size; mh->parsed_date = src->parsed_date;
mh->score = src->score;
mh->subject = str_copy(src->subject);
mh->from = str_copy(src->from);
mh->date = str_copy(src->date);
mh->id = str_copy(src->id);
mh->ref = str_copy(src->ref);
mh->xref = str_copy(src->xref);
mh->path = str_copy(src->path);
mh->newsgroups = str_copy(src->newsgroups);
mh->to = str_copy(src->to);
mh->grp = src->grp;
mh->extra_hdrs = g_hash_table_new(g_str_hash, g_str_equal);
if (src->extra_hdrs)
g_hash_table_foreach(src->extra_hdrs, (GHFunc) cp_extra_hdrs, mh->extra_hdrs);
return mh;
}
static void rm_extra_hdrs(gchar *key, gchar *data, GHashTable *ht)
{
str_free(&key); str_free(&data);
}
void t_msgheaders_rm_extra_hdrs(t_msgheaders *mh)
{
if (! mh->extra_hdrs) return;
g_hash_table_foreach(mh->extra_hdrs, (GHFunc) rm_extra_hdrs, mh->extra_hdrs);
g_hash_table_destroy(mh->extra_hdrs); mh->extra_hdrs = NULL;
}
void t_msgheaders_free(t_msgheaders **mh)
{
g_return_if_fail(*mh != NULL);
str_free(&((*mh)->subject));
str_free(&((*mh)->from));
str_free(&((*mh)->date));
str_free(&((*mh)->id));
str_free(&((*mh)->ref));
str_free(&((*mh)->xref));
str_free(&((*mh)->path));
str_free(&((*mh)->newsgroups));
str_free(&((*mh)->to));
t_msgheaders_rm_extra_hdrs(*mh);
g_free(*mh); *mh = NULL;
}
gchar *t_msgheaders_get_hdr(t_msgheaders *mh, gchar *hdr)
{
g_return_val_if_fail(mh != NULL, NULL);
g_return_val_if_fail(hdr != NULL, NULL);
if (! strcmp(hdr, "Subject")) return mh->subject;
if (! strcmp(hdr, "From")) return mh->from;
if (! strcmp(hdr, "Date")) return mh->date;
if (! strcmp(hdr, "Message-ID")) return mh->id;
if (! strcmp(hdr, "References")) return mh->ref;
if (! strcmp(hdr, "Xref")) return mh->xref;
if (! strcmp(hdr, "Path")) return mh->path;
if (! strcmp(hdr, "Newsgroups")) return mh->newsgroups;
if (! strcmp(hdr, "To")) return mh->to;
if (! strcmp(hdr, "Lines"))
{
static gchar s[10];
g_snprintf(s, 9, "%lu", mh->lines); return s;
}
if (! strcmp(hdr, "Size"))
{
static gchar s[10];
g_snprintf(s, 9, "%lu", mh->size); return s;
}
return t_msgheaders_get_extra_hdr(mh, hdr);
}
gchar *t_msgheaders_get_extra_hdr(t_msgheaders *mh, gchar *hdr)
{
g_return_val_if_fail(mh != NULL, NULL);
g_return_val_if_fail(hdr != NULL, NULL);
if (! mh->extra_hdrs) return NULL;
return g_hash_table_lookup(mh->extra_hdrs, hdr);
}
void t_msgheaders_set_hdr(t_msgheaders *mh, gchar *hdr, gchar *data)
{
g_return_if_fail(mh != NULL);
g_return_if_fail(hdr != NULL);
if (! data) return;
if (! strcmp(hdr, "Subject"))
{
str_free(&(mh->subject)); mh->subject = g_strdup(data);
}
else if (! strcmp(hdr, "From"))
{
str_free(&(mh->from)); mh->from = g_strdup(data);
}
else if (! strcmp(hdr, "Date"))
{
str_free(&(mh->date)); mh->date = g_strdup(data);
}
else if (! strcmp(hdr, "Message-ID"))
{
str_free(&(mh->id)); mh->id = g_strdup(data);
}
else if (! strcmp(hdr, "References"))
{
str_free(&(mh->ref)); mh->ref = g_strdup(data);
}
else if (! strcmp(hdr, "Xref"))
{
str_free(&(mh->xref)); mh->xref = g_strdup(data);
}
else if (! strcmp(hdr, "Path"))
{
str_free(&(mh->path)); mh->path = g_strdup(data);
}
else if (! strcmp(hdr, "Newsgroups"))
{
str_free(&(mh->newsgroups)); mh->newsgroups = g_strdup(data);
}
else if (! strcmp(hdr, "To"))
{
str_free(&(mh->to)); mh->to = g_strdup(data);
}
else t_msgheaders_add_extra_hdr(mh, hdr, data);
}
/* hdr and data are g_strdup()'ed inside the function */
void t_msgheaders_add_extra_hdr(t_msgheaders *mh, gchar *hdr, gchar *data)
{
g_return_if_fail(mh != NULL);
g_return_if_fail(hdr != NULL);
if (! data) return;
if (! mh->extra_hdrs) mh->extra_hdrs = g_hash_table_new(g_str_hash, g_str_equal);
t_msgheaders_rm_extra_hdr(mh, hdr);
g_hash_table_insert(mh->extra_hdrs, g_strdup(hdr), g_strdup(data));
}
void t_msgheaders_rm_hdr(t_msgheaders *mh, gchar *hdr)
{
g_return_if_fail(mh != NULL);
g_return_if_fail(hdr != NULL);
if (! strcmp(hdr, "Subject")) str_free(&(mh->subject));
else if (! strcmp(hdr, "From")) str_free(&(mh->from));
else if (! strcmp(hdr, "Date")) str_free(&(mh->date));
else if (! strcmp(hdr, "Message-ID")) str_free(&(mh->id));
else if (! strcmp(hdr, "References")) str_free(&(mh->ref));
else if (! strcmp(hdr, "Xref")) str_free(&(mh->xref));
else if (! strcmp(hdr, "Path")) str_free(&(mh->path));
else if (! strcmp(hdr, "Newsgroups")) str_free(&(mh->newsgroups));
else if (! strcmp(hdr, "To")) str_free(&(mh->to));
else if (! strcmp(hdr, "Lines")) mh->lines = 0;
else t_msgheaders_rm_extra_hdr(mh, hdr);
}
void t_msgheaders_rm_extra_hdr(t_msgheaders *mh, gchar *hdr)
{
gchar *key, *value;
g_return_if_fail(mh != NULL);
g_return_if_fail(hdr != NULL);
if (! mh->extra_hdrs) return;
if (g_hash_table_lookup_extended(mh->extra_hdrs, hdr, (gpointer *) &key,
(gpointer *) &value))
{
g_hash_table_remove(mh->extra_hdrs, hdr);
str_free(&key); str_free(&value);
}
}
void msghdrlist_free(GList **gl)
{
GList *l;
if (! gl) return;
if (! *gl) return;
for(l=*gl; l; l=l->next)
{
t_msgheaders *mh = (t_msgheaders *) l->data;
if (mh) t_msgheaders_free(&mh);
}
g_list_free(*gl); *gl = NULL;
}
t_message *t_message_alloc(void)
{
t_message *msg = g_new(t_message, 1);
msg->mh = NULL;
msg->body = NULL;
msg->threads = NULL;
msg->nchildren = NULL;
msg->pos = 0;
msg->tagged = FALSE;
msg->cached = FALSE;
msg->data = NULL;
msg->attachments = NULL;
msg->temp_files = NULL;
msg->filename = NULL;
msg->fp = NULL;
return msg;
}
t_message *t_message_copy(t_message *src)
{
t_message *m;
GSList *l;
if (! src) return NULL;
m = t_message_alloc();
if (src->mh) m->mh = t_msgheaders_copy(src->mh);
m->body = str_copy(src->body);
m->tagged = src->tagged;
m->pos = src->pos;
for (l=src->attachments; l; l=l->next)
{
BODY *b = (BODY *) l->data;
if (! b) continue;
m->attachments = g_slist_append(m->attachments, mutt_copy_body(b, NULL));
}
return m;
}
void t_message_free(t_message **msg)
{
GSList *l;
g_return_if_fail(*msg != NULL);
if ((*msg)->mh) t_msgheaders_free(&((*msg)->mh));
str_free(&((*msg)->body));
if ((*msg)->fp) fclose((*msg)->fp);
if ((*msg)->attachments)
{
for (l=(*msg)->attachments; l; l=l->next)
{
BODY *b = (BODY *) l->data;
if (! b) continue;
mutt_free_body(&b);
}
g_slist_free((*msg)->attachments);
}
if ((*msg)->temp_files)
{
for (l=(*msg)->temp_files; l; l=l->next)
{
gchar *s = l->data;
if (str_check(s)) unlink(s);
str_free(&s);
}
g_slist_free((*msg)->temp_files);
}
if ((*msg)->data)
{
BODY *b = (BODY *) ((*msg)->data);
mutt_free_body(&b);
}
str_free(&((*msg)->filename));
msglist_free(&((*msg)->threads));
g_free(*msg); *msg = NULL;
}
void t_message_destroy(t_message *msg)
{ t_message_free(&msg); }
gboolean t_message_is_read(t_message *msg)
{
if (! msg) return FALSE;
if (! msg->mh) return FALSE;
if (! msg->mh->grp) return FALSE;
return grn_news_art_read(msg->mh->grp->seq, msg->mh->number);
}
void msglist_free(GList **gl)
{
GList *l;
if (! gl) return;
if (! *gl) return;
for(l=*gl; l; l=l->next)
{
t_message *msg = (t_message *) l->data;
if (! msg) continue;
t_message_free(&msg);
}
g_list_free(*gl); *gl = NULL;
}
void msglist_destroy(GList *gl)
{ msglist_free(&gl); }
static gboolean msg_in_cur_thread(GList *ml, t_message *msg)
{
GList *l;
if (! msg) return FALSE;
if (! ml) return FALSE;
for (l=ml; l; l=l->next)
{
t_message *m = (t_message *) l->data;
if (! m) continue;
if (m == msg) return TRUE;
if (m->threads)
if (msg_in_cur_thread(m->threads, msg)) return TRUE;
}
return FALSE;
}
t_message *msglist_find_cur_thread(GList *ml, t_message *msg)
{
GList *l;
if (! msg) return NULL;
for (l=ml; l; l=l->next)
{
t_message *m = (t_message *) l->data;
if (! m) continue;
if (m == msg) return msg;
if (! m->threads) continue;
if (msg_in_cur_thread(m->threads, msg)) return m;
}
return NULL;
}
static void generate_plain_unread_list(GList *ml, GList **ret)
{
GList *l;
if (! ml) return;
for (l=ml; l; l=l->next)
{
t_message *msg = (t_message *) l->data;
if (! msg) continue;
*ret = g_list_prepend(*ret, msg);
if (msg->threads) generate_plain_unread_list(msg->threads, ret);
}
}
static GList *get_prev_next_unread(GList *pml, gboolean prev)
{
GList *l = pml;
if (! pml) return NULL;
if (prev) l = l->prev;
else l = l->next;
while (l)
{
t_message *msg = (t_message *) l->data;
if (! msg) continue;
if (! t_message_is_read(msg)) return l;
if (prev) l = l->prev;
else l = l->next;
}
return NULL;
}
t_message *msglist_find_prev_unread(GList *ml, t_message *msg)
{
t_message *prev = NULL;
GList *found;
if (! ml) return NULL;
if (! msg) return NULL;
if ((! msg->mh) && (! msg->mh->grp)) return NULL;
if (! msg->mh->grp->plain_unread)
{
generate_plain_unread_list(ml, &(msg->mh->grp->plain_unread));
msg->mh->grp->plain_unread = g_list_reverse(msg->mh->grp->plain_unread);
}
found = g_list_find(msg->mh->grp->plain_unread, msg);
if (found) found = get_prev_next_unread(found, TRUE);
if (found) prev = (t_message *) found->data;
return prev;
}
t_message *msglist_find_next_unread(GList *ml, t_message *msg)
{
t_message *next = NULL;
GList *found;
if (! ml) return NULL;
if (! msg) return NULL;
if ((! msg->mh) && (! msg->mh->grp)) return NULL;
if (! msg->mh->grp->plain_unread)
{
generate_plain_unread_list(ml, &(msg->mh->grp->plain_unread));
msg->mh->grp->plain_unread = g_list_reverse(msg->mh->grp->plain_unread);
}
found = g_list_find(msg->mh->grp->plain_unread, msg);
if (found) found = get_prev_next_unread(found, FALSE);
if (found) next = (t_message *) found->data;
return next;
}
static t_message *get_next_unread_in_thread(t_message *thread)
{
GList *l;
if (! thread) return NULL;
if (! t_message_is_read(thread)) return thread;
if (! thread->threads) return NULL;
for (l=thread->threads; l; l=l->next)
{
t_message *msg = (t_message *) l->data;
t_message *ret_msg = NULL;
if (! msg) continue;
ret_msg = get_next_unread_in_thread(msg);
if (ret_msg) return ret_msg;
}
return NULL;
}
t_message *msglist_find_prev_thread_unread(GList *ml, t_message *msg)
{
t_message *cur_thread, *prev=NULL;
GList *l;
if (! ml) return NULL;
if (! msg) return NULL;
cur_thread = msglist_find_cur_thread(ml, msg);
l = g_list_find(ml, cur_thread);
if (! l) return NULL;
l = l->prev;
while (! prev)
{
if (! l) break;
prev = get_next_unread_in_thread((t_message *) l->data);
l = l->prev;
}
return prev;
}
t_message *msglist_find_next_thread_unread(GList *ml, t_message *msg)
{
t_message *cur_thread, *next=NULL;
GList *l;
if (! ml) return NULL;
if (! msg) return NULL;
cur_thread = msglist_find_cur_thread(ml, msg);
l = g_list_find(ml, cur_thread);
if (! l) return NULL;
l = l->next;
while (! next)
{
if (! l) break;
next = get_next_unread_in_thread((t_message *) l->data);
l = l->next;
}
return next;
}
t_message *msglist_find_by_id(GList *ml, t_message *msrc)
{
GList *l;
gchar *ref, *ref_prev;
t_message *ret = NULL;
if (! ml) return NULL;
if (! msrc) return NULL;
if (! msrc->mh) return NULL;
for (l=ml; l; l=l->next)
{
t_message *msg = (t_message *) l->data;
if (! msg) continue;
ret = msglist_find_by_id(msg->threads, msrc);
if (ret) break;
if (msg->mh && msrc->mh->ref)
{
gchar *buf = g_strdup(msrc->mh->ref);
ref = strtok(buf, " ");
while (1)
{
ref_prev = ref;
ref = strtok(NULL, " ");
if ((! ref) || (ref[0] == '\0')) break;
}
ref = ref_prev;
str_free(&buf);
if (msg->mh->id && (! strcmp(msg->mh->id, ref))) { ret=msg; break; }
}
}
return ret;
}
static gchar *strip_re(gchar *src)
{
while (*src && (strstr(src, "Re: ") == src)) src += 4;
return src;
}
t_message *msglist_find_by_subj(GList *ml, t_message *msrc)
{
GList *l;
t_message *ret = NULL;
if (! ml) return NULL;
if (! msrc) return NULL;
if (! msrc->mh) return NULL;
for (l=ml; l; l=l->next)
{
t_message *msg = (t_message *) l->data;
if (! msg) continue;
if (msg->mh)
{
if (msg->mh->ref && msrc->mh->ref &&
(! strcmp(msrc->mh->ref, msg->mh->ref))) { ret=msg; break; }
if (msg->mh->subject && msrc->mh->subject)
{
gchar *str1 = strip_re(msg->mh->subject);
gchar *str2 = strip_re(msrc->mh->subject);
if (! strcmp(str1, str2)) { ret=msg; break; }
}
}
ret = msglist_find_by_subj(msg->threads, msrc);
if (ret) break;
}
return ret;
}
grn_newsgroup *grn_newsgroup_alloc(void)
{
grn_newsgroup *grp = g_new(grn_newsgroup, 1);
grp->name = NULL;
grp->descr = NULL;
grp->seq = NULL;
grp->plain_unread = NULL;
grp->nunread = -1;
return grp;
}
void grn_newsgroup_free(grn_newsgroup **grp)
{
g_return_if_fail(*grp != NULL);
str_free(&((*grp)->name));
str_free(&((*grp)->descr));
str_free(&((*grp)->seq));
if ((*grp)->plain_unread) g_list_free((*grp)->plain_unread);
g_free(*grp); *grp = NULL;
}
void grn_newsgroup_set_seq(grn_newsgroup *grp, gchar *seq)
{
g_return_if_fail(grp != NULL);
str_free(&(grp->seq));
grp->seq = seq;
grp->nunread = grn_news_get_unread(grp);
if (newsrc_ht && grp->name) change_newsrc(newsrc_ht, grp->name, grp->seq);
}
void grouplist_free(GList **gl)
{
GList *l;
g_return_if_fail(gl != NULL);
if (! *gl) return;
for(l=*gl; l; l=l->next)
{
grn_newsgroup *grp = (grn_newsgroup *) l->data;
if (grp) grn_newsgroup_free(&grp);
}
g_list_free(*gl); *gl = NULL;
}
gulong grouplist_get_nunread(GList *gl)
{
GList *l;
gulong cnt=0;
if (! gl) return 0;
for (l=gl; l; l=l->next)
{
grn_newsgroup *grp = (grn_newsgroup *) l->data;
if (grp && (grp->nunread > 0)) cnt++;
}
return cnt;
}
grn_find *grn_find_alloc(void)
{
grn_find *gf = g_new(grn_find, 1);
gf->data = g_strdup(""); gf->hdr = g_strdup("X-Comment-To");
gf->search_from = FALSE; gf->search_subj = TRUE;
gf->search_body = FALSE; gf->search_hdr = FALSE;
gf->is_regex = FALSE; gf->sensitive = FALSE;
gf->unread_only = TRUE;
return gf;
}
void grn_find_free(grn_find **gf)
{
g_return_if_fail(*gf != NULL);
str_free(&((*gf)->data));
str_free(&((*gf)->hdr));
g_free(*gf); *gf = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1