1 /*
2  *  AREAS.C
3  *
4  *  Written by jim nutt, John Dennis and released into the public domain
5  *  by John Dennis in 1994.
6  *
7  *  This file contains the routines to select one of the areas specified
8  *  in the config files.  It pops up a list with all the areas and lets
9  *  the user choose.
10  */
11 
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <time.h>
16 #include "addr.h"
17 #include "nedit.h"
18 #include "msged.h"
19 #include "winsys.h"
20 #include "menu.h"
21 #include "main.h"
22 #include "misc.h"
23 #include "memextra.h"
24 #include "specch.h"
25 #include "keys.h"
26 #include "unused.h"
27 #include "help.h"
28 #include "version.h"
29 #include "strextra.h"
30 #include "areas.h"
31 #include "group.h"
32 
33 char **alist = NULL;
34 char **alist2 = NULL;
35 
36 static int AreaBox(char **Itms, int y1, int real_y2, int len, int def, WND * hPrev, WND * hWnd, int Sel, int Norm, int indent);
37 
BuildList(char *** lst)38 void BuildList(char ***lst)
39 {
40     int i;
41     AREA *a;
42     char line[256];
43     unsigned long unread, last;
44     int areano;
45 
46     *lst = xcalloc(SW->groupareas + 2 ,sizeof(char *));
47 
48     for (i = 0; i < SW->groupareas; i++)
49     {
50         areano = group_getareano(i);
51 
52         if (areano>=0)
53         {
54             a = arealist + areano;
55             memset(line, ' ', sizeof line);
56 
57             if (a->scanned)
58             {
59                 last = a->lastread;
60                 if (last > a->messages)
61                 {
62                     last = a->messages;
63                 }
64                 unread = a->messages - a->lastread;
65                 if (unread > a->messages)
66                 {
67                     unread = a->messages;
68                 }
69 
70                 /* F_ALTERNATE for SC14 is set in SelShowItem */
71                 sprintf(line, "%c%-*.*s", unread ? (SC14) : ' ',
72                         maxx - 25, maxx - 25, a->description);
73                 line[strlen(line)] = ' ';
74                 sprintf(line + maxx - 23, "%6lu%6lu%6lu",
75                         a->messages, unread, last);
76             }
77             else
78             {
79                 sprintf(line, " %-*.*s", maxx - 25, maxx - 25, a->description);
80                 line[strlen(line)] = ' ';
81                 sprintf(line + maxx - 19, " -     -     -");
82             }
83         }
84         else
85         {
86             memset(line, '=', sizeof line);
87             sprintf(line + 2, " Group: %s ", group_getname(-areano));
88             line[strlen(line)]='=';
89             line[sizeof(line) - 1] = '\0';
90         }
91 
92         (*lst)[i] = xstrdup(line);
93     }
94     (*lst)[i] = NULL;
95 }
96 
SelShowItem(char * text,int y,int len,int Attr,int indent)97 static void SelShowItem(char *text, int y, int len, int Attr, int indent)
98 {
99     char line[256];
100 
101     memset(line, ' ', sizeof(line));
102 
103     if (strlen(text) + indent > sizeof(line) - 1)
104     {
105         strcpy(line,"<--- internal buffer overflow --->");
106     }
107 
108     strcpy(line + indent, text);
109     line[sizeof(line)-1] = 0;
110     if (indent)
111     {
112         WndPutsn(1, y, indent, Attr, line);
113     }
114     WndPutsn(1 + indent, y, 1, Attr | F_ALTERNATE, line + indent);
115     if (len > (indent + 1))
116     {
117         WndPutsn(2 + indent, y, len - (indent + 1), Attr, line+indent+1);
118     }
119 }
120 
SelShowPage(char ** text,int top,int bot,int len,int pos,int Attr,int indent)121 static void SelShowPage(char **text, int top, int bot, int len, int pos, int Attr, int indent)
122 {
123     int i, y;
124 
125     y = top;
126     for (i = pos; text[i] != NULL; i++)
127     {
128         if (y > bot)
129         {
130             break;
131         }
132         SelShowItem(text[i], y++, len, Attr, indent);
133     }
134     if (y <= bot)
135     {
136         while (y <= bot)
137         {
138             SelShowItem(" ", y++, len, Attr, indent);
139         }
140     }
141 }
142 
CalcDef(int max,int cur,int * top,int miny,int maxy,int * y)143 static void CalcDef(int max, int cur, int *top, int miny, int maxy, int *y)
144 {
145     int dif;
146     unused(cur);
147     dif = (maxy - 1) - miny;
148     if ((max - 1) - *top < dif && max > dif)
149     {
150         *y = maxy;
151         *top = (max - 1) - dif;
152     }
153 }
154 
setup_areabox_coordinates(int * currItem,int * curY,int * Top,int * y2,int * page,int y1,int real_y2,int itemCnt)155 static void setup_areabox_coordinates(int *currItem, int *curY,
156                                       int *Top, int *y2, int *page,
157                                       int y1, int real_y2, int itemCnt)
158 {
159 
160     *y2=min(real_y2, itemCnt);
161     *page = *y2 - y1;
162 
163     *curY = y1;
164 
165     *Top = *currItem;
166 
167     if (*currItem + y1 < y1)
168     {
169         *curY = y1 + *currItem;
170         *Top = 0;
171     }
172     else
173     {
174         if ((itemCnt - *currItem) <= (*y2 - y1))
175         {
176             (*Top) -= ((*y2 - y1 + 1) - (itemCnt - *Top));
177             *curY = y1 + (*currItem - *Top);
178             if (*Top < 0)
179             {
180                 *Top = 0;
181                 (*curY)--;
182             }
183         }
184     }
185 }
186 
187 
188 static int AreaBoxCurItem;
189 
AreaBox(char ** Itms,int y1,int real_y2,int len,int def,WND * hPrev,WND * hWnd,int Sel,int Norm,int indent)190 static int AreaBox(char **Itms, int y1, int real_y2, int len, int def,
191                    WND * hPrev, WND * hWnd, int Sel, int Norm, int indent)
192 {
193     EVT e;
194     char find[30];
195     int itemCnt, Stuff, done, curY, Msg, currItem, Top, page, i, y2;
196     int real_areano;
197 
198     itemCnt = 0;
199     Stuff = 0;
200     for (i = 0; Itms[i] != NULL; i++)
201     {
202         itemCnt++;
203     }
204 
205     currItem = def;
206 
207     setup_areabox_coordinates(&currItem, &curY, &Top, &y2, &page,
208                               y1, real_y2, itemCnt);
209 
210     done = 0;
211 
212     TTBeginOutput();
213     SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
214     SelShowItem(Itms[currItem], curY, len, Sel, indent);
215     TTEndOutput();
216 
217     TTClearQue();               /* clear input queue */
218 
219     memset(find, '\0', sizeof find);
220     while (!done)
221     {
222         if (!*find)
223         {
224             WndCurr(hPrev);
225             WndClear(16, 0, maxx - 36, 0, cm[MN_NTXT]);
226             WndWriteStr(0, 0, cm[MN_NTXT], ">>Pick New Area:");
227             WndCurr(hWnd);
228         }
229 
230         if (!Stuff)
231         {
232             Msg = MnuGetMsg(&e, hWnd->wid);
233         }
234         else
235         {
236             e.msgtype = WND_WM_CHAR;
237             Msg = Stuff;
238             Stuff = 0;
239         }
240 
241         switch (e.msgtype)
242         {
243         case WND_WM_RESIZE:
244             maxx = term.NCol;
245             maxy = term.NRow;
246             AreaBoxCurItem = currItem;
247             return -2;  /* leave the list so it can be rebuilt with
248                            the proper window dimensions */
249 
250         case WND_WM_MOUSE:
251             switch (Msg)
252             {
253             case RMOU_CLCK:
254             case MOU_RBTUP:
255                 return -1;
256 
257             case LMOU_RPT:
258             case MOU_LBTDN:
259             case LMOU_CLCK:
260                 {
261                     int x, y;
262                     WndGetRel(e.x, e.y, &x, &y);
263                     if (y >= y1 && y <= y2)  /* in window */
264                     {
265                         Stuff = 0;
266                         if (x >= 0 && x < len)
267                         {
268                             if (y == curY)
269                             {
270                                 if (Msg == LMOU_CLCK || Msg == MOU_LBTUP)
271                                 {
272                                     if (group_getareano(currItem) >= 0)
273                                         return currItem;
274                                 }
275                                 else
276                                 {
277                                     continue;
278                                 }
279                             }
280 
281                             SelShowItem(Itms[currItem], curY, len, Norm, indent);
282 
283                             if (y > curY)
284                             {
285                                 currItem += y - curY;
286                             }
287                             else
288                             {
289                                 currItem -= curY - y;
290                             }
291 
292                             curY = y;
293                             SelShowItem(Itms[currItem], curY, len, Sel, indent);
294 
295                             if (Msg == LMOU_CLCK || Msg == MOU_LBTUP)
296                             {
297                                 if (group_getareano(currItem) >= 0)
298                                     return currItem;
299                             }
300                         }
301                     }
302                     else
303                     {
304                         if (Msg != LMOU_CLCK)
305                         {
306                             if (y < y1)
307                             {
308                                 Stuff = Key_Up;
309                             }
310                             else
311                             {
312                                 Stuff = Key_Dwn;
313                             }
314                         }
315                     }
316                 }
317                 memset(find, '\0', sizeof find);
318                 break;
319 
320             default:
321                 break;
322             }
323             break;
324 
325         case WND_WM_CHAR:
326             switch (Msg)
327             {
328             case Key_Home:
329                 if (!currItem)
330                 {
331                     break;
332                 }
333                 TTBeginOutput();
334                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
335                 currItem = 0;
336                 Top = 0;
337                 curY = y1;
338                 SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
339                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
340                 TTEndOutput();
341                 memset(find, '\0', sizeof find);
342                 break;
343 
344             case Key_A_H:
345             case Key_F1:
346                 if (ST->helpfile != NULL)
347                 {
348                     DoHelp(3);
349                 }
350                 break;
351 
352             case Key_End:
353                 if (currItem == itemCnt - 1)
354                 {
355                     break;
356                 }
357                 TTBeginOutput();
358                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
359                 currItem = itemCnt - 1;
360                 while (currItem && currItem >= (itemCnt - page))
361                 {
362                     currItem--;
363                 }
364                 Top = currItem;
365                 currItem = itemCnt - 1;
366                 curY = currItem - Top + y1;
367                 CalcDef(itemCnt, currItem, &Top, y1, y2, &curY);
368                 SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
369                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
370                 TTEndOutput();
371                 memset(find, '\0', sizeof find);
372                 break;
373 
374             case Key_Dwn:
375                 if (currItem == itemCnt - 1)
376                 {
377                     break;
378                 }
379                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
380                 currItem++;
381                 if (curY == y2)
382                 {
383                     WndScroll(1, y1, len, y2, 1);
384                     Top++;
385                 }
386                 else
387                 {
388                     curY++;
389                 }
390                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
391                 memset(find, '\0', sizeof find);
392                 break;
393 
394             case Key_Up:
395                 if (!currItem)
396                 {
397                     break;
398                 }
399                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
400                 currItem--;
401                 if (curY == y1)
402                 {
403                     WndScroll(1, y1, len, y2, 0);
404                     if (Top)
405                     {
406                         Top--;
407                     }
408                 }
409                 else
410                 {
411                     curY--;
412                 }
413                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
414                 memset(find, '\0', sizeof find);
415                 break;
416 
417             case Key_PgUp:
418                 if (!currItem)
419                 {
420                     break;
421                 }
422                 TTBeginOutput();
423                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
424                 if ((currItem -= page) < 0)
425                 {
426                     currItem = 0;
427                 }
428                 Top = currItem;
429                 curY = y1;
430                 SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
431                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
432                 TTEndOutput();
433                 memset(find, '\0', sizeof find);
434                 break;
435 
436             case Key_PgDn:
437                 if (currItem == itemCnt - 1)
438                 {
439                     break;
440                 }
441                 TTBeginOutput();
442                 SelShowItem(Itms[currItem], curY, len, Norm, indent);
443                 Top = currItem;
444 
445                 if ((currItem += page) > itemCnt - 1)
446                 {
447                     currItem = itemCnt - 1;
448                     if (currItem > page)
449                     {
450                         Top = currItem - page;
451                     }
452                     else
453                     {
454                         Top = 0;
455                     }
456                 }
457 
458                 curY = currItem - Top + y1;
459                 CalcDef(itemCnt, currItem, &Top, y1, y2, &curY);
460                 SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
461                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
462                 TTEndOutput();
463                 memset(find, '\0', sizeof find);
464                 break;
465 
466             case Key_Ent:
467             case Key_Rgt:
468                 if (group_getareano(currItem) >= 0)
469                 {
470                     size_t i;
471 
472                     i = (size_t) (((char *)(strchr(Itms[currItem] + 1, ' '))) - Itms[currItem]);
473                     i--;
474                     if (i > 28)
475                     {
476                         i = 28;
477                     }
478                     strncpy(find, Itms[currItem] + 1, i);
479                     *(find + i) = '\0';
480                     strupr(find);
481                     WndCurr(hPrev);
482                     WndWriteStr(17, 0, cm[MN_NTXT], find);
483                     WndCurr(hWnd);
484                     return currItem;
485                 }
486                 break;
487 
488             case Key_Esc:
489                 if (SW->confirmations)
490                 {
491                     if (confirm("Exit " PROG "?"))
492                     {
493                         return -1;
494                     }
495                 }
496                 else
497                 {
498                     return -1;
499                 }
500                 memset(find, '\0', sizeof find);
501                 break;
502 
503             case Key_A_X:
504                 return -1;
505 
506             case '*':
507             case '#':
508             case Key_A_S:
509             case Key_A_T:
510                 arealist_area_scan(Msg == '*' || Msg == Key_A_T);
511 
512                 for (i = 0; i < SW->groupareas; i++)
513                 {
514                     xfree(alist[i]);
515                 }
516                 xfree(alist);
517                 BuildList(&alist);
518                 Itms = alist;
519                 TTBeginOutput();
520                 SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
521                 SelShowItem(Itms[currItem], curY, len, Sel, indent);
522                 TTEndOutput();
523                 memset(find, '\0', sizeof find);
524                 break;
525 
526             case Key_A_1:
527             case Key_A_2:
528             case Key_A_3:
529             case Key_A_4:
530             case Key_A_5:
531             case Key_A_6:
532             case Key_A_7:
533             case Key_A_8:
534             case Key_A_9:
535             case Key_A_0:
536             case Key_A_G:
537             case Key_C_G:
538 
539                 real_areano = group_getareano(currItem);
540                 i = 1;
541 
542                 switch(Msg)
543                 {
544                 case Key_C_G:
545                     i = sel_group();
546                     break;
547                 case Key_A_1:
548                     group_set_group(1);
549                     break;
550                 case Key_A_2:
551                     group_set_group(2);
552                     break;
553                 case Key_A_3:
554                     group_set_group(3);
555                     break;
556                 case Key_A_4:
557                     group_set_group(4);
558                     break;
559                 case Key_A_5:
560                     group_set_group(5);
561                     break;
562                 case Key_A_6:
563                     group_set_group(6);
564                     break;
565                 case Key_A_7:
566                     group_set_group(7);
567                     break;
568                 case Key_A_8:
569                     group_set_group(8);
570                     break;
571                 case Key_A_9:
572                     group_set_group(9);
573                     break;
574                 case Key_A_0:
575                     group_set_group(0);
576                     break;
577                 }
578 
579                 if (i)
580                 {
581                     /* group has changed ... we have quite some work! */
582 
583                     /* find the new position of the currently selected group */
584                     currItem = group_getareano(0) < 0 ? 1 : 0;
585 
586                     for (i = 0; i < SW->groupareas; i++)
587                     {
588                         if (group_getareano(i) == real_areano)
589                         {
590                             currItem = i;
591                             break;
592                         }
593                     }
594 
595                     /* delete the old area list */
596 
597                     for (i = 0; alist[i] != NULL; i++)
598                     {
599                         xfree(alist[i]);
600                     }
601                     xfree(alist);
602 
603                     /* Build a new area list */
604                     BuildList(&alist);
605                     Itms = alist;
606                     itemCnt = 0;
607                     for (i = 0; Itms[i] != NULL; i++)
608                     {
609                         itemCnt++;
610                     }
611 
612                     /* Do coordinate arithmetics */
613                     y2 = min(maxy - 4, SW->groupareas);
614                     setup_areabox_coordinates(&currItem, &curY, &Top,
615                                               &y2, &page,
616                                               y1, real_y2, itemCnt);
617 
618                     /* Redraw everything */
619                     TTBeginOutput();
620                     SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
621                     SelShowItem(Itms[currItem], curY, len, Sel, indent);
622                     if (y2 < real_y2)
623                     {
624                         WndClear(1, y2 + 1, len, real_y2, Norm);
625                     }
626                     TTEndOutput();
627                     memset(find, '\0', sizeof find);
628                 }
629                 break;
630             default:
631                 if (Msg > 32 && Msg < 127)
632                 {
633                     if (strlen(find) >= 28)
634                     {
635                         break;
636                     }
637                     *(find + strlen(find)) = (char)toupper((char)Msg);
638                     WndCurr(hPrev);
639                     WndWriteStr(17, 0, cm[MN_NTXT], find);
640                     WndCurr(hWnd);
641                     i = currItem;
642 
643                     while (i < itemCnt)
644                     {
645                         if (SW->arealistexactmatch)
646                         {
647                             if (stristr(Itms[i] + 1, find) == Itms[i] + 1)
648                             {
649                                 break;
650                             }
651                         }
652                         else
653                         {
654                             if (stristr(Itms[i] + 1, find) != NULL)
655                             {
656                                 break;
657                             }
658                         }
659                         i++;
660                     }
661                     if (i == itemCnt)
662                     {
663                         for (i = 0; i < currItem; i++)
664                         {
665                             if (SW->arealistexactmatch)
666                             {
667                                 if (stristr(Itms[i] + 1, find) == Itms[i] + 1)
668                                 {
669                                     break;
670                                 }
671                             }
672                             else
673                             {
674                                 if (stristr(Itms[i] + 1, find) != NULL)
675                                 {
676                                     break;
677                                 }
678                             }
679                         }
680                         if (i == currItem)
681                         {
682                             i = itemCnt;
683                         }
684                     }
685                     if ((i != currItem) && (i != itemCnt))
686                     {
687                         SelShowItem(Itms[currItem], curY, len, Norm, indent);
688                         currItem = i;
689                         curY = y1;
690                         Top = currItem;
691 
692                         if ((itemCnt - 1) - currItem < y2 - y1)
693                         {
694                             if (currItem > y2 - y1)
695                             {
696                                 curY = y2;
697                                 Top = currItem - (y2 - y1);
698                             }
699                             else
700                             {
701                                 curY = currItem + y1;
702                                 Top = 0;
703                             }
704                         }
705                         SelShowPage(Itms, y1, y2, len, Top, Norm, indent);
706                         SelShowItem(Itms[currItem], curY, len, Sel, indent);
707                     }
708                 }
709                 else if (Msg == '\b' && *find)
710                 {
711                     *(find + strlen(find) - 1) = '\0';
712                     WndCurr(hPrev);
713                     WndClear(17 + strlen(find), 0, maxx - 36, 0, cm[CM_NINF]);
714                     WndCurr(hWnd);
715                 }
716                 else
717                 {
718                     memset(find, '\0', sizeof find);
719                 }
720                 break;
721 
722             }
723             break;
724         }
725     }
726     return -1;
727 }
728 
mainArea(void)729 int mainArea(void)
730 {
731     WND *hCurr, *hWnd;
732     int ret = -1, wid, dep, i;
733 
734     do
735     {
736         msgederr = 0;
737         wid = maxx - 1;
738         dep = maxy - 2;
739 
740         TTBeginOutput();
741         WndClearLine(0, cm[MN_NTXT]);
742         WndClearLine(maxy - 1, cm[MN_NTXT]);
743         hCurr = WndTop();
744         hWnd = WndOpen(0, 1, wid, dep, NBDR | NOSAVE, 0, cm[MN_BTXT]);
745         WndBox(0, 0, maxx - 1, maxy - 3, cm[MN_BTXT], SBDR);
746 
747         WndWriteStr(3, 0, cm[LS_TTXT], "EchoID");
748         WndWriteStr(maxx - 19, 0, cm[LS_TTXT], "Msgs");
749         WndWriteStr(maxx - 12, 0, cm[LS_TTXT], "New");
750         WndWriteStr(maxx - 7, 0, cm[LS_TTXT], "Last");
751         TTEndOutput();
752 
753 
754         BuildList(&alist);
755 
756         ret = AreaBox(alist, 1, dep - 2, wid - 1,
757                       (ret == -2) ? AreaBoxCurItem : SW->grouparea,
758                       hCurr, hWnd, cm[MN_STXT], cm[MN_NTXT], 1);
759 
760         for (i = 0; alist[i] != NULL; i++)
761         {
762                 xfree(alist[i]);
763         }
764 
765         xfree(alist);
766 
767         if (ret == -1)
768         {
769                 msgederr = 1;
770         }
771         if (ret == -2)
772         {
773             /*  returncode -2 means that the list should be restarted;
774                 it did only exit because it had to be rebuild because
775                 of a terminal window resize operation */
776             window_resized = 1;
777         }
778 
779         WndClose(hWnd);
780         WndCurr(hCurr);
781 
782         /* returncode -3 means rebuild because group has changed */
783 
784     } while (ret == -2 || ret == -3);
785 
786     return ret;
787 }
788 
selectarea(char * topMsg,int def)789 int selectarea(char *topMsg, int def)
790 {
791     WND *hCurr, *hWnd;
792     int ret, wid, dep, i;
793 
794     msgederr = 0;
795     wid = maxx - 1;
796     dep = maxy - 2;
797 
798     WndClearLine(0, cm[MN_NTXT]);
799     WndClearLine(maxy - 1, cm[MN_NTXT]);
800     hCurr = WndTop();
801     hWnd = WndOpen(0, 1, wid, dep, NBDR, 0, cm[MN_BTXT]);
802     WndBox(0, 0, maxx - 1, maxy - 3, cm[MN_BTXT], SBDR);
803     WndCurr(hCurr);
804     WndClear(0, 0, maxx - 36, 0, cm[CM_NINF]);
805     WndCurr(hWnd);
806     WndWriteStr(3, 0, cm[LS_TTXT], "EchoID");
807     WndWriteStr(maxx - 21, 0, cm[LS_TTXT], "Msgs");
808     WndWriteStr(maxx - 13, 0, cm[LS_TTXT], "New");
809     WndWriteStr(maxx - 7, 0, cm[LS_TTXT], "Last");
810 
811     BuildList(&alist2);
812 
813     do
814     {
815         ret = SelBox(alist2, 1, dep - 2, wid - 1, def, hCurr, hWnd,
816                      cm[MN_STXT], cm[MN_NTXT], SELBOX_REPLYOTH, topMsg);
817     } while (ret != -1 && group_getareano(ret) < 0);
818 
819                                 /* don't allow group separators
820                                    as selection */
821 
822     for (i = 0; i < SW->groupareas; i++)
823     {
824         xfree(alist2[i]);
825     }
826 
827     xfree(alist2);
828 
829     if (ret < 0)
830     {
831         msgederr = 1;
832     }
833 
834     WndClose(hWnd);
835     WndCurr(hCurr);
836 
837     if (ret < 0)
838     {
839         return def;
840     }
841 
842     return ret;
843 }
844