summaryrefslogtreecommitdiff
path: root/pith/msgno.c
diff options
context:
space:
mode:
Diffstat (limited to 'pith/msgno.c')
-rw-r--r--pith/msgno.c941
1 files changed, 941 insertions, 0 deletions
diff --git a/pith/msgno.c b/pith/msgno.c
new file mode 100644
index 00000000..465a42e0
--- /dev/null
+++ b/pith/msgno.c
@@ -0,0 +1,941 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: msgno.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/msgno.h"
+#include "../pith/flag.h"
+#include "../pith/mailindx.h"
+#include "../pith/pineelt.h"
+#include "../pith/icache.h"
+
+
+/* internal prototypes */
+void set_msg_score(MAILSTREAM *, long, long);
+
+
+/*
+ * * * * Message number management functions * * *
+ */
+
+
+/*----------------------------------------------------------------------
+ Initialize a message manipulation structure for the given total
+
+ Accepts: msgs - pointer to pointer to message manipulation struct
+ tot - number of messages to initialize with
+ ----*/
+void
+msgno_init(MSGNO_S **msgs, long int tot, SortOrder def_sort, int def_sort_rev)
+{
+ long slop = (tot + 1L) % 64;
+ size_t len;
+
+ if(!msgs)
+ return;
+
+ if(!(*msgs)){
+ (*msgs) = (MSGNO_S *)fs_get(sizeof(MSGNO_S));
+ memset((void *)(*msgs), 0, sizeof(MSGNO_S));
+ }
+
+ (*msgs)->sel_cur = 0L;
+ (*msgs)->sel_cnt = 1L;
+ (*msgs)->sel_size = 8L;
+ len = (size_t)(*msgs)->sel_size * sizeof(long);
+ if((*msgs)->select)
+ fs_resize((void **)&((*msgs)->select), len);
+ else
+ (*msgs)->select = (long *)fs_get(len);
+
+ (*msgs)->select[0] = (tot) ? 1L : 0L;
+
+ (*msgs)->sort_size = (tot + 1L) + (64 - slop);
+ len = (size_t)(*msgs)->sort_size * sizeof(long);
+ if((*msgs)->sort)
+ fs_resize((void **)&((*msgs)->sort), len);
+ else
+ (*msgs)->sort = (long *)fs_get(len);
+
+ memset((void *)(*msgs)->sort, 0, len);
+ for(slop = 1L ; slop <= tot; slop++) /* reusing "slop" */
+ (*msgs)->sort[slop] = slop;
+
+ /*
+ * If there is filtering happening, isort will become larger than sort.
+ * Sort is a list of raw message numbers in their sorted order. There
+ * are missing raw numbers because some of the messages are excluded
+ * (MN_EXLD) from the view. Isort has one entry for every raw message
+ * number, which maps to the corresponding msgno (the row in the sort
+ * array). Some of the entries in isort are not used because those
+ * messages are excluded, but the entry is still there because we want
+ * to map from rawno to message number and the row number is the rawno.
+ */
+ (*msgs)->isort_size = (*msgs)->sort_size;
+ if((*msgs)->isort)
+ fs_resize((void **)&((*msgs)->isort), len);
+ else
+ (*msgs)->isort = (long *)fs_get(len);
+
+ (*msgs)->max_msgno = tot;
+ (*msgs)->nmsgs = tot;
+
+ /* set the inverse array */
+ msgno_reset_isort(*msgs);
+
+ (*msgs)->sort_order = def_sort;
+ (*msgs)->reverse_sort = def_sort_rev;
+ (*msgs)->flagged_hid = 0L;
+ (*msgs)->flagged_exld = 0L;
+ (*msgs)->flagged_chid = 0L;
+ (*msgs)->flagged_chid2= 0L;
+ (*msgs)->flagged_coll = 0L;
+ (*msgs)->flagged_usor = 0L;
+ (*msgs)->flagged_tmp = 0L;
+ (*msgs)->flagged_stmp = 0L;
+
+ /*
+ * This one is the total number of messages which are flagged
+ * hid OR chid. It isn't the sum of those two because a
+ * message may be flagged both at the same time.
+ */
+ (*msgs)->flagged_invisible = 0L;
+
+ /*
+ * And this keeps track of visible threads in the THRD_INDX. This is
+ * weird because a thread is visible if any of its messages are
+ * not hidden, including those that are CHID hidden. You can't just
+ * count up all the messages that are hid or chid because you would
+ * miss a thread that has its top-level message hidden but some chid
+ * message not hidden.
+ */
+ (*msgs)->visible_threads = -1L;
+}
+
+
+/*
+ * Isort makes mn_raw2m fast. Alternatively, we could look through
+ * the sort array to do mn_raw2m.
+ */
+void
+msgno_reset_isort(MSGNO_S *msgs)
+{
+ long i;
+
+ if(msgs){
+ /*
+ * Zero isort so raw messages numbers which don't appear in the
+ * sort array show up as undefined.
+ */
+ memset((void *) msgs->isort, 0,
+ (size_t) msgs->isort_size * sizeof(long));
+
+ /* fill in all the defined entries */
+ for(i = 1L; i <= mn_get_total(msgs); i++)
+ msgs->isort[msgs->sort[i]] = i;
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Release resources of a message manipulation structure
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to test
+ Returns: with specified structure and its members free'd
+ ----*/
+void
+msgno_give(MSGNO_S **msgs)
+{
+ if(msgs && *msgs){
+ if((*msgs)->sort)
+ fs_give((void **) &((*msgs)->sort));
+
+ if((*msgs)->isort)
+ fs_give((void **) &((*msgs)->isort));
+
+ if((*msgs)->select)
+ fs_give((void **) &((*msgs)->select));
+
+ fs_give((void **) msgs);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Release resources of a message part exception list
+
+ Accepts: parts -- list of parts to free
+ Returns: with specified structure and its members free'd
+ ----*/
+void
+msgno_free_exceptions(PARTEX_S **parts)
+{
+ if(parts && *parts){
+ if((*parts)->next)
+ msgno_free_exceptions(&(*parts)->next);
+
+ fs_give((void **) &(*parts)->partno);
+ fs_give((void **) parts);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Increment the current message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ ----*/
+void
+msgno_inc(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i;
+
+ if(!msgs || mn_get_total(msgs) < 1L)
+ return;
+
+ for(i = msgs->select[msgs->sel_cur] + 1; i <= mn_get_total(msgs); i++){
+ if(!msgline_hidden(stream, msgs, i, flags)){
+ (msgs)->select[((msgs)->sel_cur)] = i;
+ break;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Decrement the current message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ ----*/
+void
+msgno_dec(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i;
+
+ if(!msgs || mn_get_total(msgs) < 1L)
+ return;
+
+ for(i = (msgs)->select[((msgs)->sel_cur)] - 1L; i >= 1L; i--){
+ if(!msgline_hidden(stream, msgs, i, flags)){
+ (msgs)->select[((msgs)->sel_cur)] = i;
+ break;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Got thru the message mapping table, and remove messages with DELETED flag
+
+ Accepts: stream -- mail stream to removed message references from
+ msgs -- pointer to message manipulation struct
+ f -- flags to use a purge criteria
+ ----*/
+void
+msgno_exclude_deleted(MAILSTREAM *stream, MSGNO_S *msgs)
+{
+ long i, rawno;
+ MESSAGECACHE *mc;
+ int need_isort_reset = 0;
+
+ if(!msgs || msgs->max_msgno < 1L)
+ return;
+
+ /*
+ * With 3.91 we're using a new strategy for finding and operating
+ * on all the messages with deleted status. The idea is to do a
+ * mail_search for deleted messages so the elt's "searched" bit gets
+ * set, and then to scan the elt's for them and set our local bit
+ * to indicate they're excluded...
+ */
+ (void) count_flagged(stream, F_DEL);
+
+ /*
+ * Start with the end of the folder and work backwards so that
+ * msgno_exclude doesn't have to shift the entire array each time when
+ * there are lots of deleteds. In fact, if everything is deleted (like
+ * might be the case in a huge newsgroup) then it never has to shift
+ * anything. It is always at the end of the array just eliminating the
+ * last one instead. So instead of an n**2 operation, it is n.
+ */
+ for(i = msgs->max_msgno; i >= 1L; i--)
+ if((rawno = mn_m2raw(msgs, i)) > 0L && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))
+ && ((mc->valid && mc->deleted) || (!mc->valid && mc->searched))){
+ msgno_exclude(stream, msgs, i, 0);
+ need_isort_reset++;
+ }
+
+ if(need_isort_reset)
+ msgno_reset_isort(msgs);
+
+ /*
+ * If we excluded away a zoomed display, unhide everything...
+ */
+ if(msgs->max_msgno > 0L && any_lflagged(msgs, MN_HIDE) >= msgs->max_msgno)
+ for(i = 1L; i <= msgs->max_msgno; i++)
+ set_lflag(stream, msgs, i, MN_HIDE, 0);
+}
+
+
+
+void
+msgno_exclude(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int reset_isort)
+{
+ long i;
+
+ /*--- clear all flags to keep our counts consistent ---*/
+ set_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID | MN_CHID2 | MN_SLCT, 0);
+ set_lflag(stream, msgmap, msgno, MN_EXLD, 1); /* mark excluded */
+
+ /* erase knowledge in sort array (shift array down) */
+ for(i = msgno + 1L; i <= msgmap->max_msgno; i++)
+ msgmap->sort[i-1L] = msgmap->sort[i];
+
+ msgmap->max_msgno = MAX(0L, msgmap->max_msgno - 1L);
+ if(reset_isort)
+ msgno_reset_isort(msgmap);
+
+ msgno_flush_selected(msgmap, msgno);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Accepts: stream -- mail stream to removed message references from
+ msgs -- pointer to message manipulation struct
+ flags
+ MI_REFILTERING -- do includes appropriate for refiltering
+ MI_STATECHGONLY -- when refiltering, maybe only re-include
+ messages which have had state changes
+ since they were originally filtered
+ Returns 1 if any new messages are included (indicating that we need
+ to re-sort)
+ 0 if no new messages are included
+ ----*/
+int
+msgno_include(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i, slop, old_total, old_size;
+ int exbits, ret = 0;
+ size_t len;
+ MESSAGECACHE *mc;
+
+ if(!msgs)
+ return(ret);
+
+ for(i = 1L; i <= stream->nmsgs; i++){
+ if(!msgno_exceptions(stream, i, "0", &exbits, FALSE))
+ exbits = 0;
+
+ if((((flags & MI_REFILTERING) && (exbits & MSG_EX_FILTERED)
+ && !(exbits & MSG_EX_FILED)
+ && (!(flags & MI_STATECHGONLY) || (exbits & MSG_EX_STATECHG)))
+ || (!(flags & MI_REFILTERING) && !(exbits & MSG_EX_FILTERED)))
+ && get_lflag(stream, NULL, i, MN_EXLD)){
+ old_total = msgs->max_msgno;
+ old_size = msgs->sort_size;
+ slop = (msgs->max_msgno + 1L) % 64;
+ msgs->sort_size = (msgs->max_msgno + 1L) + (64 - slop);
+ len = (size_t) msgs->sort_size * sizeof(long);
+ if(msgs->sort){
+ if(old_size != msgs->sort_size)
+ fs_resize((void **)&(msgs->sort), len);
+ }
+ else
+ msgs->sort = (long *)fs_get(len);
+
+ ret = 1;
+ msgs->sort[++msgs->max_msgno] = i;
+ msgs->isort[i] = msgs->max_msgno;
+ set_lflag(stream, msgs, msgs->max_msgno, MN_EXLD, 0);
+ if(flags & MI_REFILTERING){
+ exbits &= ~(MSG_EX_FILTERED | MSG_EX_TESTED);
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+
+ if(old_total <= 0L){ /* if no previous messages, */
+ if(!msgs->select){ /* select the new message */
+ msgs->sel_size = 8L;
+ len = (size_t)msgs->sel_size * sizeof(long);
+ msgs->select = (long *)fs_get(len);
+ }
+
+ msgs->sel_cnt = 1L;
+ msgs->sel_cur = 0L;
+ msgs->select[0] = 1L;
+ }
+ }
+ else if((flags & MI_REFILTERING)
+ && (exbits & (MSG_EX_FILTERED | MSG_EX_TESTED))
+ && !(exbits & MSG_EX_FILED)
+ && (!(exbits & MSG_EX_MANUNDEL)
+ || ((mc = mail_elt(stream, i)) && mc->deleted))
+ && (!(flags & MI_STATECHGONLY) || (exbits & MSG_EX_STATECHG))){
+ /*
+ * We get here if the message was filtered by a filter that
+ * just changes status bits (it wasn't excluded), and now also
+ * if the message was merely tested for filtering. It has also
+ * not been manually undeleted. If it was manually undeleted, we
+ * don't want to reprocess the filter, undoing the user's
+ * manual undeleting. Of course, a new pine will re check this
+ * message anyway, so the user had better be using this
+ * manual undeleting only to temporarily save him or herself
+ * from an expunge before Saving or printing or something.
+ * Also, we want to still try filtering if the message has at
+ * all been marked deleted, even if the there was any manual
+ * undeleting, since this directly precedes an expunge, we want
+ * to make sure the filter does the right thing before getting
+ * rid of the message forever.
+ */
+ exbits &= ~(MSG_EX_FILTERED | MSG_EX_TESTED);
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+ }
+
+ return(ret);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Add the given number of raw message numbers to the end of the
+ current list...
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to add
+ Returns: with fixed up msgno struct
+
+ Only have to adjust the sort array, as since new mail can't cause
+ selection!
+ ----*/
+void
+msgno_add_raw(MSGNO_S *msgs, long int n)
+{
+ long slop, islop, old_total, old_size, old_isize;
+ size_t len, ilen;
+
+ if(!msgs || n <= 0L)
+ return;
+
+ old_total = msgs->max_msgno;
+ old_size = msgs->sort_size;
+ old_isize = msgs->isort_size;
+ slop = (msgs->max_msgno + n + 1L) % 64;
+ islop = (msgs->nmsgs + n + 1L) % 64;
+ msgs->sort_size = (msgs->max_msgno + n + 1L) + (64 - slop);
+ msgs->isort_size = (msgs->nmsgs + n + 1L) + (64 - islop);
+ len = (size_t) msgs->sort_size * sizeof(long);
+ ilen = (size_t) msgs->isort_size * sizeof(long);
+ if(msgs->sort){
+ if(old_size != msgs->sort_size)
+ fs_resize((void **) &(msgs->sort), len);
+ }
+ else
+ msgs->sort = (long *) fs_get(len);
+
+ if(msgs->isort){
+ if(old_isize != msgs->isort_size)
+ fs_resize((void **) &(msgs->isort), ilen);
+ }
+ else
+ msgs->isort = (long *) fs_get(ilen);
+
+ while(n-- > 0){
+ msgs->sort[++msgs->max_msgno] = ++msgs->nmsgs;
+ msgs->isort[msgs->nmsgs] = msgs->max_msgno;
+ }
+
+ if(old_total <= 0L){ /* if no previous messages, */
+ if(!msgs->select){ /* select the new message */
+ msgs->sel_size = 8L;
+ len = (size_t) msgs->sel_size * sizeof(long);
+ msgs->select = (long *) fs_get(len);
+ }
+
+ msgs->sel_cnt = 1L;
+ msgs->sel_cur = 0L;
+ msgs->select[0] = 1L;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Remove all knowledge of the given raw message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ rawno - number to remove
+ Returns: with fixed up msgno struct
+
+ After removing *all* references, adjust the sort array and
+ various pointers accordingly...
+ ----*/
+void
+msgno_flush_raw(MSGNO_S *msgs, long int rawno)
+{
+ long i, old_sorted = 0L;
+ int shift = 0;
+
+ if(!msgs)
+ return;
+
+ /* blast rawno from sort array */
+ for(i = 1L; i <= msgs->max_msgno; i++){
+ if(msgs->sort[i] == rawno){
+ old_sorted = i;
+ shift++;
+ }
+
+ if(shift && i < msgs->max_msgno)
+ msgs->sort[i] = msgs->sort[i + 1L];
+
+ if(msgs->sort[i] > rawno)
+ msgs->sort[i] -= 1L;
+ }
+
+ /*---- now, fixup counts and select array ----*/
+ if(--msgs->nmsgs < 0)
+ msgs->nmsgs = 0L;
+
+ if(old_sorted){
+ if(--msgs->max_msgno < 0)
+ msgs->max_msgno = 0L;
+
+ msgno_flush_selected(msgs, old_sorted);
+ }
+
+ msgno_reset_isort(msgs);
+
+ { char b[100];
+ snprintf(b, sizeof(b),
+ "isort validity: end of msgno_flush_raw: rawno=%ld\n", rawno);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Remove all knowledge of the given selected message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to remove
+ Returns: with fixed up selec members in msgno struct
+
+ Remove reference and fix up selected message numbers beyond
+ the specified number
+ ----*/
+void
+msgno_flush_selected(MSGNO_S *msgs, long int n)
+{
+ long i;
+ int shift = 0;
+
+ for(i = 0L; i < msgs->sel_cnt; i++){
+ if(!shift && (msgs->select[i] == n))
+ shift++;
+
+ if(shift && i + 1L < msgs->sel_cnt)
+ msgs->select[i] = msgs->select[i + 1L];
+
+ if(n < msgs->select[i] || msgs->select[i] > msgs->max_msgno)
+ msgs->select[i] -= 1L;
+ }
+
+ if(shift && msgs->sel_cnt > 1L)
+ msgs->sel_cnt -= 1L;
+}
+
+
+void
+msgno_set_sort(MSGNO_S *msgs, SortOrder sort)
+{
+ if(msgs){
+ if(sort == SortScore)
+ scores_are_used(SCOREUSE_INVALID);
+
+ msgs->sort_order = sort;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Test to see if the given message number is in the selected message
+ list...
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to test
+ Returns: true if n is in selected array, false otherwise
+
+ ----*/
+int
+msgno_in_select(MSGNO_S *msgs, long int n)
+{
+ long i;
+
+ if(msgs)
+ for(i = 0L; i < msgs->sel_cnt; i++)
+ if(msgs->select[i] == n)
+ return(1);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ return our index number for the given raw message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ msgno - number that's important
+ part
+ Returns: our index number of given raw message
+
+ ----*/
+int
+msgno_exceptions(MAILSTREAM *stream, long int rawno, char *part, int *bits, int set)
+{
+ PINELT_S **peltp;
+ PARTEX_S **partp;
+ MESSAGECACHE *mc;
+
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return FALSE;
+
+ /*
+ * Get pointer to exceptional part list, and scan down it
+ * for the requested part...
+ */
+ if((mc = mail_elt(stream, rawno)) && (*(peltp = (PINELT_S **) &mc->sparep)))
+ for(partp = &(*peltp)->exceptions; *partp; partp = &(*partp)->next){
+ if(part){
+ if(!strcmp(part, (*partp)->partno)){
+ if(bits){
+ if(set)
+ (*partp)->handling = *bits;
+ else
+ *bits = (*partp)->handling;
+ }
+
+ return(TRUE); /* bingo! */
+ }
+ }
+ else if(bits){
+ /*
+ * The caller provided flags, but no part.
+ * We are looking to see if the bits are set in any of the
+ * parts. This doesn't count parts with non-digit partno's (like
+ * scores) because those are used differently.
+ * any of the flags...
+ */
+ if((*partp)->partno && *(*partp)->partno &&
+ isdigit((unsigned char) *(*partp)->partno) &&
+ (*bits & (*partp)->handling) == *bits)
+ return(TRUE);
+ }
+ else
+ /*
+ * The caller didn't specify a part, so
+ * they must just be interested in whether
+ * the msg had any exceptions at all...
+ */
+ return(TRUE);
+ }
+
+ if(set && part){
+ if(!*peltp){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ partp = &(*peltp)->exceptions;
+ }
+
+ (*partp) = (PARTEX_S *) fs_get(sizeof(PARTEX_S));
+ (*partp)->partno = cpystr(part);
+ (*partp)->next = NULL;
+ (*partp)->handling = *bits;
+ return(TRUE);
+ }
+
+ if(bits) /* init bits */
+ *bits = 0;
+
+ return(FALSE);
+}
+
+
+/*
+ * Checks whether any parts of any of the messages in msgmap are marked
+ * for deletion.
+ */
+int
+msgno_any_deletedparts(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long n, rawno;
+ PINELT_S *pelt;
+ PARTEX_S **partp;
+ MESSAGECACHE *mc;
+
+ for(n = mn_first_cur(msgmap); n > 0L; n = mn_next_cur(msgmap))
+ if((rawno = mn_m2raw(msgmap, n)) > 0L
+ && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))
+ && (pelt = (PINELT_S *) mc->sparep))
+ for(partp = &pelt->exceptions; *partp; partp = &(*partp)->next)
+ if(((*partp)->handling & MSG_EX_DELETE)
+ && (*partp)->partno
+ && *(*partp)->partno != '0'
+ && isdigit((unsigned char) *(*partp)->partno))
+ return(1);
+
+ return(0);
+}
+
+
+int
+msgno_part_deleted(MAILSTREAM *stream, long int rawno, char *part)
+{
+ char *p;
+ int expbits;
+
+ /*
+ * Is this attachment or any of it's parents in the
+ * MIME structure marked for deletion?
+ */
+ for(p = part; p && *p; p = strindex(++p, '.')){
+ if(*p == '.')
+ *p = '\0';
+
+ (void) msgno_exceptions(stream, rawno, part, &expbits, FALSE);
+ if(!*p)
+ *p = '.';
+
+ if(expbits & MSG_EX_DELETE)
+ return(TRUE);
+ }
+
+ /* Finally, check if the whole message body's deleted */
+ return(msgno_exceptions(stream, rawno, "", &expbits, FALSE)
+ ? (expbits & MSG_EX_DELETE) : FALSE);
+}
+
+
+
+/*
+ * Set the score for a message to score, which can be anything including
+ * SCORE_UNDEF.
+ */
+void
+set_msg_score(MAILSTREAM *stream, long int rawmsgno, long int score)
+{
+ int intscore;
+
+ /* scores are between SCORE_MIN and SCORE_MAX, so ok */
+ intscore = (int) score;
+
+ (void) msgno_exceptions(stream, rawmsgno, "S", &intscore, TRUE);
+}
+
+
+/*
+ * Returns the score for a message. If that score is undefined the value
+ * returned will be SCORE_UNDEF, so the caller has to be prepared for that.
+ * The caller should calculate the undefined scores before calling this.
+ */
+long
+get_msg_score(MAILSTREAM *stream, long int rawmsgno)
+{
+ int s;
+ long score;
+
+ if(msgno_exceptions(stream, rawmsgno, "S", &s, FALSE))
+ score = (long) s;
+ else
+ score = SCORE_UNDEF;
+
+ return(score);
+}
+
+
+void
+clear_msg_score(MAILSTREAM *stream, long int rawmsgno)
+{
+ if(!stream)
+ return;
+
+ set_msg_score(stream, rawmsgno, SCORE_UNDEF);
+}
+
+
+/*
+ * Set all the score values to undefined.
+ */
+void
+clear_folder_scores(MAILSTREAM *stream)
+{
+ long n;
+
+ if(!stream)
+ return;
+
+ for(n = 1L; n <= stream->nmsgs; n++)
+ clear_msg_score(stream, n);
+}
+
+
+/*
+ * Calculates all of the scores for the searchset and stores them in the
+ * mail elts. Careful, this function uses patterns so if the caller is using
+ * patterns then the caller will probably have to reset the pattern functions.
+ * That is, will have to call first_pattern again with the correct type.
+ *
+ * Args: stream
+ * searchset -- calculate scores for this set of messages
+ * no_fetch -- we're in a callback from c-client, don't call c-client
+ *
+ * Returns 1 -- ok
+ * 0 -- error, because of no_fetch
+ */
+int
+calculate_some_scores(MAILSTREAM *stream, SEARCHSET *searchset, int no_fetch)
+{
+ PAT_S *pat = NULL;
+ PAT_STATE pstate;
+ char *savebits;
+ long newscore, addtoscore, score;
+ int error = 0;
+ long rflags = ROLE_SCORE;
+ long n, i;
+ SEARCHSET *s;
+ MESSAGECACHE *mc;
+ HEADER_TOK_S *hdrtok;
+
+ dprint((7, "calculate_some_scores\n"));
+
+ if(nonempty_patterns(rflags, &pstate)){
+
+ /* calculate scores */
+ if(searchset){
+
+ /* this calls match_pattern which messes up searched bits */
+ savebits = (char *)fs_get((stream->nmsgs+1) * sizeof(char));
+ for(i = 1L; i <= stream->nmsgs; i++)
+ savebits[i] = (mc = mail_elt(stream, i)) ? mc->searched : 0;
+
+ /*
+ * First set all the scores in the searchset to zero so that they
+ * will no longer be undefined.
+ */
+ score = 0L;
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ set_msg_score(stream, n, score);
+
+ for(pat = first_pattern(&pstate);
+ !error && pat;
+ pat = next_pattern(&pstate)){
+
+ newscore = pat->action->scoreval;
+ hdrtok = pat->action->scorevalhdrtok;
+
+ /*
+ * This no_fetch probably isn't necessary since
+ * we will actually have fetched this with
+ * the envelope. Just making sure.
+ */
+ if(hdrtok && no_fetch){
+ error++;
+ break;
+ }
+
+ switch(match_pattern(pat->patgrp, stream, searchset, NULL, NULL,
+ (no_fetch ? MP_IN_CCLIENT_CB : 0)
+ | (SE_NOSERVER|SE_NOPREFETCH))){
+ case 1:
+ if(!pat->action || pat->action->bogus)
+ break;
+
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ if(n > 0L && stream && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n)) && mc->searched){
+ if((score = get_msg_score(stream,n)) == SCORE_UNDEF)
+ score = 0L;
+
+ if(hdrtok)
+ addtoscore = scorevalfrommsg(stream, n, hdrtok, no_fetch);
+ else
+ addtoscore = newscore;
+
+ score += addtoscore;
+ set_msg_score(stream, n, score);
+ }
+
+ break;
+
+ case 0:
+ break;
+
+ case -1:
+ error++;
+ break;
+ }
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = savebits[i];
+
+ fs_give((void **)&savebits);
+
+ if(error){
+ /*
+ * Revert to undefined scores.
+ */
+ score = SCORE_UNDEF;
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ set_msg_score(stream, n, score);
+ }
+ }
+ }
+
+ return(error ? 0 : 1);
+}
+
+
+void
+free_pine_elt(void **sparep)
+{
+ PINELT_S **peltp;
+
+ peltp = (PINELT_S **) sparep;
+
+ if(peltp && *peltp){
+ msgno_free_exceptions(&(*peltp)->exceptions);
+ if((*peltp)->pthrd)
+ fs_give((void **) &(*peltp)->pthrd);
+
+ if((*peltp)->ice)
+ free_ice(&(*peltp)->ice);
+
+ fs_give((void **) peltp);
+ }
+}