1 /*
2  *  LIST.C
3  *
4  *  Written on 10-Jul-94 by John Dennis and released to the public domain.
5  *
6  *  Lists the messages in the messagebase.
7  */
8 
9 #include <stdio.h>
10 #include <string.h>
11 #include <time.h>
12 #include "addr.h"
13 #include "nedit.h"
14 #include "msged.h"
15 #include "memextra.h"
16 #include "specch.h"
17 #include "winsys.h"
18 #include "menu.h"
19 #include "main.h"
20 #include "strextra.h"
21 #include "keys.h"
22 #include "dosmisc.h"
23 #include "help.h"
24 #include "maintmsg.h"
25 #include "nshow.h"
26 #include "dlist.h"
27 #include "list.h"
28 #include "screen.h"
29 #include "charset.h"
30 #include "config.h"
31 #include "group.h"
32 
33 static int long_subj;
34 static int display_address = 1;
35 
36 static void getheader(unsigned long n, MLHEAD * h, int check_sel);
37 static void showit(MLHEAD * h, int y, int sel);
38 
39 static DLIST *ulist;
40 
41 static char *forgroupmenu[] =
42 {
43     "Move Message(s)",
44     "Copy Message(s)",
45     "Redirect Message(s)",
46     "Forward Message(s)",
47     NULL
48 };
49 
50 /*
51  *  Checks to see if the message has been selected.
52  */
53 
ulistFindUid(unsigned long uid)54 static int ulistFindUid(unsigned long uid)
55 {
56     DLISTNODE *p_node;
57     if (ulist == NULL)
58     {
59         return 0;
60     }
61     p_node = dlistTravFirst(ulist);
62     while (p_node != NULL)
63     {
64         unsigned long *puid;
65         puid = dlistGetElement(p_node);
66         if (*puid == uid)
67         {
68             return 1;
69         }
70         p_node = dlistTravNext(p_node);
71     }
72     return 0;
73 }
74 
75 /*
76  *  Removes a selection from the list.
77  */
78 
ulistDropUid(unsigned long uid)79 static void ulistDropUid(unsigned long uid)
80 {
81     DLISTNODE *p_node;
82     if (ulist == NULL)
83     {
84         return;
85     }
86     p_node = dlistTravFirst(ulist);
87     while (p_node != NULL)
88     {
89         unsigned long *puid;
90         puid = dlistGetElement(p_node);
91         if (*puid == uid)
92         {
93             dlistDropNode(ulist, p_node);
94         }
95         p_node = dlistTravNext(p_node);
96     }
97 }
98 
99 /*
100  *  Adds a new message to the list of selected messages.
101  */
102 
ulistAddUid(unsigned long uid)103 static int ulistAddUid(unsigned long uid)
104 {
105     unsigned long *puid;
106     DLISTNODE *p_node;
107 
108     if (ulist == NULL)
109     {
110         ulist = dlistInit();
111         if (ulist == NULL)
112         {
113             return 0;
114         }
115     }
116     puid = xmalloc(sizeof *puid);
117     if (puid == NULL)
118     {
119         return 0;
120     }
121     p_node = dlistCreateNode(puid);
122     if (p_node == NULL)
123     {
124         xfree(puid);
125         return 0;
126     }
127     *puid = uid;
128     dlistAddNode(ulist, p_node);
129     return 1;
130 }
131 
132 /*
133  *  Frees the list of msgnumbers.
134  */
135 
ulistTerm(void)136 static void ulistTerm(void)
137 {
138     DLISTNODE *p_node;
139     if (ulist == NULL)
140     {
141         return;
142     }
143     p_node = dlistTravFirst(ulist);
144     while (p_node != NULL)
145     {
146         unsigned long *puid;
147         puid = dlistGetElement(p_node);
148         xfree(puid);
149         p_node = dlistTravNext(p_node);
150     }
151     dlistTerm(ulist);
152     ulist = NULL;
153 }
154 
155 /*
156  *  Re-displays the entire screen.
157  */
158 
update(MLHEAD * headers,unsigned long i,int y)159 static void update(MLHEAD * headers, unsigned long i, int y)
160 {
161     TTBeginOutput();
162     while (i <= CurArea.messages && y <= maxy - 4)
163     {
164         getheader(i, &headers[y - 1], 1);
165         showit(&headers[y - 1], y, 0);
166         i++;
167         y++;
168     }
169     if (y <= (maxy - 4))
170     {
171         WndClear(1, y, maxx - 2, maxy - 4, cm[LS_NTXT]);
172     }
173     TTEndOutput();
174 }
175 
176 /*
177  *  Shows a header on the screen.
178  */
179 
showit(MLHEAD * h,int y,int sel)180 static void showit(MLHEAD * h, int y, int sel)
181  {
182     unsigned long msgn;
183     char line[384];
184     char msgnbuf[9];
185     int l;
186     char *cp;
187 
188     TTBeginOutput();
189 
190     msgn = SW->showrealmsgn ? h->umsgid : h->msgnum;
191     sprintf(msgnbuf, "%5ld %c", msgn, h->sel ? SC14 : ' ');
192     l = strlen(msgnbuf) - 1;
193 
194     if (long_subj)
195     {
196         sprintf(line, "%c%s%-15.15s %-70s", h->times_read > 0 ? '*' : ' ',
197           msgnbuf, h->fr_name, h->subj);
198     }
199     else
200     {
201         sprintf(line, "%c%s%-15.15s %-15.15s %-70s",
202           h->times_read > 0 ? '*' : ' ', msgnbuf, h->fr_name,
203           h->to_name, h->subj);
204     }
205 
206     /* caveat the broken subject line!!! */
207     for (cp=line; *cp; cp++)
208         if ((*cp >= 0 && *cp < ' ') && (*cp != SC14))
209             *cp = ' ';
210 
211     if (sel)
212     {
213         if (l>0)
214             WndPutsn(1, y, l, cm[LS_STXT], line);
215         if (l>1)
216             WndPutsn(1+l, y, 1, cm[LS_STXT] | F_ALTERNATE, line + l);
217         if (l + 1 < maxx - 2)
218             WndPutsn(1+l+1, y, (maxx - 2) - (l + 1), cm[LS_STXT],
219                      line+l+1);
220     }
221     else if (stricmp(h->to_name, ST->username) == 0 ||
222       stricmp(h->fr_name, ST->username) == 0)
223     {
224         if (l > 0)
225             WndPutsn(1, y, l, cm[LS_ITXT], line);
226         if (l>1)
227             WndPutsn(1+l, y, 1, cm[LS_ITXT] | F_ALTERNATE, line + l);
228         if (l + 1 < maxx - 2)
229             WndPutsn(l + 2, y, (maxx - 2) - (l + 1), cm[LS_ITXT],
230                      line + l + 1);
231     }
232     else
233     {
234         if (l > 0)
235             WndPutsn(1, y, l, cm[LS_NTXT], line);
236         if (l>1)
237             WndPutsn(1+l, y, 1, cm[LS_NTXT] | F_ALTERNATE, line + l);
238         if (l + 1 < maxx - 2)
239             WndPutsn(l + 2, y, (maxx - 2) - (l + 1), cm[LS_NTXT],
240                      line + l + 1);
241     }
242 
243     TTEndOutput();
244 
245 }
246 
247 /*
248  *  Gets a header from the msgbase.
249  */
250 
getheader(unsigned long n,MLHEAD * h,int check_sel)251 static void getheader(unsigned long n, MLHEAD * h, int check_sel)
252 {
253     msg *x;
254     char *text;
255     char *charset; int level;
256     char *tokens[5];
257     LOOKUPTABLE *ltable = NULL;
258 
259     /* Read the message header */
260 
261     memset(h, 0, sizeof *h);
262     x = MsgReadHeader((unsigned int)n, RD_ALL);
263     if (x == NULL)
264     {
265         return;
266     }
267 
268 
269     /* Search the CHRS kludge */
270 
271     if (ST->input_charset != NULL)
272     {
273 	charset = xstrdup(ST->input_charset);
274 	level = 2;
275     }
276     else
277     {
278 	charset = xstrdup("ASCII");
279         level = 2; /* ASCII 2 is nonsense, but get_readtable will return
280                       the correct table */
281     }
282 
283     while ((text = MsgReadText((unsigned int)n)) != NULL)
284     {
285         if (*text == '\01')
286         {
287             if (strncmp(text + 1, "CHRS:", 5) == 0)
288             {
289 		memset(tokens, 0, sizeof(tokens));
290 		parse_tokens(text + 7, tokens, 2);
291 		if (tokens[1] != NULL)
292 		{
293 
294                     if ( have_readtable(tokens[0], atoi(tokens[1])) ||
295                         ST->input_charset == NULL)
296                     {
297                         release(charset);
298                         charset = xstrdup(tokens[0]);
299                         level = atoi(tokens[1]);
300                     }
301                 }
302             }
303             release(text);
304         }
305         else
306         {
307             /* Kludges are over! */
308             release(text);
309             /* break;  this does not work, we have to read the whole msg */
310         }
311     }
312 
313     MsgClose();
314 
315     ltable = get_readtable(charset, level);
316     release(charset);
317 
318     /* copy the header info */
319 
320     h->msgnum = n;
321     h->umsgid = x->msgnum;
322     h->to_net = x->to.net;
323     h->to_node = x->to.node;
324     h->fr_net = x->from.net;
325     h->fr_node = x->from.node;
326     h->times_read = x->times_read;
327     if (check_sel)
328     {
329         h->sel = ulistFindUid(x->msgnum);
330     }
331     else
332     {
333         h->sel = 0;
334     }
335 
336     text = translate_text(x->subj, ltable);
337     strncpy(h->subj, text, 72);
338     h->subj[72] = '\0';
339     release(text);
340 
341     text = translate_text(x->isto, ltable);
342     strncpy(h->to_name, text, 36);
343     h->to_name[36] = '\0';
344     release(text);
345 
346     text = translate_text(x->isfrom, ltable);
347     strncpy(h->fr_name, text, 36);
348     h->fr_name[36] = '\0';
349     release(text);
350 
351     dispose(x);
352 }
353 
354 /*
355  *  Deletes the selected messages, or the current one if none.
356  */
357 
DeleteMsgs(unsigned long * CurrMsgn)358 static void DeleteMsgs(unsigned long *CurrMsgn)
359 {
360     if (ulist == NULL || dlistIsEmpty(ulist))
361     {
362 /*
363         if (!confirm("Erase message?"))
364         {
365             return;
366         }
367 */
368         CurArea.current = *CurrMsgn;
369         deletemsg();
370     }
371     else
372     {
373         int confirm_temp;
374         DLISTNODE *p_node;
375         unsigned long msgn, oldmsgn;
376         char messagetxt[80];
377 
378         if (!confirm("Erase all selected messages?"))
379         {
380             return;
381         }
382         if (!OpenMsgWnd(50, 6, "Deleting Messages", NULL, 0, 0))
383         {
384             return;
385         }
386         SendMsgWnd("Press Esc to interrupt", 2);
387 
388         confirm_temp = SW->confirmations;
389         SW->confirmations = 0;
390 
391         oldmsgn = 0L;
392 
393         p_node = dlistTravFirst(ulist);
394         while (p_node != NULL)
395         {
396             unsigned long *puid;
397 
398             if (KeyHit() && GetKey() == Key_Esc)
399             {
400                 p_node = NULL;
401                 break;
402             }
403 
404             puid = dlistGetElement(p_node);
405             msgn = UidToMsgn(*puid);
406 
407             sprintf(messagetxt, "Working on message #%lu",
408                     SW->showrealmsgn ? *puid: msgn);
409             SendMsgWnd(messagetxt, 1);
410 
411             if (oldmsgn == 0L)
412             {
413                 oldmsgn = msgn - 1;
414             }
415             if (msgn != 0L)
416             {
417                 CurArea.current = msgn;
418                 deletemsg();
419             }
420             p_node = dlistTravNext(p_node);
421         }
422 
423         if (oldmsgn == 0L)
424         {
425             oldmsgn = *CurrMsgn;
426         }
427         *CurrMsgn = oldmsgn;
428         CurArea.current = oldmsgn;
429         SW->confirmations = confirm_temp;
430 
431         CloseMsgWnd();
432     }
433 }
434 
435 /*
436  *  Forwards, redirects, moves or copies a group of messages.
437  */
438 
movemsgs(int rc,int to_area)439 static int movemsgs(int rc, int to_area)
440 {
441     int clear = (to_area == -1);
442 
443     if (rc == 2 || rc == 3)
444     {
445         DrawHeader();
446     }
447     switch (rc)
448     {
449     case 0:                    /* Move */
450         to_area = move_msg(to_area);
451         break;
452 
453     case 1:                    /* Copy */
454         to_area = copy_msg(to_area);
455         break;
456 
457     case 2:                    /* Redirect */
458         to_area = redirect_msg(to_area);
459         break;
460 
461     case 3:                    /* Forward */
462         to_area = forward_msg(to_area);
463         break;
464 
465     case -1:                   /* Escape */
466         return -1;
467     }
468 
469     if (clear)
470     {
471         TTBeginOutput();
472         WndClearLine(0, cm[MN_NTXT]);
473         WndWriteStr(2, 0, cm[LS_TTXT], CurArea.description);
474 
475         if (rc == 2 || rc == 3)
476         {
477             int i;
478             for (i = 1; i <= 5; i++)
479             {
480                 WndClearLine(i, cm[MN_NTXT]);
481             }
482             WndBox(0, 1, maxx - 1, maxy - 2, cm[LS_BTXT], SBDR);
483         }
484         TTEndOutput();
485     }
486     return to_area;
487 }
488 
489 /*
490  *  Moves, copies, redirects or forwards the selected messages, or the
491  *  current one if none.
492  */
493 
MoveMsgs(unsigned long * CurrMsgn)494 static void MoveMsgs(unsigned long *CurrMsgn)
495 {
496     int rc;
497     WND *hCurr;
498 
499     rc = DoMenu((maxx / 2) - 10, (maxy / 2) - 1, (maxx / 2) + 9, (maxy / 2) + 2,
500       forgroupmenu, 0, SELBOX_MOVEMSG, "");
501 
502     if (ulist == NULL || dlistIsEmpty(ulist))
503     {
504         CurArea.current = *CurrMsgn;
505         hCurr = WndTop();
506         WndCurr(hMnScr);
507         groupmove = 1;
508         movemsgs(rc, -1);
509         groupmove = 0;
510         WndCurr(hCurr);
511     }
512     else
513     {
514         DLISTNODE *p_node, *p_old_node;
515         unsigned long msgn, oldmsgn;
516         int to_area = -1;
517         char messagetxt[80];
518         int ogroup;
519 
520         ogroup = group_set_group(0); /* allow copies etc. to everywhere */
521 
522         oldmsgn = 0L;
523 
524         switch(rc)
525         {
526         case 0:
527             strcpy(messagetxt," Moving");
528             break;
529 
530         case 1:
531             strcpy(messagetxt," Copying");
532             break;
533 
534         case 2:
535             strcpy(messagetxt," Redirecting");
536             break;
537 
538         case 3:
539             strcpy(messagetxt," Forwarding");
540             break;
541 
542         default:
543             strcpy(messagetxt," <internal error>");
544             break;
545         }
546         strcat(messagetxt, " Messages ");
547 
548         if (!OpenMsgWnd(50, 6, messagetxt, NULL, 0, 0))
549         {
550             return;
551         }
552         SendMsgWnd("Press Esc to stop", 2);
553 
554         p_node = dlistTravFirst(ulist);
555         while (p_node != NULL)
556         {
557             unsigned long *puid;
558 
559             if (KeyHit() && GetKey() == Key_Esc)
560             {
561                 p_node = NULL;
562                 break;
563             }
564 
565             puid = dlistGetElement(p_node);
566             msgn = UidToMsgn(*puid);
567 
568             sprintf(messagetxt, "Working on message #%lu",
569                     SW->showrealmsgn ? *puid: msgn);
570             SendMsgWnd(messagetxt, 1);
571 
572             if (oldmsgn == 0L)
573             {
574                 oldmsgn = msgn - 1L;
575             }
576             p_old_node = p_node;
577             if (msgn != 0L)
578             {
579                 CurArea.current = msgn;
580                 hCurr = WndTop();
581                 WndCurr(hMnScr);
582                 groupmove = 1;
583                 to_area = movemsgs(rc, to_area);
584                 groupmove = 0;
585                 WndCurr(hCurr);
586             }
587             p_node = dlistTravNext(p_node);
588             dlistDropNode(ulist, p_old_node);
589 
590             if (to_area == -1) /* an escape or an error occured */
591             {
592                 p_node = NULL;
593             }
594         }
595         if (oldmsgn == 0L)
596         {
597             oldmsgn = *CurrMsgn;
598         }
599         *CurrMsgn = oldmsgn;
600         CurArea.current = oldmsgn;
601 
602         CloseMsgWnd();
603 
604         group_set_group(ogroup);
605     }
606 }
607 
608 /*
609  *  Puts a list of messages up in a window on the screen.  Allows for
610  *  some basic management of those messages.
611  */
612 
do_list(void)613 int do_list(void)
614 {
615     EVT event;
616     WND *hWnd, *hCurr;
617     static int in_list = 0;     /* stop recursion */
618     MLHEAD *headers;            /* headers */
619     int done = 0;               /* finished ? */
620     int down = 0;
621     int ForceEvt = 0;           /* forced/piped keypress */
622     int Msg;                    /* message */
623     int lbutton = 0;
624     unsigned long i, a, j;
625     int y;
626 
627 begin:
628 
629     if (in_list || !CurArea.status)  /* stop recursion */
630     {
631         return 0;
632     }
633     else
634     {
635         in_list = 1;
636     }
637 
638     /* Open the window and draw the screen and allocate the memory. */
639 
640     TTBeginOutput();
641     WndClearLine(0, cm[MN_NTXT]);
642     WndClearLine(maxy - 1, cm[MN_NTXT]);
643     WndWriteStr(2, 0, cm[LS_TTXT], CurArea.description);
644     hCurr = WndTop();
645     hWnd = WndOpen(0, 1, maxx - 1, maxy - 2, NBDR | NOSAVE, 0, cm[LS_NTXT]);
646     headers = xcalloc(maxy, sizeof(MLHEAD));
647 
648     WndBox(0, 0, maxx - 1, maxy - 3, cm[LS_BTXT], SBDR);
649 
650     message = KillMsg(message);
651 
652     if (done == 2)
653     {
654          done = 0;  /* this an subsequent entry into the list which
655                        results from a window resize operation. */
656     }
657     else
658     {
659          a = CurArea.current;
660                     /* this is the first entry - set the pointer to
661                        the current message in this area */
662     }
663     y = 1;
664     update(headers, a, y);
665     TTEndOutput();
666 
667 
668     while (!done)
669     {
670         TTBeginOutput();
671 #if defined(MSDOS) && !defined(__FLAT__)
672         /* shows memory if compiled under dos */
673 
674         if (SW->statbar)
675         {
676             char line[255];
677             sprintf(line, "%c %3ldK ", SC7, (long)(corerem() / 1024));
678             WndCurr(WndTop());
679             WndPutsn(maxx - 7, maxy - 1, 1, cm[CM_ITXT] | F_ALTERNATE,
680                      line + 1);
681             WndPutsn(maxx - 6, maxy - 1, 6, cm[CM_ITXT], line + 1);
682             WndCurr(hWnd);
683         }
684 #endif
685 
686         WndWriteStr(3, 0, cm[LS_TTXT], "Msg");
687         WndWriteStr(9, 0, cm[LS_TTXT], "From");
688         if (long_subj)
689         {
690             char tmp[8];
691 
692             WndWriteStr(25, 0, cm[LS_TTXT], "Subject");
693 
694             memset(tmp, SC8, 7);
695             *(tmp + 7) = '\0';
696             WndWriteStr(41, 0, cm[LS_BTXT] | F_ALTERNATE, tmp);
697         }
698         else
699         {
700             char tmp[8];
701 
702             memset(tmp, SC8, 7);
703             *(tmp + 7) = '\0';
704             WndWriteStr(25, 0, cm[LS_BTXT] | F_ALTERNATE, tmp);
705             WndWriteStr(25, 0, cm[LS_TTXT], "To");
706             WndWriteStr(41, 0, cm[LS_TTXT], "Subject");
707         }
708         showit(&headers[y - 1], y, 1);
709         TTEndOutput();
710 
711         if (down)
712         {
713             /* If a selection has occured, then force cursor down. */
714 
715             Msg = Key_Dwn;
716             event.msg = Msg;
717             event.msgtype = WND_WM_CHAR;
718             down = 0;
719         }
720         else if (ForceEvt)
721         {
722             /*
723              * These events are from the mouse (no point in
724              * duplicating code).
725              */
726 
727             Msg = ForceEvt;
728             event.msg = Msg;
729             event.msgtype = WND_WM_CHAR;
730             ForceEvt = 0;
731         }
732         else
733         {
734             Msg = MnuGetMsg(&event, hWnd->wid);
735         }
736 
737         switch (event.msgtype)
738         {
739         case WND_WM_RESIZE:
740                 /* the window has been resized. we have to exit and
741                    rebuild the list. */
742                 maxx = term.NCol;
743                 maxy = term.NRow;
744                 done = 2;
745                 break;
746 
747         case WND_WM_MOUSE:
748             switch (Msg)
749             {
750             case LMOU_RPT:
751                 {
752                     int y1 = event.y;
753 
754                     if (y1 > maxy - 4 && lbutton)
755                     {
756                         ForceEvt = Key_Dwn;
757                     }
758                     else if (y1 < 1 && lbutton)
759                     {
760                         ForceEvt = Key_Up;
761                     }
762                 }
763                 break;
764 
765             case LMOU_CLCK:
766             case MOU_LBTDN:
767             case MOU_LBTUP:
768             case MOUSE_EVT:
769                 {
770                     int y1 = event.y - 1, ok = 0;
771 
772                     if (Msg == MOU_LBTDN)
773                     {
774                         lbutton = 1;
775                     }
776                     else if (Msg == MOU_LBTUP)
777                     {
778                         lbutton = 0;
779                     }
780 
781                     if (y1 > maxy - 4)
782                     {
783                         if (Msg == MOU_LBTDN && lbutton)
784                         {
785                             ForceEvt = Key_Dwn;
786                         }
787                     }
788                     else
789                     {
790                         if (y1 < 1)
791                         {
792                             if (Msg == MOU_LBTDN && lbutton)
793                             {
794                                 ForceEvt = Key_Up;
795                             }
796                         }
797                         else
798                         {
799                             /* The event occured on the list. */
800 
801                             if (y == y1 && (Msg == MOU_LBTUP || Msg == LMOU_CLCK))
802                             {
803                                 ForceEvt = Key_Ent;
804                                 continue;
805                             }
806                             showit(&headers[y - 1], y, 0);
807                             if (y > y1)
808                             {
809                                 if (a - y - y1 >= 1)
810                                 {
811                                     a -= y - y1;
812                                     ok = TRUE;
813                                 }
814                             }
815                             else
816                             {
817                                 if (a + y1 - y <= CurArea.messages)
818                                 {
819                                     a += y1 - y;
820                                     ok = TRUE;
821                                 }
822                             }
823 
824                             if (ok == TRUE)
825                             {
826                                 y = y1;
827                                 if (Msg == MOU_LBTUP || Msg == LMOU_CLCK)
828                                 {
829                                     ForceEvt = Key_Ent;
830                                 }
831                             }
832                         }
833                     }
834                 }
835                 break;
836 
837             default:
838                 break;
839             }
840             break;
841 
842         case WND_WM_CHAR:
843             switch (Msg)
844             {
845             case Key_PgDn:
846                 i = maxy - 4 - y;
847                 while (i > 0 && a < CurArea.messages)
848                 {
849                     i--;
850                     a++;
851                 }
852                 y = 1;
853                 update(headers, a, y);
854                 break;
855 
856             case Key_PgUp:
857                 if (y == 1)
858                 {
859                     i = maxy - 5;
860                 }
861                 else
862                 {
863                     i = y - 1;
864                 }
865                 while (i > 0 && a > 1)
866                 {
867                     i--;
868                     a--;
869                 }
870                 y = 1;
871                 update(headers, a, y);
872                 break;
873 
874             case Key_Up:
875                 if (a > 1)
876                 {
877                     showit(&headers[y - 1], y, 0);
878                     a--;
879                     y--;
880 
881                     if (y < 1)
882                     {
883                         y = 1;
884                         WndScroll(1, 1, maxx - 2, maxy - 4, 0);
885                         if (SW->statbar)
886                         {
887                             memmove(headers + 1, headers,
888                               sizeof(MLHEAD) * (maxy - 2));
889                         }
890                         else
891                         {
892                             memmove(headers + 1, headers,
893                               sizeof(MLHEAD) * (maxy - 1));
894                         }
895                         getheader(a, &headers[0], 1);
896                     }
897                 }
898                 break;
899 
900             case Key_Dwn:
901                 if (a < CurArea.messages)
902                 {
903                     showit(&headers[y - 1], y, 0);
904                     a++;
905                     y++;
906 
907                     if (y > maxy - 4)
908                     {
909                         y = maxy - 4;
910                         WndScroll(1, 1, maxx - 2, y, 1);
911                         if (SW->statbar)
912                         {
913                             memmove(headers, headers + 1,
914                               sizeof(MLHEAD) * (maxy - 2));
915                         }
916                         else
917                         {
918                             memmove(headers, headers + 1,
919                               sizeof(MLHEAD) * (maxy - 1));
920                         }
921                         getheader(a, &headers[y - 1], 1);
922                     }
923                 }
924                 break;
925 
926             case Key_Home:
927                 a = CurArea.first;
928                 update(headers, a, y = 1);
929                 break;
930 
931             case Key_End:
932                 a = CurArea.last;
933                 update(headers, a, y = 1);
934                 break;
935 
936             case '+':
937                 ulistTerm();
938                 for (j = CurArea.first; j <= CurArea.messages; j++)
939                 {
940                     ulistAddUid(MsgnToUid(j));
941                 }
942                 update(headers, a, y = 1);
943                 break;
944 
945             case '-':
946                 ulistTerm();
947                 update(headers, a, y = 1);
948                 break;
949 
950             case Key_Spc:
951                 if (headers[y - 1].sel == 0)
952                 {
953                     headers[y - 1].sel = 1;
954                     ulistAddUid(headers[y - 1].umsgid);
955                 }
956                 else
957                 {
958                     ulistDropUid(headers[y - 1].umsgid);
959                     headers[y - 1].sel = 0;
960                 }
961                 showit(&headers[y - 1], y, 1);
962                 down = 1;
963                 break;
964 
965             case Key_Del:
966                 DeleteMsgs(&a);
967                 if (a > CurArea.last)
968                 {
969                     a = CurArea.last;
970                 }
971                 update(headers, a, y = 1);
972                 break;
973 
974             case Key_A_H:
975                 if (ST->helpfile)
976                 {
977                     DoHelp(2);
978                 }
979                 break;
980 
981             case Key_Ent:
982                 CurArea.current = a;
983                 done = 3;
984                 break;
985 
986             case Key_A_X:
987             case Key_Esc:
988                 done = 1;
989                 break;
990 
991             case Key_A_S:
992                 long_subj ^= 1;
993                 update(headers, a, y = 1);
994                 break;
995 
996             case Key_A_A:
997                 display_address ^= 1;
998                 update(headers, a, y = 1);
999                 break;
1000 
1001             case Key_A_M:
1002                 MoveMsgs(&a);
1003                 if (a > CurArea.last)
1004                 {
1005                     a = CurArea.last;
1006                 }
1007                 update(headers, a, y = 1);
1008                 break;
1009 
1010             default:
1011                 break;
1012             }
1013             break;
1014         }
1015     }
1016     in_list = 0;
1017     ulistTerm();
1018     xfree(headers);
1019     WndClose(hWnd);
1020     WndCurr(hCurr);
1021 
1022     switch(done)
1023     {
1024     case 1:
1025         return 0;               /* exit with ESC */
1026     case 2:                     /* resize occured, continue with
1027                                    rebuilding the list */
1028         goto begin;
1029     case 3:
1030         return 1;               /* exit with Enter */
1031     }
1032 
1033     abort();                    /* something went wrong! */
1034 }
1035