1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: sort.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2013-2021 Eduardo Chappa
8  * Copyright 2006-2007 University of Washington
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 #include "../pith/headers.h"
20 #include "../pith/sort.h"
21 #include "../pith/state.h"
22 #include "../pith/status.h"
23 #include "../pith/msgno.h"
24 #include "../pith/flag.h"
25 #include "../pith/pineelt.h"
26 #include "../pith/thread.h"
27 #include "../pith/search.h"
28 #include "../pith/pattern.h"
29 #include "../pith/util.h"
30 #include "../pith/signal.h"
31 #include "../pith/busy.h"
32 #include "../pith/icache.h"
33 
34 
35 /*
36  * global place to store mail_sort and mail_thread results
37  */
38 struct global_sort_data g_sort;
39 
40 
41 /*
42  * Internal prototypes
43  */
44 void	sort_sort_callback(MAILSTREAM *, unsigned long *, unsigned long);
45 int     percent_sorted(void);
46 int	pine_compare_long(const qsort_t *, const qsort_t *);
47 int	pine_compare_long_rev(const qsort_t *, const qsort_t *);
48 int	pine_compare_scores(const qsort_t *, const qsort_t *);
49 void	build_score_array(MAILSTREAM *, MSGNO_S *);
50 void	free_score_array(void);
51 
52 
53 /*----------------------------------------------------------------------
54     Map sort types to names
55   ----*/
56 char *
sort_name(SortOrder so)57 sort_name(SortOrder so)
58 {
59     /*
60      * Make sure the first upper case letter of any new sort name is
61      * unique.  The command char and label for sort selection is
62      * derived from this name and its first upper case character.
63      * See mailcmd.c:select_sort().
64      */
65     return((so == SortArrival)  ? "Arrival" :
66 	    (so == SortDate)	 ? "Date" :
67 	     (so == SortSubject)  ? "Subject" :
68 	      (so == SortCc)	   ? "Cc" :
69 	       (so == SortFrom)	    ? "From" :
70 		(so == SortTo)	     ? "To" :
71 		 (so == SortSize)     ? "siZe" :
72 		  (so == SortSubject2) ? "OrderedSubj" :
73 		   (so == SortScore)    ? "scorE" :
74 		     (so == SortThread)  ? "tHread" :
75 					  "BOTCH");
76 }
77 
78 
79 /*----------------------------------------------------------------------
80     Sort the current folder into the order set in the msgmap
81 
82 Args: msgmap --
83       new_sort --
84       new_rev --
85 
86     The idea of the deferred sort is to let the user interrupt a long sort
87     and have a chance to do a different command, such as a sort by arrival
88     or a Goto.  The next newmail call will increment the deferred variable,
89     then the user may do a command, then the newmail call after that
90     causes the sort to happen if it is still needed.
91   ----*/
92 void
sort_folder(MAILSTREAM * stream,MSGNO_S * msgmap,SortOrder new_sort,int new_rev,unsigned int flags)93 sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort,
94 	    int new_rev, unsigned int flags)
95 {
96     long	   raw_current, i, j;
97     unsigned long *sort = NULL;
98     int		   we_cancel = 0;
99     char	   sort_msg[MAX_SCREEN_COLS+1];
100     SortOrder      current_sort;
101     int	           current_rev;
102     MESSAGECACHE  *mc;
103 
104     dprint((2, "Sorting by %s%s\n",
105 	       sort_name(new_sort), new_rev ? "/reverse" : ""));
106 
107     if(!msgmap)
108       return;
109 
110     current_sort = mn_get_sort(msgmap);
111     current_rev = mn_get_revsort(msgmap);
112     /*
113      * If we were previously threading (spare == 1) and now we're switching
114      * sorts (other than just a rev switch) then erase the information
115      * about the threaded state (collapsed and so forth).
116      */
117     if(stream && stream->spare && (current_sort != new_sort))
118       erase_threading_info(stream, msgmap);
119 
120     if(mn_get_total(msgmap) <= 1L
121        && !(mn_get_total(msgmap) == 1L
122             && (new_sort == SortThread || new_sort == SortSubject2))){
123 	mn_set_sort(msgmap, new_sort);
124 	mn_set_revsort(msgmap, new_rev);
125 	if(!mn_get_mansort(msgmap))
126 	  mn_set_mansort(msgmap, (flags & SRT_MAN) ? 1 : 0);
127 
128 	return;
129     }
130 
131     raw_current = mn_m2raw(msgmap, mn_get_cur(msgmap));
132 
133     if(new_sort == SortArrival){
134 	/*
135 	 * NOTE: RE c-client sorting, our idea of arrival is really
136 	 *	 just the natural sequence order.  C-client, and probably
137 	 *	 rightly so, considers "arrival" the order based on the
138 	 *	 message's internal date.  This is more costly to compute
139 	 *	 since it means touching the message store (say "nntp"?),
140 	 *	 so we just preempt it here.
141 	 *
142 	 *	 Someday c-client will support "unsorted" and do what
143 	 *	 we're doing here.  That day this gets scrapped.
144 	 */
145 
146 	mn_set_sort(msgmap, new_sort);
147 	mn_set_revsort(msgmap, new_rev);
148 
149 	if(current_sort != new_sort || current_rev != new_rev ||
150 	   any_lflagged(msgmap, MN_EXLD))
151 	  clear_index_cache(stream, 0);
152 
153 	if(any_lflagged(msgmap, MN_EXLD)){
154 	    /*
155 	     * BEWARE: "exclusion" may leave holes in the unsorted sort order
156 	     * so we have to do a real sort if that is the case.
157 	     */
158 	    qsort(msgmap->sort+1, (size_t) mn_get_total(msgmap),
159 		  sizeof(long),
160 		  new_rev ? pine_compare_long_rev : pine_compare_long);
161 	}
162 	else if(mn_get_total(msgmap) > 0L){
163 	    if(new_rev){
164 		clear_index_cache(stream, 0);
165 		for(i = 1L, j = mn_get_total(msgmap); j >= 1; i++, j--)
166 		  msgmap->sort[i] = j;
167 	    }
168 	    else
169 	      for(i = 1L; i <= mn_get_total(msgmap); i++)
170 		msgmap->sort[i] = i;
171 	}
172 
173 	/* reset the inverse array */
174 	msgno_reset_isort(msgmap);
175     }
176     else if(new_sort == SortScore){
177 
178 	/*
179 	 * We have to build a temporary array which maps raw msgno to
180 	 * score. We use the index cache machinery to build the array.
181 	 */
182 
183 	mn_set_sort(msgmap, new_sort);
184 	mn_set_revsort(msgmap, new_rev);
185 
186 	if(flags & SRT_VRB){
187 	    /* TRANSLATORS: tell user they are waiting for Sorting of %s, the foldername */
188 	    snprintf(sort_msg, sizeof(sort_msg), _("Sorting \"%s\""),
189 		    strsquish(tmp_20k_buf + 500, 500, ps_global->cur_folder,
190 			      ps_global->ttyo->screen_cols - 20));
191 	    we_cancel = busy_cue(sort_msg, NULL, 0);
192 	}
193 
194 	/*
195 	 * We do this so that we don't have to lookup the scores with function
196 	 * calls for each qsort compare.
197 	 */
198 	build_score_array(stream, msgmap);
199 
200 	qsort(msgmap->sort+1, (size_t) mn_get_total(msgmap),
201 	      sizeof(long), pine_compare_scores);
202 	free_score_array();
203 	clear_index_cache(stream, 0);
204 
205 	if(we_cancel)
206 	  cancel_busy_cue(1);
207 
208 	/*
209 	 * Flip the sort if necessary (cheaper to do it once than for
210 	 * every comparison in pine_compare_scores.
211 	 */
212 	if(new_rev && mn_get_total(msgmap) > 1L){
213 	    long *ep = &msgmap->sort[mn_get_total(msgmap)],
214 		 *sp = &msgmap->sort[1], tmp;
215 
216 	    do{
217 		tmp   = *sp;
218 		*sp++ = *ep;
219 		*ep-- = tmp;
220 	    }
221 	    while(ep > sp);
222 	}
223 
224 	/* reset the inverse array */
225 	msgno_reset_isort(msgmap);
226     }
227     else{
228 
229 	mn_set_sort(msgmap, new_sort);
230 	mn_set_revsort(msgmap, new_rev);
231 	clear_index_cache(stream, 0);
232 
233 	if(flags & SRT_VRB){
234 	    int (*sort_func)() = NULL;
235 
236 	    /*
237 	     * IMAP sort doesn't give us any way to get progress,
238 	     * so just spin the bar rather than show zero percent
239 	     * forever while a slow sort's going on...
240 	     */
241 	    if(!(stream && stream->dtb && stream->dtb->name
242 		 && !strucmp(stream->dtb->name, "imap")
243 		 && LEVELSORT(stream)))
244 	      sort_func = percent_sorted;
245 
246 	    snprintf(sort_msg, sizeof(sort_msg), _("Sorting \"%s\""),
247 		    strsquish(tmp_20k_buf + 500, 500, ps_global->cur_folder,
248 			      ps_global->ttyo->screen_cols - 20));
249 	    we_cancel = busy_cue(sort_msg, sort_func, 0);
250 	}
251 
252 	/*
253 	 * Limit the sort/thread if messages are hidden from view
254 	 * by lighting searched bit of every interesting msg in
255 	 * the folder and call c-client thread/sort to do the dirty work.
256 	 *
257 	 * Unfortunately it isn't that easy. IMAP servers are not able to
258 	 * handle medium to large sized sequence sets (more than 1000
259 	 * characters in the command line breaks some) so we have to try
260 	 * to handle it locally. By lighting the searched bits and
261 	 * providing a NULL search program we get a special c-client
262 	 * interface. This means that c-client will attempt to send the
263 	 * sequence set with the SORT or THREAD but it may get back
264 	 * a BAD response because of long command lines. In that case,
265 	 * if it is a SORT call, c-client will issue the full SORT
266 	 * without the sequence sets and will then filter the results
267 	 * locally. So sort_sort_callback will see the correctly
268 	 * filtered results. If it is a mail_thread call, a similar thing
269 	 * will be done. If a BAD is received, then there is no way to
270 	 * easily filter the results. C-client (in this special case where
271 	 * we provide a NULL search program) will set tree->num to zero
272 	 * for nodes of the thread tree which were supposed to be
273 	 * filtered out of the thread. Then pine, in sort_thread_callback,
274 	 * will treat those as dummy nodes (nodes which are part of the
275 	 * tree logically but where we don't have access to the messages).
276 	 * This will give us a different answer than we would have gotten
277 	 * if the restricted thread would have worked, but it's something.
278 	 *
279 	 * It isn't in general possible to give some shorter search set
280 	 * in place of the long sequence set because the long sequence set
281 	 * may be the result of several filter rules or of excluded
282 	 * messages in news (in this 2nd case maybe we could give
283 	 * a shorter search set).
284 	 *
285 	 * We note also that the too-long commands in imap is a general
286 	 * imap deficiency. It comes up in particular also in SEARCH
287 	 * commands. Pine likes to exclude the hidden messages from the
288 	 * SEARCH. SEARCH will be handled transparently by the local
289 	 * c-client by first issuing the full SEARCH command, if that
290 	 * comes back with a BAD and there is a pgm->msgno at the top
291 	 * level of pgm, then c-client will re-issue the SEARCH command
292 	 * but without the msgno sequence set in hopes that the resulting
293 	 * command will now be short enough, and then it will filter out
294 	 * the sequence set locally. If that doesn't work, it will
295 	 * download the messages and do the SEARCH locally. That is
296 	 * controllable by a flag bit.
297 	 */
298 	for(i = 1L; i <= stream->nmsgs; i++)
299 	  if((mc = mail_elt(stream, i)) != NULL)
300 	    mc->searched = !get_lflag(stream, NULL, i, MN_EXLD);
301 
302 	g_sort.msgmap = msgmap;
303 	if(new_sort == SortThread || new_sort == SortSubject2){
304 	    THREADNODE *thread;
305 
306 	    /*
307 	     * Install callback to collect thread results
308 	     * and update sort mapping.  Problem this solves
309 	     * is that of receiving exists/expunged events
310 	     * within sort/thread response.  Since we update
311 	     * the sorted table within those handlers, we
312 	     * can get out of sync when we replace possibly
313 	     * stale sort/thread results once the function
314 	     * call's returned.  Make sense?  Thought so.
315 	     */
316 	    mail_parameters(NULL, SET_THREADRESULTS,
317 			    (void *) sort_thread_callback);
318 
319 	    thread = mail_thread(stream,
320 				 (new_sort == SortThread)
321 				   ? "REFERENCES" : "ORDEREDSUBJECT",
322 				 NULL, NULL, 0L);
323 
324 	    mail_parameters(NULL, SET_THREADRESULTS, (void *) NULL);
325 
326 	    if(!thread){
327 		new_sort = current_sort;
328 		new_rev  = current_rev;
329 		q_status_message1(SM_ORDER, 3, 3,
330 				  "Sort Failed!  Restored %.200s sort.",
331 				  sort_name(new_sort));
332 	    }
333 
334 	    if(thread)
335 	      mail_free_threadnode(&thread);
336 	}
337 	else{
338 	    /*
339 	     * Set up the sort program.
340 	     * NOTE: we deal with reverse bit below.
341 	     */
342 	    g_sort.prog = mail_newsortpgm();
343 	    g_sort.prog->function = (new_sort == SortSubject)
344 				     ? SORTSUBJECT
345 				     : (new_sort == SortFrom)
346 					? SORTFROM
347 					: (new_sort == SortTo)
348 					   ? SORTTO
349 					   : (new_sort == SortCc)
350 					      ? SORTCC
351 					      : (new_sort == SortDate)
352 						 ? SORTDATE
353 						 : (new_sort == SortSize)
354 						    ? SORTSIZE
355 						    : SORTARRIVAL;
356 
357 	    mail_parameters(NULL, SET_SORTRESULTS,
358 			    (void *) sort_sort_callback);
359 
360 	    /* Where the rubber meets the road. */
361 	    sort = mail_sort(stream, NULL, NULL, g_sort.prog, 0L);
362 
363 	    mail_parameters(NULL, SET_SORTRESULTS, (void *) NULL);
364 
365 	    if(!sort){
366 		new_sort = current_sort;
367 		new_rev  = current_rev;
368 		q_status_message1(SM_ORDER, 3, 3,
369 				  "Sort Failed!  Restored %s sort.",
370 				  sort_name(new_sort));
371 	    }
372 
373 	    if(sort)
374 	      fs_give((void **) &sort);
375 
376 	    mail_free_sortpgm(&g_sort.prog);
377 	}
378 
379 	if(we_cancel)
380 	  cancel_busy_cue(1);
381 
382 	/*
383 	 * Flip the sort if necessary (cheaper to do it once than for
384 	 * every comparison underneath mail_sort()
385 	 */
386 	if(new_rev && mn_get_total(msgmap) > 1L){
387 	    long *ep = &msgmap->sort[mn_get_total(msgmap)],
388 		 *sp = &msgmap->sort[1], tmp;
389 
390 	    do{
391 		tmp   = *sp;
392 		*sp++ = *ep;
393 		*ep-- = tmp;
394 	    }
395 	    while(ep > sp);
396 
397 	    /* reset the inverse array */
398 	    msgno_reset_isort(msgmap);
399 
400 	    /*
401 	     * Flip the thread numbers around.
402 	     * This puts us in a weird state that requires keeping track
403 	     * of. The direction of the thread list hasn't changed, but the
404 	     * thrdnos have and the display direction has.
405 	     *
406 	     * For Sort   thrdno 1       thread list head
407 	     *            thrdno 2              |
408 	     *            thrdno .              v  nextthd this direction
409 	     *            thrdno .
410 	     *            thrdno n       thread list tail
411 	     *
412 	     * Rev Sort   thrdno 1       thread list tail
413 	     *            thrdno 2
414 	     *            thrdno .              ^  nextthd this direction
415 	     *            thrdno .              |
416 	     *            thrdno n       thread list head
417 	     */
418 	    if(new_sort == SortThread || new_sort == SortSubject2){
419 		PINETHRD_S *thrd;
420 
421 		thrd = fetch_head_thread(stream);
422 		for(j = msgmap->max_thrdno; thrd && j >= 1L; j--){
423 		    thrd->thrdno = j;
424 
425 		    if(thrd->nextthd)
426 		      thrd = fetch_thread(stream,
427 					  thrd->nextthd);
428 		    else
429 		      thrd = NULL;
430 		}
431 	    }
432 	}
433     }
434 
435     /* Fix up sort structure */
436     mn_set_sort(msgmap, new_sort);
437     mn_set_revsort(msgmap, new_rev);
438     /*
439      * Once a folder has been sorted manually, we continue treating it
440      * as manually sorted until it is closed.
441      */
442     if(!mn_get_mansort(msgmap))
443       mn_set_mansort(msgmap, (flags & SRT_MAN) ? 1 : 0);
444 
445     if(!msgmap->hilited){
446 	/*
447 	 * If current is hidden, change current to visible parent.
448 	 * It can only be hidden if we are threading.
449 	 *
450 	 * Don't do this if hilited is set, because it means we're in the
451 	 * middle of an aggregate op, and this will mess up our selection.
452 	 * "hilited" means we've done a pseudo_selected, which we'll later
453 	 * fix with restore_selected.
454 	 */
455 	if(THREADING())
456 	  mn_reset_cur(msgmap, first_sorted_flagged(new_rev ? F_NONE : F_SRCHBACK,
457 						    stream,
458 						    mn_raw2m(msgmap, raw_current),
459 						    FSF_SKIP_CHID));
460 	else
461 	  mn_reset_cur(msgmap, mn_raw2m(msgmap, raw_current));
462     }
463 
464     msgmap->top = -1L;
465 
466     if(!sp_mail_box_changed(stream))
467       sp_set_unsorted_newmail(stream, 0);
468 
469     /*
470      * Turn off the MN_USOR flag. Don't bother going through the
471      * function call and the message number mappings.
472      */
473     if(THREADING()){
474 	PINELT_S *pelt;
475 
476 	for(i = 1L; i <= stream->nmsgs; i++)
477 	  if((mc = mail_elt(stream, i)) != NULL && (pelt = mc->sparep) != NULL)
478 	    pelt->unsorted = 0;
479     }
480 }
481 
482 
483 void
sort_sort_callback(MAILSTREAM * stream,long unsigned int * list,long unsigned int nmsgs)484 sort_sort_callback(MAILSTREAM *stream, long unsigned int *list, long unsigned int nmsgs)
485 {
486     long i;
487 
488     dprint((2, "sort_sort_callback\n"));
489 
490     if(mn_get_total(g_sort.msgmap) < nmsgs)
491       alpine_panic("Message count shrank after sort!");
492 
493     /* copy ulongs to array of longs */
494     for(i = nmsgs; i > 0; i--)
495       g_sort.msgmap->sort[i] = list ? (long) list[i-1] : i-1;
496 
497     /* reset the inverse array */
498     msgno_reset_isort(g_sort.msgmap);
499 
500     dprint((2, "sort_sort_callback done\n"));
501 }
502 
503 
504 /*
505  * Return value for use by progress bar.
506  */
507 int
percent_sorted(void)508 percent_sorted(void)
509 {
510     /*
511      * C-client's sort routine exports two types of status
512      * indicators.  One's the progress thru loading the cache (typically
513      * the elephantine bulk of the delay, and the progress thru the
514      * actual sort (typically qsort).  Our job is to balance the two
515      */
516 
517     return(g_sort.prog && g_sort.prog->nmsgs
518       ? (((((g_sort.prog->progress.cached * 100)
519 					 / g_sort.prog->nmsgs) * 9) / 10)
520 	 + (((g_sort.prog->progress.sorted) * 10)
521 					 / g_sort.prog->nmsgs))
522       : 0);
523 }
524 
525 
526 /*
527  * This is only used at startup time.  It sets ps->def_sort and
528  * ps->def_sort_rev.  The syntax of the sort_spec is type[/reverse].
529  * A reverse without a type is the same as arrival/reverse.  A blank
530  * argument also means arrival/reverse.
531  */
532 int
decode_sort(char * sort_spec,SortOrder * def_sort,int * def_sort_rev)533 decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev)
534 {
535     char *sep;
536     char *fix_this = NULL;
537     int   x, reverse;
538 
539     if(!sort_spec || !*sort_spec){
540 	*def_sort = SortArrival;
541 	*def_sort_rev = 0;
542         return(0);
543     }
544 
545     if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){
546 	*def_sort = SortArrival;
547 	*def_sort_rev = 1;
548         return(0);
549     }
550 
551     reverse = 0;
552     if((sep = strindex(sort_spec, '/')) != NULL){
553         *sep = '\0';
554 	fix_this = sep;
555         sep++;
556         if(struncmp(sep, "reverse", strlen(sep)) == 0)
557           reverse = 1;
558         else{
559 	    *fix_this = '/';
560 	    return(-1);
561 	}
562     }
563 
564     for(x = 0; ps_global->sort_types[x] != EndofList; x++)
565       if(struncmp(sort_name(ps_global->sort_types[x]),
566                   sort_spec, strlen(sort_spec)) == 0)
567         break;
568 
569     if(fix_this)
570       *fix_this = '/';
571 
572     if(ps_global->sort_types[x] == EndofList)
573       return(-1);
574 
575     *def_sort     = ps_global->sort_types[x];
576     *def_sort_rev = reverse;
577     return(0);
578 }
579 
580 
581 /*----------------------------------------------------------------------
582   Compare raw message numbers
583  ----*/
584 int
pine_compare_long(const qsort_t * a,const qsort_t * b)585 pine_compare_long(const qsort_t *a, const qsort_t *b)
586 {
587     long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
588 
589     return((mdiff = *mess_a - *mess_b) ? ((mdiff > 0L) ? 1 : -1) : 0);
590 }
591 
592 /*
593  * reverse version of pine_compare_long
594  */
595 int
pine_compare_long_rev(const qsort_t * a,const qsort_t * b)596 pine_compare_long_rev(const qsort_t *a, const qsort_t *b)
597 {
598     long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
599 
600     return((mdiff = *mess_a - *mess_b) ? ((mdiff < 0L) ? 1 : -1) : 0);
601 }
602 
603 
604 long *g_score_arr;
605 
606 /*
607  * This calculate all of the scores and also puts them into a temporary array
608  * for speed when sorting.
609  */
610 void
build_score_array(MAILSTREAM * stream,MSGNO_S * msgmap)611 build_score_array(MAILSTREAM *stream, MSGNO_S *msgmap)
612 {
613     SEARCHSET *searchset;
614     long       msgno, cnt, nmsgs, rawmsgno;
615     long       score;
616     MESSAGECACHE *mc;
617 
618     nmsgs = mn_get_total(msgmap);
619     g_score_arr = (long *) fs_get((nmsgs+1) * sizeof(score));
620     memset(g_score_arr, 0, (nmsgs+1) * sizeof(score));
621 
622     /*
623      * Build a searchset that contains everything except those that have
624      * already been looked up.
625      */
626 
627     for(msgno=1L; msgno <= stream->nmsgs; msgno++)
628       if((mc = mail_elt(stream, msgno)) != NULL)
629 	mc->sequence = 0;
630 
631     for(cnt=0L, msgno=1L; msgno <= nmsgs; msgno++){
632 	rawmsgno = mn_m2raw(msgmap, msgno);
633 	if(get_msg_score(stream, rawmsgno) == SCORE_UNDEF
634 	   && rawmsgno > 0L && stream && rawmsgno <= stream->nmsgs
635 	   && (mc = mail_elt(stream, rawmsgno))){
636 	    mc->sequence = 1;
637 	    cnt++;
638 	}
639     }
640 
641     if(cnt){
642 	searchset = build_searchset(stream);
643 	(void)calculate_some_scores(stream, searchset, 0);
644 	mail_free_searchset(&searchset);
645     }
646 
647     /*
648      * Copy scores to g_score_arr. They should all be defined now but if
649      * they aren't assign score zero.
650      */
651     for(rawmsgno = 1L; rawmsgno <= nmsgs; rawmsgno++){
652 	score = get_msg_score(stream, rawmsgno);
653 	g_score_arr[rawmsgno] = (score == SCORE_UNDEF) ? 0L : score;
654     }
655 }
656 
657 
658 void
free_score_array(void)659 free_score_array(void)
660 {
661     if(g_score_arr)
662       fs_give((void **) &g_score_arr);
663 }
664 
665 
666 /*----------------------------------------------------------------------
667   Compare scores
668  ----*/
669 int
pine_compare_scores(const qsort_t * a,const qsort_t * b)670 pine_compare_scores(const qsort_t *a, const qsort_t *b)
671 {
672     long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
673     long  sdiff;
674 
675     return((sdiff = g_score_arr[*mess_a] - g_score_arr[*mess_b])
676 	    ? ((sdiff > 0L) ? 1 : -1)
677 	    : ((mdiff = *mess_a - *mess_b) ? ((mdiff > 0) ? 1 : -1) : 0));
678 }
679 
680 
681 void
reset_sort_order(unsigned int flags)682 reset_sort_order(unsigned int flags)
683 {
684     long rflags = ROLE_DO_OTHER;
685     PAT_STATE     pstate;
686     PAT_S        *pat;
687     SortOrder	  the_sort_order;
688     int           sort_is_rev;
689 
690     /* set default order */
691     the_sort_order = ps_global->def_sort;
692     sort_is_rev    = ps_global->def_sort_rev;
693 
694     if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
695 	for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
696 	    if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL,
697 			     NULL, NULL, SE_NOSERVER|SE_NOPREFETCH))
698 	      break;
699 	}
700 
701 	if(pat && pat->action && !pat->action->bogus
702 	   && pat->action->sort_is_set){
703 	    the_sort_order = pat->action->sortorder;
704 	    sort_is_rev    = pat->action->revsort;
705 	}
706     }
707 
708     sort_folder(ps_global->mail_stream, ps_global->msgmap,
709 		the_sort_order, sort_is_rev, flags);
710 }
711