* Update configure script to remove the search for a linking tcl
[alpine.git] / pith / sequence.c
blob1e44263049bf4a0bd13460dbe24937bd9cd976e7
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: sequence.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/sequence.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/msgno.h"
24 #include "../pith/flag.h"
25 #include "../pith/thread.h"
26 #include "../pith/icache.h"
30 * Internal prototypes
34 /*----------------------------------------------------------------------
35 Build comma delimited list of selected messages
37 Args: stream -- mail stream to use for flag testing
38 msgmap -- message number struct of to build selected messages in
39 count -- pointer to place to write number of comma delimited
40 mark -- mark manually undeleted so we don't refilter right away
42 Returns: malloc'd string containing sequence, else NULL if
43 no messages in msgmap with local "selected" flag.
44 ----*/
45 char *
46 selected_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count, int mark)
48 long i;
49 MESSAGECACHE *mc;
51 if(!stream)
52 return(NULL);
55 * The plan here is to use the c-client elt's "sequence" bit
56 * to work around any orderings or exclusions in pine's internal
57 * mapping that might cause the sequence to be artificially
58 * lengthy. It's probably cheaper to run down the elt list
59 * twice rather than call nm_raw2m() for each message as
60 * we run down the elt list once...
62 for(i = 1L; i <= stream->nmsgs; i++)
63 if((mc = mail_elt(stream, i)) != NULL)
64 mc->sequence = 0;
66 for(i = 1L; i <= mn_get_total(msgmap); i++)
67 if(get_lflag(stream, msgmap, i, MN_SLCT)){
68 long rawno;
69 int exbits = 0;
72 * Forget we knew about it, and set "add to sequence"
73 * bit...
75 clear_index_cache_ent(stream, i, 0);
76 rawno = mn_m2raw(msgmap, i);
77 if(rawno > 0L && rawno <= stream->nmsgs
78 && (mc = mail_elt(stream, rawno)))
79 mc->sequence = 1;
82 * Mark this message manually flagged so we don't re-filter it
83 * with a filter which only sets flags.
85 if(mark){
86 if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
87 exbits |= MSG_EX_MANUNDEL;
88 else
89 exbits = MSG_EX_MANUNDEL;
91 msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
95 return(build_sequence(stream, NULL, count));
99 /*----------------------------------------------------------------------
100 Build comma delimited list of messages current in msgmap which have all
101 flags matching the arguments
103 Args: stream -- mail stream to use for flag testing
104 msgmap -- only consider messages selected in this msgmap
105 flag -- flags to match against
106 count -- pointer to place to return number of comma delimited
107 mark -- mark index cache entry changed, and count state change
108 kw_on -- if flag contains F_KEYWORD, this is
109 the array of keywords to be checked
110 kw_off -- if flag contains F_UNKEYWORD, this is
111 the array of keywords to be checked
113 Returns: malloc'd string containing sequence, else NULL if
114 no messages in msgmap with local "selected" flag (a flag
115 of zero means all current msgs).
116 ----*/
117 char *
118 currentf_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int flag,
119 long int *count, int mark, char **kw_on, char **kw_off)
121 char *seq, **t;
122 long i, rawno;
123 int exbits;
124 MESSAGECACHE *mc;
126 if(!stream)
127 return(NULL);
129 /* First, make sure elts are valid for all the interesting messages */
130 if((seq = invalid_elt_sequence(stream, msgmap)) != NULL){
131 pine_mail_fetch_flags(stream, seq, NIL);
132 fs_give((void **) &seq);
135 for(i = 1L; i <= stream->nmsgs; i++)
136 if((mc = mail_elt(stream, i)) != NULL)
137 mc->sequence = 0; /* clear "sequence" bits */
139 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
140 /* if not already set, go on... */
141 rawno = mn_m2raw(msgmap, i);
142 mc = (rawno > 0L && rawno <= stream->nmsgs)
143 ? mail_elt(stream, rawno) : NULL;
144 if(!mc)
145 continue;
147 if((flag == 0)
148 || ((flag & F_DEL) && mc->deleted)
149 || ((flag & F_UNDEL) && !mc->deleted)
150 || ((flag & F_SEEN) && mc->seen)
151 || ((flag & F_UNSEEN) && !mc->seen)
152 || ((flag & F_ANS) && mc->answered)
153 || ((flag & F_UNANS) && !mc->answered)
154 || ((flag & F_FLAG) && mc->flagged)
155 || ((flag & F_UNFLAG) && !mc->flagged)){
156 mc->sequence = 1; /* set "sequence" flag */
159 /* check for forwarded status */
160 if(!mc->sequence
161 && (flag & F_FWD)
162 && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
163 mc->sequence = 1;
166 if(!mc->sequence
167 && (flag & F_UNFWD)
168 && !user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
169 mc->sequence = 1;
172 /* check for user keywords or not */
173 if(!mc->sequence && flag & F_KEYWORD && kw_on){
174 for(t = kw_on; !mc->sequence && *t; t++)
175 if(user_flag_is_set(stream, rawno, *t))
176 mc->sequence = 1;
178 else if(!mc->sequence && flag & F_UNKEYWORD && kw_off){
179 for(t = kw_off; !mc->sequence && *t; t++)
180 if(!user_flag_is_set(stream, rawno, *t))
181 mc->sequence = 1;
184 if(mc->sequence){
185 if(mark){
186 if(THRD_INDX()){
187 PINETHRD_S *thrd;
188 long t;
190 /* clear thread index line instead of index index line */
191 thrd = fetch_thread(stream, mn_m2raw(msgmap, i));
192 if(thrd && thrd->top
193 && (thrd=fetch_thread(stream,thrd->top))
194 && (t = mn_raw2m(msgmap, thrd->rawno)))
195 clear_index_cache_ent(stream, t, 0);
197 else
198 clear_index_cache_ent(stream, i, 0);/* force new index line */
201 * Mark this message manually flagged so we don't re-filter it
202 * with a filter which only sets flags.
204 exbits = 0;
205 if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
206 exbits |= MSG_EX_MANUNDEL;
207 else
208 exbits = MSG_EX_MANUNDEL;
210 msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
215 return(build_sequence(stream, NULL, count));
219 /*----------------------------------------------------------------------
220 Return sequence numbers of messages with invalid MESSAGECACHEs
222 Args: stream -- mail stream to use for flag testing
223 msgmap -- message number struct of to build selected messages in
225 Returns: malloc'd string containing sequence, else NULL if
226 no messages in msgmap with local "selected" flag (a flag
227 of zero means all current msgs).
228 ----*/
229 char *
230 invalid_elt_sequence(MAILSTREAM *stream, MSGNO_S *msgmap)
232 long i, rawno;
233 MESSAGECACHE *mc;
235 if(!stream)
236 return(NULL);
238 for(i = 1L; i <= stream->nmsgs; i++)
239 if((mc = mail_elt(stream, i)) != NULL)
240 mc->sequence = 0; /* clear "sequence" bits */
242 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap))
243 if((rawno = mn_m2raw(msgmap, i)) > 0L && rawno <= stream->nmsgs
244 && (mc = mail_elt(stream, rawno)) && !mc->valid)
245 mc->sequence = 1;
247 return(build_sequence(stream, NULL, NULL));
251 /*----------------------------------------------------------------------
252 Build comma delimited list of messages with elt "sequence" bit set
254 Args: stream -- mail stream to use for flag testing
255 msgmap -- struct containing sort to build sequence in
256 count -- pointer to place to write number of comma delimited
257 NOTE: if non-zero, it's a clue as to how many messages
258 have the sequence bit lit.
260 Returns: malloc'd string containing sequence, else NULL if
261 no messages in msgmap with elt's "sequence" bit set
262 ----*/
263 char *
264 build_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count)
266 #define SEQ_INCREMENT 128
267 long n = 0L, i, x, lastn = 0L, runstart = 0L;
268 size_t size = SEQ_INCREMENT;
269 char *seq = NULL, *p;
270 MESSAGECACHE *mc;
272 if(!stream)
273 return(NULL);
275 if(count){
276 if(*count > 0L)
277 size = MAX(size, MIN((*count) * 4, 16384));
279 *count = 0L;
282 for(x = 1L; x <= stream->nmsgs; x++){
283 if(msgmap){
284 if((i = mn_m2raw(msgmap, x)) == 0L)
285 continue;
287 else
288 i = x;
290 if(i > 0L && i <= stream->nmsgs
291 && (mc = mail_elt(stream, i)) && mc->sequence){
292 n++;
293 if(!seq) /* initialize if needed */
294 seq = p = fs_get(size);
297 * This code will coalesce the ascending runs of
298 * sequence numbers, but fails to break sequences
299 * into a reasonably sensible length for imapd's to
300 * swallow (reasonable addtition to c-client?)...
302 if(lastn){ /* if may be in a run */
303 if(lastn + 1L == i){ /* and its the next raw num */
304 lastn = i; /* skip writing anything... */
305 continue;
307 else if(runstart != lastn){
308 *p++ = (runstart + 1L == lastn) ? ',' : ':';
309 sstrncpy(&p, long2string(lastn), size-(p-seq));
310 } /* wrote end of run */
313 runstart = lastn = i; /* remember last raw num */
315 if(n > 1L && (p-seq) < size) /* !first num, write delim */
316 *p++ = ',';
318 if(size - (p - seq) < 16){ /* room for two more nums? */
319 size_t offset = p - seq; /* grow the sequence array */
320 size += SEQ_INCREMENT;
321 fs_resize((void **)&seq, size);
322 p = seq + offset;
325 sstrncpy(&p, long2string(i), size-(p-seq)); /* write raw number */
329 if(lastn && runstart != lastn){ /* were in a run? */
330 if((p-seq) < size) /* !first num, write delim */
331 *p++ = (runstart + 1L == lastn) ? ',' : ':';
333 sstrncpy(&p, long2string(lastn), size-(p-seq)); /* write the trailing num */
336 if(seq && (p-seq) < size) /* if sequence, tie it off */
337 *p = '\0';
339 if(seq)
340 seq[size-1] = '\0';
342 if(count)
343 *count = n;
345 return(seq);
349 /*----------------------------------------------------------------------
350 If any messages flagged "selected", fake the "currently selected" array
352 Args: map -- message number struct of to build selected messages in
354 OK folks, here's the tradeoff: either all the functions have to
355 know if the user want's to deal with the "current" hilited message
356 or the list of currently "selected" messages, *or* we just
357 wrap the call to these functions with some glue that tweeks
358 what these functions see as the "current" message list, and let them
359 do their thing.
360 ----*/
362 pseudo_selected(MAILSTREAM *stream, MSGNO_S *map)
364 long i, later = 0L;
366 if(any_lflagged(map, MN_SLCT)){
367 map->hilited = mn_m2raw(map, mn_get_cur(map));
369 for(i = 1L; i <= mn_get_total(map); i++)
370 if(get_lflag(stream, map, i, MN_SLCT)){
371 if(!later++){
372 mn_set_cur(map, i);
374 else{
375 mn_add_cur(map, i);
379 return(1);
382 return(0);
386 /*----------------------------------------------------------------------
387 Antidote for the monkey business committed above
389 Args: map -- message number struct of to build selected messages in
391 ----*/
392 void
393 restore_selected(MSGNO_S *map)
395 if(map->hilited){
396 mn_reset_cur(map, mn_raw2m(map, map->hilited));
397 map->hilited = 0L;
403 * Return a search set which can be used to limit the search to a smaller set,
404 * for performance reasons. The caller must still work correctly if we return
405 * the whole set (or NULL) here, because we may not be able to send the full
406 * search set over IMAP. In cases where the search set description is getting
407 * too large we send a search set which contains all of the relevant messages.
408 * It may contain more.
410 * Args stream
411 * narrow -- If set, we are narrowing our selection (restrict to
412 * messages with MN_SLCT already set) or if not set,
413 * we are broadening (so we may look only at messages
414 * with MN_SLCT not set)
416 * Returns - allocated search set or NULL. Caller is responsible for freeing it.
418 SEARCHSET *
419 limiting_searchset(MAILSTREAM *stream, int narrow)
421 long n, run;
422 int cnt = 0;
423 SEARCHSET *full_set = NULL, **set;
426 * If we're talking to anything other than a server older than
427 * imap 4rev1, build a searchset otherwise it'll choke.
429 if(!(is_imap_stream(stream) && !modern_imap_stream(stream))){
430 for(n = 1L, set = &full_set, run = 0L; n <= stream->nmsgs; n++)
431 /* test for end of run */
432 if(get_lflag(stream, NULL, n, MN_EXLD)
433 || (narrow && !get_lflag(stream, NULL, n, MN_SLCT))
434 || (!narrow && get_lflag(stream, NULL, n, MN_SLCT))){
435 if(run){ /* previous selected? */
436 set = &(*set)->next;
437 run = 0L;
440 else if(run++){ /* next in run */
441 (*set)->last = n;
443 else{ /* start of run */
444 *set = mail_newsearchset();
445 (*set)->first = (*set)->last = n;
448 * Make this last set cover the rest of the messages.
449 * We could be fancier about this but it probably isn't
450 * worth the trouble.
452 if(++cnt > 100){
453 (*set)->last = stream->nmsgs;
454 break;
459 return(full_set);