1 /* -*- mode: C; mode: fold -*- */
2 /*
3  This file is part of SLRN.
4 
5  Copyright (c) 1994, 1999 John E. Davis <davis@space.mit.edu>
6 
7  This program is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the Free
9  Software Foundation; either version 2 of the License, or (at your option)
10  any later version.
11 
12  This program is distributed in the hope that it will be useful, but WITHOUT
13  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  more details.
16 
17  You should have received a copy of the GNU General Public License along
18  with this program; if not, write to the Free Software Foundation, Inc., 675
19  Mass Ave, Cambridge, MA 02139, USA.
20 */
21 #include "config.h"
22 #include "slrnfeat.h"
23 
24 /*{{{ Include files */
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29 #ifndef VMS
30 # include <sys/types.h>
31 # include <sys/stat.h>
32 #else
33 # include "vms.h"
34 #endif
35 
36 #include <signal.h>
37 
38 #ifdef HAVE_STDLIB_H
39 # include <stdlib.h>
40 #endif
41 
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 
46 
47 #include <slang.h>
48 #include "jdmacros.h"
49 
50 #ifdef KANJI
51 #include "chmap.h"
52 #endif
53 #include "slrn.h"
54 #include "group.h"
55 #include "art.h"
56 #include "misc.h"
57 #include "post.h"
58 #include "server.h"
59 #include "hash.h"
60 #include "score.h"
61 #include "menu.h"
62 #include "util.h"
63 #include "startup.h"
64 #include "slrndir.h"
65 
66 /*}}}*/
67 
68 /*{{{ Global Variables */
69 
70 int Slrn_Query_Group_Cutoff = 100;
71 int Slrn_Groups_Dirty;	       /* greater than 0 if need to write newsrc */
72 int Slrn_List_Active_File = 0;
73 int Slrn_Use_Xgtitle = 0;
74 int Slrn_Write_Newsrc_Flags = 0;       /* if 1, do not save unsubscribed
75 					* if 2, do not save new unsubscribed.
76 					*/
77 
78 int Slrn_Display_Cursor_Bar;
79 int Slrn_Group_Description_Column = 40;/* column where group descr start */
80 char *Slrn_Group_Help_Line;
81 
82 Slrn_Group_Type *Slrn_Group_Current_Group;
83 static SLscroll_Window_Type Group_Window;
84 static Slrn_Group_Type *Groups;
85 
86 int Slrn_Group_Display_Descriptions = 1;
87 int Slrn_No_Backups = 0;
88 int Slrn_Prompt_Next_Group = 1;
89 
90 int Slrn_Unsubscribe_New_Groups = 0;
91 
92 SLKeyMap_List_Type *Slrn_Group_Keymap;
93 int *Slrn_Prefix_Arg_Ptr;
94 
95 /*}}}*/
96 /*{{{ Static Variables */
97 
98 #define GROUP_HASH_TABLE_SIZE 1250
99 static Slrn_Group_Type *Group_Hash_Table [GROUP_HASH_TABLE_SIZE];
100 
101 static unsigned int Last_Cursor_Row;
102 static int Groups_Hidden;	       /* if true, hide groups with no arts */
103 
104 typedef struct Unsubscribed_Slrn_Group_Type
105 {
106    char name[MAX_GROUP_NAME_LEN + 1];
107    struct Unsubscribed_Slrn_Group_Type *next;
108 }
109 Unsubscribed_Slrn_Group_Type;
110 
111 static Unsubscribed_Slrn_Group_Type *Unsubscribed_Groups;
112 
113 /*}}}*/
114 
115 /*{{{ Forward Function Declarations */
116 
117 static void group_update_screen (void);
118 static void group_quick_help (void);
119 static void save_newsrc_cmd (void);
120 
121 /*}}}*/
122 
123 /*{{{ Functions that deal with Group Range */
124 
125 
126 /* Note: This routine is NOT very robust.  It assumes that this function
127  * is called in an ordered way such that the implied range is always increasing.
128  * This is why the range is re-built in art.c:update_ranges.  Yes, it is ugly.
129  * See also slrn_group_mark_article_as_read for something more random.
130  */
slrn_add_group_ranges(Slrn_Group_Type * g,int min,int max)131 void slrn_add_group_ranges (Slrn_Group_Type *g, int min, int max) /*{{{*/
132 {
133    Slrn_Range_Type *r, *next;
134    int unread;
135 
136    if ((max < min) || (g == NULL)) return;
137 
138    /* The first one is range of articles on server so expand max to cover
139     * the range of articles nolonger available.
140     */
141    next = &g->range;
142    if (max < next->min)
143      {
144 	/* If we have already expanded the range up to range currently available
145 	 * at server and we are now trying to add another range below available
146 	 * range, do not bother.
147 	 */
148 	if (next->next != NULL) return;
149 	max = next->min - 1;
150      }
151 
152    /* Count number unread */
153    unread = next->max;
154    while (next->next != NULL)
155      {
156 	next = next->next;
157 	unread -= next->max - next->min + 1;
158      }
159 
160    /* check to see if a merge is possible */
161    if ((min <= next->max + 1)
162        && (next != &g->range))
163      {
164 	next->max = max;
165      }
166    else
167      {
168 	r = (Slrn_Range_Type *) slrn_safe_malloc (sizeof(Slrn_Range_Type));
169 
170 	r->next = next->next;
171 	next->next = r;
172 	r->prev = next;
173 
174 	r->min = min;
175 	r->max = max;
176 
177 #if 0
178 	/* For this case, min should be 1 */
179 	if (next == &g->range)
180 	  {
181 	     min = r->min = 1;
182 	  }
183 #endif
184      }
185 
186    unread -= max - min + 1;
187 
188    if (unread < 0) unread = 0;
189    g->unread = unread;
190    Slrn_Groups_Dirty = 1;
191 }
192 
193 /*}}}*/
194 
group_mark_article_as_read(Slrn_Group_Type * g,long num)195 static void group_mark_article_as_read (Slrn_Group_Type *g, long num) /*{{{*/
196 {
197    Slrn_Range_Type *r, *r1, *newr;
198 
199    r1 = &g->range;
200    if (r1->max < num)  /* not at server yet so update our data */
201      {
202 	r1->max = num;
203 	g->unread += 1;
204      }
205 
206    r = r1->next;
207 
208    while (r != NULL)
209      {
210 	/* Already read */
211 	if ((num <= r->max) && (num >= r->min)) return;
212 	if (num < r->min) break;
213 	r1 = r;
214 	r = r->next;
215      }
216 
217    if (g->unread > 0) g->unread -= 1;
218    Slrn_Groups_Dirty = 1;
219    if ((r != NULL) && (r->min == num + 1))
220      {
221 	r->min = num;
222 	return;
223      }
224 
225    if ((r1->max + 1 == num) && (r1 != &g->range))
226      {
227 	r1->max = num;
228 	return;
229      }
230 
231    newr = (Slrn_Range_Type *) slrn_safe_malloc (sizeof (Slrn_Range_Type));
232 
233    newr->min = newr->max = num;
234    newr->next = r;
235    if (r != NULL) r->prev = newr;
236    newr->prev = r1;
237    r1->next = newr;
238 }
239 
240 /*}}}*/
241 
slrn_mark_article_as_read(char * group,long num)242 void slrn_mark_article_as_read (char *group, long num) /*{{{*/
243 {
244    Slrn_Group_Type *g;
245    unsigned long hash;
246 
247    if (group == NULL)
248      {
249 	group_mark_article_as_read (Slrn_Group_Current_Group, num);
250 	return;
251      }
252 
253    hash = slrn_compute_hash ((unsigned char *) group,
254 			     (unsigned char *) group + strlen (group));
255 
256    g = Group_Hash_Table[hash % GROUP_HASH_TABLE_SIZE];
257 
258    while (g != NULL)
259      {
260 	if ((g->hash == hash) && !strcmp (group, g->name))
261 	  {
262 	     /* If it looks like we have read this group, mark it read. */
263 	     if (((g->flags & GROUP_UNSUBSCRIBED) == 0)
264 		 || (g->range.next != NULL))
265 	       group_mark_article_as_read (g, num);
266 
267 	     break;
268 	  }
269 	g = g->hash_next;
270      }
271 }
272 
273 /*}}}*/
274 
group_sync_group_with_server(Slrn_Group_Type * g,int * minp,int * maxp)275 static int group_sync_group_with_server (Slrn_Group_Type *g, int *minp, int *maxp) /*{{{*/
276 {
277    int min, max, n, max_available;
278    char *group;
279    Slrn_Range_Type *r;
280    int status;
281 
282    if (g == NULL) return -1;
283 
284    group = g->name;
285 
286 #ifdef KANJI
287    slrn_message_now (Slrn_Japanese_Messages
288 		     ? "%s �������� ... "
289 		     : "Selecting %s ...", group);
290 #else
291    slrn_message_now ("Selecting %s ...", group);
292 #endif
293 
294    status = Slrn_Server_Obj->sv_select_group (group, minp, maxp);
295    if (status == -1)
296      return -1;
297 
298    if (status != OK_GROUP)
299      {
300 	g->flags |= GROUP_UNSUBSCRIBED;
301 #ifdef KANJI
302 	slrn_error (Slrn_Japanese_Messages
303 		    ? "���Υ��롼�פ�̵���Τ褦�Ǥ���"
304 		    : "This group appears to be bogus.");
305 #else
306 	slrn_error ("This group appears to be bogus.");
307 #endif
308 	return -1;
309      }
310 
311    min = *minp;
312    max = *maxp;
313 
314    if (max == 0)
315      {
316 	int nmax, nmin;
317 
318 	nmax = g->range.max;
319 	nmin = g->range.min;
320 
321 	/* Server database inconsistent. */
322 	for (n = nmin; n <= nmax; n++)
323 	  group_mark_article_as_read (g, n);
324 
325 	/* g->unread = 0; */
326 #ifdef KANJI
327 	slrn_message (Slrn_Japanese_Messages
328 		      ? "�ɤ൭���Ϥ���ޤ���"
329 		      : "No articles to read.");
330 #else
331 	slrn_message ("No articles to read.");
332 #endif
333 	Slrn_Full_Screen_Update = 1;
334 	Slrn_Groups_Dirty = 1;
335 	return -1;
336      }
337 
338    g->range.min = min;
339    if (max < g->range.max)
340      {
341 	/* There is only one way for this to happen that I am aware of:
342 	 * an article has been cancelled-- update the ranges.
343 	 */
344 	int nmax = g->range.max;
345 	for (n = max + 1; n <= nmax; n++)
346 	  group_mark_article_as_read (g, n);
347      }
348    else g->range.max = max;
349 
350    /* In case more articles arrived at the server between the time that
351     * slrn was first started and when the server was just queried, update
352     * the ranges of read/unread articles.
353     */
354 
355    max_available = g->range.max - g->range.min + 1;
356 
357    r = &g->range;
358    if (r->next != NULL)
359      {
360 #if 0
361 	/* stesch@parsec.rhein-neckar.de (Stefan Scholl) suggests moving this
362 	 * below to avoid posibility of n < 0.
363 	 */
364 	if (r->next->min <= r->min)
365 	  r->next->min = 1;
366 #endif
367 	n = r->max;
368 	while (r->next != NULL)
369 	  {
370 	     r = r->next;
371 	     n -= r->max - r->min + 1;
372 	  }
373 	if (n < 0) n = 0;
374 	if (n > max_available) n = max_available;
375 	g->unread = n;
376 #if 1
377 	r = &g->range;
378 	if (r->next->min <= r->min)
379 	  r->next->min = 1;
380 #endif
381      }
382 
383    return 0;
384 }
385 
386 
387 
388 /*}}}*/
389 
free_group_ranges(Slrn_Group_Type * g)390 static void free_group_ranges (Slrn_Group_Type *g) /*{{{*/
391 {
392    Slrn_Range_Type *r, *rnext;
393 
394    if (g == NULL) return;
395 
396    r = g->range.next;
397    while (r != NULL)
398      {
399 	rnext = r->next;
400 	SLFREE (r);
401 	r = rnext;
402      }
403    g->range.next = NULL;
404 }
405 
406 /*}}}*/
407 
slrn_catchup_group(void)408 void slrn_catchup_group (void) /*{{{*/
409 {
410    if (Slrn_Group_Current_Group == NULL) return;
411    free_group_ranges (Slrn_Group_Current_Group);
412    slrn_add_group_ranges (Slrn_Group_Current_Group, 1, Slrn_Group_Current_Group->range.max);
413    Slrn_Group_Current_Group->flags |= GROUP_TOUCHED;
414 }
415 
416 /*}}}*/
417 
slrn_uncatchup_group(void)418 void slrn_uncatchup_group (void) /*{{{*/
419 {
420    if (Slrn_Group_Current_Group == NULL)
421      return;
422    free_group_ranges (Slrn_Group_Current_Group);
423    slrn_add_group_ranges (Slrn_Group_Current_Group, 1, 1);
424    Slrn_Group_Current_Group->flags |= GROUP_TOUCHED;
425 }
426 
427 /*}}}*/
428 
429 /*}}}*/
430 
431 /*{{{ Misc Utility Functions */
432 
find_group_entry(char * name,unsigned int len)433 static Slrn_Group_Type *find_group_entry (char *name, unsigned int len) /*{{{*/
434 {
435    int hash_index;
436    unsigned long hash;
437    Slrn_Group_Type *g;
438 
439    hash = slrn_compute_hash ((unsigned char *) name,
440 			     (unsigned char *) name + len);
441 
442    hash_index = hash % GROUP_HASH_TABLE_SIZE;
443    g = Group_Hash_Table[hash_index];
444 
445    while (g != NULL)
446      {
447 	if ((g->hash == hash) && !strncmp (name, g->name, len))
448 	  {
449 	     if (len == strlen (g->name)) break;;
450 	  }
451 	g = g->hash_next;
452      }
453    return g;
454 }
455 
456 /*}}}*/
create_group_entry(char * name,unsigned int len,int min,int max,int query_server,int skip_find)457 static Slrn_Group_Type *create_group_entry (char *name, unsigned int len, /*{{{*/
458 					    int min, int max, int query_server,
459 					    int skip_find)
460 {
461    int hash_index;
462    unsigned long hash;
463    Slrn_Group_Type *g;
464 
465    if (skip_find == 0)
466      {
467 	g = find_group_entry (name, len);
468 	if (g != NULL) return g;
469      }
470 
471    g = (Slrn_Group_Type *) slrn_safe_malloc (sizeof (Slrn_Group_Type));
472 
473    if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
474    strncpy (g->name, name, len);
475    g->name [len] = 0;
476 
477    if (query_server)
478      {
479 	int status;
480 
481 	status = Slrn_Server_Obj->sv_select_group (g->name, &min, &max);
482 	if (status != OK_GROUP)
483 	  {
484 	     if (status != -1)
485 	       {
486 		  Slrn_Groups_Dirty = 1;
487 #ifdef KANJI
488 		  slrn_error (Slrn_Japanese_Messages
489 			      ? "���롼�� %s ��̵���Τ褦�Ǥ���"
490 			      : "Group %s may be bogus.", g->name);
491 #else
492 		  slrn_error ("Group %s may be bogus.", g->name);
493 #endif
494 	       }
495 	     SLFREE (g);
496 	     return NULL;
497 	  }
498      }
499    g->range.min = min;
500    g->range.max = max;
501    if (max > 0)
502      g->unread = max - min + 1;
503    else g->unread = 0;
504 
505    g->flags = (GROUP_UNSUBSCRIBED | GROUP_HIDDEN);
506 
507    hash = slrn_compute_hash ((unsigned char *) name,
508 			     (unsigned char *) name + len);
509    hash_index = hash % GROUP_HASH_TABLE_SIZE;
510 
511    g->hash = hash;
512    g->hash_next = Group_Hash_Table[hash_index];
513    Group_Hash_Table[hash_index] = g;
514 
515    if (Groups == NULL)
516      {
517 	Slrn_Group_Current_Group = Groups = g;
518      }
519    else
520      {
521 	if (Slrn_Group_Current_Group == NULL) Slrn_Group_Current_Group = Groups;
522 	g->next = Slrn_Group_Current_Group->next;
523 	if (g->next != NULL) g->next->prev = g;
524 	Slrn_Group_Current_Group->next = g;
525 	g->prev = Slrn_Group_Current_Group;
526      }
527    Slrn_Group_Current_Group = g;
528    return g;
529 }
530 
531 /*}}}*/
add_group(char * name,unsigned int len,unsigned int subscribe_flag,int create_flag)532 static int add_group (char *name, unsigned int len, /*{{{*/
533 		      unsigned int subscribe_flag, int create_flag)
534 {
535    char ch;
536    Slrn_Group_Type *g;
537 
538    g = find_group_entry (name, len);
539    if (g == NULL)
540      {
541 	if (Slrn_List_Active_File)
542 	  {
543 	     char namebuf[MAX_GROUP_NAME_LEN + 1];
544 	     if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
545 	     strncpy (namebuf, name, len);
546 	     namebuf[len] = 0;
547 #ifdef KANJI
548 	     slrn_message_now(Slrn_Japanese_Messages
549 			      ? "���롼�� %s ��̵���Ǥ���̵�뤷�ޤ���"
550 			      : "Group %s is bogus,  Ignoring it.", namebuf);
551 #else
552 	     fprintf (stderr, "Group %s is bogus,  Ignoring it.\r\n", namebuf);
553 #endif
554 	     return -1;
555 	  }
556 	else g = create_group_entry (name, len, -1, -1,
557 				     !(subscribe_flag & GROUP_UNSUBSCRIBED),
558 				     0);
559 	if (g == NULL) return -1;
560      }
561    Slrn_Groups_Dirty = 1;
562 
563    /* If we have already processed this, then the group is duplicated in
564     * the newsrc file.  Throw it out now.
565     */
566    if (g->flags & GROUP_PROCESSED) return -1;
567 
568    Slrn_Group_Current_Group = g;
569    g->flags = subscribe_flag;
570    g->flags |= GROUP_PROCESSED;
571 
572    if (subscribe_flag & GROUP_UNSUBSCRIBED)
573      {
574 	g->flags |= GROUP_HIDDEN;
575 
576 	if (create_flag) return 0;
577 	g->unread = 0;
578 	/* if (Slrn_List_Active_File == 0) return 0; */
579      }
580 
581    if (create_flag) return 0;
582 
583    /* find ranges for this */
584    name += len;			       /* skip past name */
585    if (*name) name++;			       /* skip colon */
586    while (1)
587      {
588 	int min, max;
589 	/* skip white space and delimiters */
590 	while (((ch = *name) != 0) && ((ch <= ' ') || (ch == ','))) name++;
591 	if ((ch < '0') || (ch > '9')) break;
592 	min = atoi (name++);
593 	while (((ch = *name) != 0) && (ch >= '0') && (ch <= '9')) name++;
594 	if (ch == '-')
595 	  {
596 	     name++;
597 	     max = atoi (name);
598 	     while (((ch = *name) != 0) && (ch >= '0') && (ch <= '9')) name++;
599 	  }
600 	else max = min;
601 
602 	slrn_add_group_ranges (Slrn_Group_Current_Group, min, max);
603      }
604 
605    if ((g->range.next != NULL)
606        && (g->range.next->min < g->unread)
607        && (g->range.next->min > 1))
608      g->unread -= g->range.next->min - 1;
609 
610    return 0;
611 }
612 
613 /*}}}*/
614 
find_line_num(void)615 static void find_line_num (void) /*{{{*/
616 {
617    Group_Window.lines = (SLscroll_Type *) Groups;
618    Group_Window.current_line = (SLscroll_Type *) Slrn_Group_Current_Group;
619    (void) SLscroll_find_line_num (&Group_Window);
620 }
621 
622 /*}}}*/
init_group_win_struct(void)623 static void init_group_win_struct (void) /*{{{*/
624 {
625    Group_Window.nrows = SLtt_Screen_Rows - 3;
626    Group_Window.hidden_mask = GROUP_HIDDEN;
627    Group_Window.current_line = (SLscroll_Type *) Slrn_Group_Current_Group;
628    Group_Window.cannot_scroll = SLtt_Term_Cannot_Scroll;
629    Group_Window.lines = (SLscroll_Type *) Groups;
630    Group_Window.border = 1;
631    if (Slrn_Scroll_By_Page)
632      {
633 	/* Slrn_Group_Window.border = 0; */
634 	Group_Window.cannot_scroll = 2;
635      }
636    find_line_num ();
637 }
638 
639 /*}}}*/
free_all_groups(void)640 static void free_all_groups (void) /*{{{*/
641 {
642    Slrn_Group_Type *g = Groups;
643    Slrn_Group_Type *nextg;
644    int i;
645 
646    while (g != NULL)
647      {
648 	nextg = g->next;
649 	slrn_free (g->descript);
650 	free_group_ranges (g);
651 	SLFREE(g);
652 	g = nextg;
653      }
654    Groups = NULL;
655    Slrn_Group_Current_Group = NULL;
656    SLMEMSET((char *) &Group_Window, 0, sizeof (SLscroll_Window_Type));
657 
658    for (i = 0; i < GROUP_HASH_TABLE_SIZE; i++) Group_Hash_Table[i] = NULL;
659 }
660 
661 /*}}}*/
662 
find_group(char * name)663 static int find_group (char *name) /*{{{*/
664 {
665    Slrn_Group_Type *g = find_group_entry (name, strlen (name));
666    if (g == NULL) return 0;
667 
668    g->flags &= ~GROUP_HIDDEN;
669    Slrn_Group_Current_Group = g;
670    find_line_num ();
671    return 1;
672 }
673 
674 /*}}}*/
675 
read_group_regexp(char * prompt,char * origpat)676 static SLRegexp_Type *read_group_regexp (char *prompt, char *origpat) /*{{{*/
677 {
678    static char pattern[256];
679 
680    if (slrn_read_input (prompt, NULL, pattern, 1, 0) <= 0) return NULL;
681 
682    if (origpat != NULL)
683      strcpy (origpat, pattern);
684 
685    return slrn_compile_regexp_pattern (slrn_fix_regexp (pattern));
686 }
687 
688 /*}}}*/
process_xgtitle_info(void)689 static Slrn_Group_Type *process_xgtitle_info (void) /*{{{*/
690 {
691    char buf [NNTP_BUFFER_SIZE];
692    Slrn_Group_Type *first = NULL, *save = Slrn_Group_Current_Group;
693 
694    while (1)
695      {
696 	char *b, ch;
697 	unsigned int len;
698 	Slrn_Group_Type *g;
699 	int status;
700 
701 	status = Slrn_Server_Obj->sv_read_line(buf, sizeof(buf));
702 	if (status <= 0)
703 	  break;
704 
705 	b = buf;
706 	while (((ch = *b) != 0)
707 	       && (ch != ' ')
708 	       && (ch != '\n')
709 	       && (ch != '\t'))
710 	  b++;
711 
712 	len = (unsigned int) (b - buf);
713 	if (len == 0) continue;
714 	*b = 0;
715 
716 	g = create_group_entry (buf, len,
717 				-1, -1, 0, 0);
718 
719 	if (g != NULL)
720 	  {
721 	     g->flags &= ~GROUP_HIDDEN;
722 	     if ((first == NULL) && (g->flags & GROUP_UNSUBSCRIBED))
723 	       first = g;
724 	  }
725      }
726 
727    if (save != Slrn_Group_Current_Group)
728      {
729 	Slrn_Group_Current_Group = save;
730 	find_line_num ();
731      }
732    return first;
733 }
734 
735 /*}}}*/
736 
add_unsubscribed_group(char * name)737 static void add_unsubscribed_group (char *name) /*{{{*/
738 {
739    Unsubscribed_Slrn_Group_Type *g;
740    char *p;
741    unsigned int len;
742 
743    g = (Unsubscribed_Slrn_Group_Type *) slrn_safe_malloc (sizeof (Unsubscribed_Slrn_Group_Type));
744 
745    g->next = Unsubscribed_Groups;
746    Unsubscribed_Groups = g;
747 
748    p = name;
749    while (*p > ' ') p++;
750    *p = 0;
751    len = p - name;
752 
753    if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
754    strncpy (g->name, name, len);
755    g->name[len] = 0;
756 }
757 
758 /*}}}*/
759 
760 /*}}}*/
761 
slrn_group_search(char * str)762 int slrn_group_search (char *str) /*{{{*/
763 {
764    SLsearch_Type st;
765    Slrn_Group_Type *g;
766 
767    g = Slrn_Group_Current_Group;
768    if (g == NULL) return 0;
769 
770    SLsearch_init (str, 1, 0, &st);
771 
772    do
773      {
774 	g = g->next;
775 	if (g == NULL)
776 	  {
777 	     g = Groups;
778 	  }
779 
780 	if ((g->flags & GROUP_HIDDEN) == 0)
781 	  {
782 	     if ((NULL != SLsearch ((unsigned char *) g->name,
783 				   (unsigned char *) g->name + strlen (g->name),
784 				   &st))
785 		 || ((NULL != g->descript)
786 		     && (NULL != SLsearch ((unsigned char *) g->descript,
787 					   (unsigned char *) g->descript + strlen (g->descript),
788 					   &st))))
789 		  break;
790 	  }
791      }
792    while (g != Slrn_Group_Current_Group);
793 
794    if (g == Slrn_Group_Current_Group)
795      return 0;
796 
797    Slrn_Group_Current_Group = g;
798    find_line_num ();
799    return 1;
800 }
801 
802 /*}}}*/
slrn_group_up_n(unsigned int n)803 unsigned int slrn_group_up_n (unsigned int n) /*{{{*/
804 {
805    n = SLscroll_prev_n (&Group_Window, n);
806    Slrn_Group_Current_Group = (Slrn_Group_Type *) Group_Window.current_line;
807    return n;
808 }
809 
810 /*}}}*/
slrn_group_down_n(unsigned int n)811 unsigned int slrn_group_down_n (unsigned int n) /*{{{*/
812 {
813    n = SLscroll_next_n (&Group_Window, n);
814    Slrn_Group_Current_Group = (Slrn_Group_Type *) Group_Window.current_line;
815    return n;
816 }
817 
818 /*}}}*/
819 
slrn_group_select_group(void)820 int slrn_group_select_group (void) /*{{{*/
821 {
822    int min, max, n, max_available, last_n;
823    int ret;
824    Slrn_Range_Type *r;
825 #ifdef KANJI
826    int prefix, direc = 0;
827 #else
828    int prefix;
829 #endif
830 
831    if (Slrn_Prefix_Arg_Ptr != NULL)
832      {
833 	prefix = *Slrn_Prefix_Arg_Ptr;
834 	Slrn_Prefix_Arg_Ptr = NULL;
835      }
836    else prefix = 0;
837 
838    if (Slrn_Group_Current_Group == NULL) return -1;
839 
840    last_n = Slrn_Group_Current_Group->unread;
841 
842    if (-1 == group_sync_group_with_server (Slrn_Group_Current_Group, &min, &max))
843      return -1;
844 
845    n = Slrn_Group_Current_Group->unread;
846 
847 #ifndef KANJI
848 #if 1
849    if ((prefix == 0) && (n == 0) && (n != last_n))
850      return -1;
851 #endif
852 #endif
853 
854    max_available = Slrn_Group_Current_Group->range.max - Slrn_Group_Current_Group->range.min + 1;
855 
856 #ifdef KANJI
857    if ((prefix & 1) || (n == 0 && last_n == 0))
858 #else
859    if ((prefix & 1) || (n == 0))
860 #endif
861      n = max_available;
862 
863    if ((prefix & 1) ||
864        ((n > Slrn_Query_Group_Cutoff)
865 	&& (Slrn_Query_Group_Cutoff > 0)))
866      {
867 	char int_prompt_buf[256];
868 #ifdef KANJI
869 	sprintf (int_prompt_buf, Slrn_Japanese_Messages
870 		 ? "%s: �������ɤߤޤ���?"
871 		 : "%s: Read how many?", Slrn_Group_Current_Group->name);
872 #else
873 	sprintf (int_prompt_buf, "%s: Read how many?", Slrn_Group_Current_Group->name);
874 #endif
875 	if ((-1 == slrn_read_integer (int_prompt_buf, &n, &n))
876 #ifdef KANJI
877 	    /* �������˥ޥ��ʥ����դ��������Ť����������ɤ��褦�ˤ��� */
878 	    || (n == 0))
879 #else
880 	    || (n <= 0))
881 #endif
882 	  {
883 	     slrn_clear_message ();
884 	     Slrn_Full_Screen_Update = 1;
885 	     return 0;
886 	  }
887 
888 #ifdef KANJI
889 	if (n < 0) {
890 	  direc = 1;
891 	}
892 	else
893 #endif
894 	if ((0 == (prefix & 1))
895 	    && (Slrn_Group_Current_Group->unread != 0))
896 	  {
897 	     r = Slrn_Group_Current_Group->range.next;
898 	     if (r != NULL)
899 	       {
900 		  while (r->next != NULL) r = r->next;
901 		  if (r->max + n > max)
902 		    n = -n;	       /* special treatment in article mode
903 					* because we will need to query the
904 					* server about articles in a group
905 					* that we have already read.
906 					*/
907 	       }
908 	  }
909      }
910    else if ((0 == (prefix & 1)) && (Slrn_Group_Current_Group->unread != 0))
911      n = 0;
912 
913    ret = slrn_select_article_mode (Slrn_Group_Current_Group, n,
914 #ifdef KANJI
915 				   ((prefix & 2) == 0), direc);
916 #else
917 				   ((prefix & 2) == 0));
918 #endif
919 
920    if (ret == -2)
921      slrn_catchup_group ();
922 
923    if (ret == 0) return 0;
924    return -1;
925 }
926 
927 /*}}}*/
slrn_select_next_group(void)928 void slrn_select_next_group (void) /*{{{*/
929 {
930    if (Slrn_Group_Current_Group == NULL)
931      return;
932 
933    while ((SLang_Error == 0) && (1 == slrn_group_down_n (1)))
934      {
935 	if (Slrn_Group_Current_Group->unread == 0)
936 	  continue;
937 
938 	if (0 == slrn_group_select_group ())
939 	  break;
940      }
941 }
942 
943 /*}}}*/
slrn_select_prev_group(void)944 void slrn_select_prev_group (void) /*{{{*/
945 {
946    if (Slrn_Group_Current_Group == NULL)
947      return;
948 
949    while ((SLang_Error == 0) && (1 == slrn_group_up_n (1)))
950      {
951        if (Slrn_Group_Current_Group->unread == 0)
952          continue;
953 
954        if (0 == slrn_group_select_group ())
955          break;
956      }
957 }
958 
959 
960 /*}}}*/
961 
962 
963 /*{{{ Interactive commands */
964 
slrn_group_quit(void)965 void slrn_group_quit (void) /*{{{*/
966 {
967    if (Slrn_User_Wants_Confirmation
968        && (Slrn_Batch == 0)
969 #ifdef KANJI
970        && (slrn_get_yesno (1, Slrn_Japanese_Messages
971 			   ? "������ȴ���ޤ���"
972 			   : "Do you really want to quit") <= 0)) return;
973 #else
974        && (slrn_get_yesno (1, "Do you really want to quit") <= 0)) return;
975 #endif
976 
977    if ((Slrn_Groups_Dirty) && (-1 == slrn_write_newsrc ()))
978      {
979 	if (Slrn_Batch)
980 	  slrn_quit (1);
981 
982 	slrn_smg_refresh ();
983 	if (Slrn_Batch == 0) sleep (2);
984 	if (slrn_get_yesno (0, "Write to newsrc file failed.  Quit anyway") <= 0)
985 	  return;
986      }
987    slrn_quit (0);
988 }
989 
990 /*}}}*/
991 
group_pagedown(void)992 static void group_pagedown (void) /*{{{*/
993 {
994    Slrn_Full_Screen_Update = 1;
995 
996    if (-1 == SLscroll_pagedown (&Group_Window))
997      slrn_error ("End of Buffer.");
998    Slrn_Group_Current_Group = (Slrn_Group_Type *) Group_Window.current_line;
999 }
1000 
1001 /*}}}*/
1002 
group_pageup(void)1003 static void group_pageup (void) /*{{{*/
1004 {
1005    Slrn_Full_Screen_Update = 1;
1006 
1007    if (-1 == SLscroll_pageup (&Group_Window))
1008      slrn_error ("Top of Buffer.");
1009 
1010    Slrn_Group_Current_Group = (Slrn_Group_Type *) Group_Window.current_line;
1011 }
1012 
1013 /*}}}*/
1014 
group_up(void)1015 static void group_up (void) /*{{{*/
1016 {
1017    if (0 == slrn_group_up_n (1))
1018      {
1019 	slrn_error ("Top of buffer.");
1020      }
1021 }
1022 
1023 /*}}}*/
1024 
refresh_groups_cmd(void)1025 static void refresh_groups_cmd (void) /*{{{*/
1026 {
1027    char name[MAX_GROUP_NAME_LEN + 1];
1028 
1029    *name = 0;
1030    if (Slrn_Group_Current_Group != NULL)
1031      strcpy (name, Slrn_Group_Current_Group->name);
1032 
1033    /* slrn_set_suspension (1); */
1034    save_newsrc_cmd ();
1035    free_all_groups ();
1036    /*
1037     * Groups_Hidden gets toggled in slrn_read_newsrc, which gets called
1038     * in slrn_get_new_news.  Therefore, pre-toggle Groups_Hidden ahead of
1039     * time.
1040     */
1041    Groups_Hidden = !Groups_Hidden;
1042    if (-1 == slrn_get_new_news (1, 0))
1043      {
1044 	slrn_quit (0);
1045      }
1046    if (*name)
1047      (void) find_group (name);
1048    /* slrn_set_suspension (0); */
1049 
1050    init_group_win_struct ();
1051    group_quick_help ();
1052 }
1053 
1054 /*}}}*/
1055 
group_search_cmd(void)1056 static void group_search_cmd (void) /*{{{*/
1057 {
1058    static char search_str[256];
1059    Slrn_Group_Type *g;
1060    unsigned int n;
1061 
1062    g = Slrn_Group_Current_Group;
1063    if (g == NULL) return;
1064 
1065 #ifdef KANJI
1066    if (slrn_read_input (Slrn_Japanese_Messages
1067 			? "����"
1068 			: "Search", search_str, NULL, 1, 0) <= 0) return;
1069 #else
1070    if (slrn_read_input ("Search", search_str, NULL, 1, 0) <= 0) return;
1071 #endif
1072 
1073    n = Group_Window.line_num;
1074    if (0 == slrn_group_search (search_str))
1075      {
1076 #ifdef KANJI
1077         slrn_error (Slrn_Japanese_Messages
1078 		    ? "���Ĥ���ޤ���"
1079 		    : "Not found.");
1080 #else
1081         slrn_error ("Not found.");
1082 #endif
1083 	return;
1084      }
1085 
1086    if (n > Group_Window.line_num)
1087      slrn_message ("Search wrapped.");
1088 }
1089 
1090 /*}}}*/
1091 
add_group_cmd(void)1092 static void add_group_cmd (void) /*{{{*/
1093 {
1094    char group[256];
1095 
1096    *group = 0;
1097 #ifdef KANJI
1098    if (slrn_read_input (Slrn_Japanese_Messages
1099 			? "���롼�פ��ɲ�"
1100 			: "Add group", NULL, group, 1, 0) > 0)
1101 #else
1102    if (slrn_read_input ("Add group", NULL, group, 1, 0) > 0)
1103 #endif
1104      {
1105 	if (!find_group (group)
1106 	    && (Slrn_List_Active_File == 0))
1107 	  add_group (group, strlen (group), 0, 0);
1108 	Slrn_Groups_Dirty = 1;
1109 	Slrn_Full_Screen_Update = 1;
1110 	find_line_num ();
1111      }
1112 }
1113 
1114 /*}}}*/
1115 
group_down(void)1116 static void group_down (void) /*{{{*/
1117 {
1118    if (1 != slrn_group_down_n (1))
1119      {
1120 	slrn_error ("End of Buffer.");
1121      }
1122 }
1123 
1124 /*}}}*/
1125 
transpose_groups(void)1126 static void transpose_groups (void) /*{{{*/
1127 {
1128    Slrn_Group_Type *g, *g1, *tmp;
1129 
1130    if (NULL == (g = Slrn_Group_Current_Group))
1131      return;
1132 
1133    if (1 != slrn_group_up_n (1))
1134      return;
1135 
1136    g1 = Slrn_Group_Current_Group;
1137    tmp = g1->next;
1138 
1139    g1->next = g->next;
1140    if (g1->next != NULL) g1->next->prev = g1;
1141    g->next = tmp;
1142    tmp->prev = g;		       /* tmp cannot be NULL but it can be
1143 					* equal to g.  This link is corrected
1144 					* below
1145 					*/
1146 
1147    tmp = g1->prev;
1148    g1->prev = g->prev;
1149    g1->prev->next = g1;		       /* g1->prev cannot be NULL */
1150    g->prev = tmp;
1151    if (tmp != NULL) tmp->next = g;
1152 
1153    if (g1 == Groups) Groups = g;
1154 
1155    find_line_num ();
1156 
1157    (void) slrn_group_down_n (1);
1158 
1159    Slrn_Full_Screen_Update = 1;
1160    Slrn_Groups_Dirty = 1;
1161 }
1162 
1163 /*}}}*/
1164 
move_group_cmd(void)1165 static void move_group_cmd (void) /*{{{*/
1166 {
1167    SLang_Key_Type *key;
1168    void (*f)(void);
1169    Slrn_Group_Type *g;
1170 
1171    if (Slrn_Batch) return;
1172    if (Slrn_Group_Current_Group == NULL) return;
1173    g = Slrn_Group_Current_Group;
1174 
1175    while (1)
1176      {
1177 #ifdef KANJI
1178 	slrn_message_now (Slrn_Japanese_Messages
1179 			  ? "%s ���ư�档��λ���� RETURN ������"
1180 			  : "Moving %s. Press RETURN when finished.", Slrn_Group_Current_Group->name);
1181 #else
1182 	slrn_message_now ("Moving %s. Press RETURN when finished.", Slrn_Group_Current_Group->name);
1183 #endif
1184 
1185 	key = SLang_do_key (Slrn_Group_Keymap, (int (*)(void)) SLang_getkey);
1186 
1187 	if ((key == NULL)
1188 	    || (key->type == SLKEY_F_INTERPRET))
1189 	  f = NULL;
1190 	else f = (void (*)(void)) key->f.f;
1191 
1192 	if (f == group_up)
1193 	  {
1194 	     transpose_groups ();
1195 	     if (2 != slrn_group_up_n (2))
1196 	       break;
1197 	  }
1198 	else if (f == group_down)
1199 	  {
1200 	     if (1 != slrn_group_down_n (1))
1201 	       break;
1202 
1203 	     transpose_groups ();
1204 
1205 	     if (1 != slrn_group_up_n (1))
1206 	       break;
1207 	  }
1208 	else break;
1209 
1210 	if (g != Slrn_Group_Current_Group)
1211 	  {
1212 	     Slrn_Group_Current_Group = g;
1213 	     find_line_num ();
1214 	  }
1215 
1216 	/* For a recenter if possible. */
1217 	/* if (Group_Window.top_window_line == Group_Window.current_line) */
1218 	Group_Window.top_window_line = NULL;
1219 
1220 	slrn_update_screen ();
1221      }
1222 }
1223 
1224 /*}}}*/
1225 
subscribe(void)1226 static void subscribe (void) /*{{{*/
1227 {
1228    SLRegexp_Type *re;
1229    Slrn_Group_Type *g;
1230 
1231    if (Slrn_Group_Current_Group == NULL) return;
1232 
1233    if (Slrn_Prefix_Arg_Ptr == NULL)
1234      {
1235 	Slrn_Group_Current_Group->flags &= ~GROUP_UNSUBSCRIBED;
1236 	Slrn_Group_Current_Group->flags |= GROUP_TOUCHED;
1237 	slrn_group_down_n (1);
1238 	Slrn_Groups_Dirty = 1;
1239 	return;
1240      }
1241 
1242    Slrn_Prefix_Arg_Ptr = NULL;
1243 
1244 #ifdef KANJI
1245    if (NULL == (re = read_group_regexp (Slrn_Japanese_Messages
1246 					? "���ɤ���ѥ�����"
1247 					: "Subscribe pattern", NULL)))
1248 #else
1249    if (NULL == (re = read_group_regexp ("Subscribe pattern", NULL)))
1250 #endif
1251      return;
1252 
1253    g = Groups;
1254    while (g != NULL)
1255      {
1256 	if (g->flags & GROUP_UNSUBSCRIBED)
1257 	  {
1258 	     if (NULL != slrn_regexp_match (re, g->name))
1259 	       {
1260 		  g->flags &= ~GROUP_HIDDEN;
1261 		  g->flags &= ~GROUP_UNSUBSCRIBED;
1262 		  g->flags |= GROUP_TOUCHED;
1263 	       }
1264 	  }
1265 	g = g->next;
1266      }
1267    find_line_num ();
1268    Slrn_Full_Screen_Update = 1;
1269 }
1270 
1271 /*}}}*/
1272 
catch_up(void)1273 static void catch_up (void) /*{{{*/
1274 {
1275    if ((Slrn_Group_Current_Group == NULL)
1276        || (Slrn_User_Wants_Confirmation
1277 	   && (Slrn_Batch == 0)
1278 #ifdef KANJI
1279 	   && slrn_get_yesno(1, Slrn_Japanese_Messages
1280 			     ? "%s ����ɤ˥ޡ������ޤ���"
1281 			     : "Mark %s as read", Slrn_Group_Current_Group->name) <= 0))
1282 #else
1283 	   && slrn_get_yesno(1, "Mark %s as read", Slrn_Group_Current_Group->name) <= 0))
1284 #endif
1285      return;
1286 
1287    slrn_catchup_group ();
1288 #ifdef KANJI
1289    slrn_message (Slrn_Japanese_Messages
1290                  ? "���롼�פ���ɤ˥ޡ������ޤ�����"
1291                  : "Group marked as read.");
1292 #else
1293    slrn_message ("Group marked as read.");
1294 #endif
1295    (void) slrn_group_down_n (1);
1296 }
1297 
1298 /*}}}*/
1299 
uncatch_up(void)1300 static void uncatch_up (void) /*{{{*/
1301 {
1302    if ((Slrn_Group_Current_Group == NULL)
1303        || (Slrn_User_Wants_Confirmation
1304 	   && (Slrn_Batch == 0)
1305 #ifdef KANJI
1306 	   && slrn_get_yesno(1, Slrn_Japanese_Messages
1307 	                     ? "%s ��̤�ɤ˥ޡ������ޤ���"
1308                              : "Mark %s as un-read", Slrn_Group_Current_Group->name) <= 0))
1309 #else
1310 	   && slrn_get_yesno(1, "Mark %s as un-read", Slrn_Group_Current_Group->name) <= 0))
1311 #endif
1312      return;
1313 
1314    slrn_uncatchup_group ();
1315 #ifdef KANJI
1316    slrn_message (Slrn_Japanese_Messages
1317                  ? "���롼�פ�̤�ɤ˥ޡ������ޤ�����"
1318                  : "Group marked as un-read.");
1319 #else
1320    slrn_message ("Group marked as un-read.");
1321 #endif
1322    (void) slrn_group_down_n (1);
1323 }
1324 
1325 /*}}}*/
1326 
unsubscribe(void)1327 static void unsubscribe (void) /*{{{*/
1328 {
1329    SLRegexp_Type *re;
1330    Slrn_Group_Type *g;
1331 
1332    if (Slrn_Group_Current_Group == NULL) return;
1333 
1334    if (Slrn_Prefix_Arg_Ptr == NULL)
1335      {
1336 	Slrn_Group_Current_Group->flags |= GROUP_UNSUBSCRIBED | GROUP_TOUCHED;
1337 	slrn_group_down_n (1);
1338 	Slrn_Groups_Dirty = 1;
1339 	return;
1340      }
1341 
1342    Slrn_Prefix_Arg_Ptr = NULL;
1343 
1344 #ifdef KANJI
1345    if (NULL == (re = read_group_regexp (Slrn_Japanese_Messages
1346    ? "���ɤ��ʤ��ѥ�����"
1347    : "Un-Subscribe pattern", NULL)))
1348 #else
1349    if (NULL == (re = read_group_regexp ("Un-Subscribe pattern", NULL)))
1350 #endif
1351      return;
1352 
1353    g = Groups;
1354    while (g != NULL)
1355      {
1356 	if ((g->flags & GROUP_UNSUBSCRIBED) == 0)
1357 	  {
1358 	     if (NULL != (slrn_regexp_match (re, g->name)))
1359 	       {
1360 		  g->flags &= ~GROUP_HIDDEN;
1361 		  g->flags |= (GROUP_TOUCHED | GROUP_UNSUBSCRIBED);
1362 	       }
1363 	  }
1364 	g = g->next;
1365      }
1366    find_line_num ();
1367    Slrn_Full_Screen_Update = 1;
1368 }
1369 
1370 /*}}}*/
1371 
group_bob(void)1372 static void group_bob (void)
1373 {
1374    while (slrn_group_up_n (1000));
1375 }
1376 
group_eob(void)1377 static void group_eob (void)
1378 {
1379    while (slrn_group_down_n (1000));
1380 }
1381 
1382 
toggle_list_all_groups1(int hide_flag)1383 static void toggle_list_all_groups1 (int hide_flag) /*{{{*/
1384 {
1385    Slrn_Group_Type *g, *first_found = NULL;
1386    static int all_hidden = 1;
1387 
1388    g = Groups;
1389 
1390    if (hide_flag != -1)
1391      {
1392 	all_hidden = hide_flag;
1393      }
1394    else all_hidden = !all_hidden;
1395 
1396    if (all_hidden)
1397      {
1398 	while (g != NULL)
1399 	  {
1400 	     if (g->flags & GROUP_UNSUBSCRIBED) g->flags |= GROUP_HIDDEN;
1401 	     g = g->next;
1402 	  }
1403      }
1404    else
1405      {
1406 	SLRegexp_Type *re;
1407 	char origpat[256];
1408 
1409 	if (NULL == (re = read_group_regexp ("List Groups (e.g., comp*unix*)",
1410 					     origpat)))
1411 	  {
1412 	     all_hidden = 1;
1413 	     return;
1414 	  }
1415 
1416 	if ((Slrn_List_Active_File == 0)
1417 	    && Slrn_Use_Xgtitle
1418 	    && (OK_XGTITLE == Slrn_Server_Obj->sv_xgtitle_cmd (origpat)))
1419 	  {
1420 	     first_found = process_xgtitle_info ();
1421 	  }
1422 	else while (g != NULL)
1423 	  {
1424 	     if (g->flags & GROUP_UNSUBSCRIBED)
1425 	       {
1426 		  if (NULL != slrn_regexp_match (re, g->name))
1427 		    {
1428 		       if (first_found == NULL) first_found = g;
1429 		       g->flags &= ~GROUP_HIDDEN;
1430 		    }
1431 	       }
1432 	     g = g->next;
1433 	  }
1434      }
1435 
1436    g = Slrn_Group_Current_Group;
1437    if (first_found != NULL)
1438      g = first_found;
1439    else
1440      {
1441 	while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->next;
1442 	if ((g == NULL) && (Slrn_Group_Current_Group != NULL))
1443 	  {
1444 	     g = Slrn_Group_Current_Group -> prev;
1445 	     while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->prev;
1446 	  }
1447      }
1448    Slrn_Group_Current_Group = g;
1449 
1450    Slrn_Full_Screen_Update = 1;
1451 
1452    if ((all_hidden == 0) && (Slrn_Group_Current_Group == NULL))
1453      {
1454 	Slrn_Group_Current_Group = Groups;
1455 	if ((Slrn_Group_Current_Group != NULL)
1456 	    && (Slrn_Group_Current_Group->flags & GROUP_HIDDEN))
1457 	  {
1458 	     Slrn_Group_Current_Group = NULL;
1459 	  }
1460      }
1461 
1462    find_line_num ();
1463 }
1464 
1465 /*}}}*/
1466 
toggle_list_all_groups(void)1467 static void toggle_list_all_groups (void) /*{{{*/
1468 {
1469    int mode;
1470    if (Slrn_Prefix_Arg_Ptr != NULL)
1471      mode = 1;
1472    else mode = -1;
1473 
1474    toggle_list_all_groups1 (mode);
1475 }
1476 
1477 /*}}}*/
1478 
toggle_group_display(void)1479 static void toggle_group_display (void) /*{{{*/
1480 {
1481    Slrn_Group_Display_Descriptions = !Slrn_Group_Display_Descriptions;
1482    Slrn_Full_Screen_Update = 1;
1483 }
1484 
1485 /*}}}*/
1486 
1487 
toggle_hide_groups(void)1488 static void toggle_hide_groups (void) /*{{{*/
1489 {
1490    Slrn_Group_Type *g;
1491 
1492    Groups_Hidden = !Groups_Hidden;
1493 
1494    g = Groups;
1495 
1496    if (Groups_Hidden)
1497      {
1498 	while (g != NULL)
1499 	  {
1500 	     if ((g->unread == 0)
1501 		 && ((g->flags & GROUP_UNSUBSCRIBED) == 0))
1502 	       g->flags |= GROUP_HIDDEN;
1503 
1504 	     g = g->next;
1505 	  }
1506      }
1507    else
1508      {
1509 	while (g != NULL)
1510 	  {
1511 	     if ((g->unread == 0)
1512 		 && ((g->flags & GROUP_UNSUBSCRIBED) == 0))
1513 	       g->flags &= ~GROUP_HIDDEN;
1514 
1515 	     g = g->next;
1516 	  }
1517      }
1518 
1519    g = Slrn_Group_Current_Group;
1520    if (g == NULL) g = Groups;
1521 
1522    while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->next;
1523    if ((g == NULL) && (Slrn_Group_Current_Group != NULL))
1524      {
1525 	g = Slrn_Group_Current_Group -> prev;
1526 	while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->prev;
1527      }
1528    Slrn_Group_Current_Group = g;
1529 
1530    find_line_num ();
1531 
1532    Slrn_Full_Screen_Update = 1;
1533 }
1534 
1535 /*}}}*/
1536 
1537 
select_group_cmd(void)1538 static void select_group_cmd (void)
1539 {
1540    if (-1 == slrn_group_select_group ())
1541 #ifdef KANJI
1542      slrn_error (Slrn_Japanese_Messages
1543 		 ? "̤�ɵ����Ϥ���ޤ���"
1544 		 : "No unread articles.");
1545 #else
1546      slrn_error ("No unread articles.");
1547 #endif
1548 }
1549 
slrn_post_cmd(void)1550 void slrn_post_cmd (void) /*{{{*/
1551 {
1552    char *name, *dist = NULL;	       /* "world"; */
1553    char group[256];
1554    char subj[256];
1555 
1556    if (Slrn_Post_Obj->po_can_post == 0)
1557      {
1558 #ifdef KANJI
1559 	slrn_error (Slrn_Japanese_Messages
1560 		    ? "��Ƥ����Ĥ���Ƥ��ޤ���"
1561 		    : "Posting not allowed.");
1562 #else
1563 	slrn_error ("Posting not allowed.");
1564 #endif
1565 	return;
1566      }
1567 
1568 #if SLRN_HAS_SLANG
1569    SLang_run_hooks ("post_hook", 0);
1570    if (SLang_Error)
1571      return;
1572 #endif
1573 
1574 #ifdef KANJI
1575    if (Slrn_User_Wants_Confirmation && (Slrn_Batch == 0) &&
1576        (slrn_get_yesno (1, Slrn_Japanese_Messages
1577 			? "��������Ƥ��ޤ���"
1578 			: "Are you sure that you want to post") <= 0))
1579 #else
1580    if (Slrn_User_Wants_Confirmation && (Slrn_Batch == 0) &&
1581        (slrn_get_yesno (1, "Are you sure that you want to post") <= 0))
1582 #endif
1583      return;
1584 
1585    if (Slrn_Group_Current_Group == NULL) name = ""; else name = Slrn_Group_Current_Group->name;
1586    strcpy (group, name);
1587    if (slrn_read_input ("Newsgroup", NULL, group, 1, 1) <= 0) return;
1588    *subj = 0; if (slrn_read_input ("Subject", NULL, subj, 1, 0) <= 0) return;
1589 
1590    (void) slrn_post (group, subj, dist);
1591 }
1592 
1593 /*}}}*/
1594 
toggle_scoring(void)1595 static void toggle_scoring (void) /*{{{*/
1596 {
1597    if (-1 == slrn_check_batch ())
1598      return;
1599 
1600 #ifdef KANJI
1601    switch (slrn_get_response ("FfSsNnCc\007",
1602 			      Slrn_Japanese_Messages
1603 			      ? "�������դ��Υ⡼������: \001Full(����), \001Simple(�ʰ�), \001None(�ʤ�), \001Cancel"
1604 			      : "Select scoring mode: \001Full, \001Simple, \001None, \001Cancel"))
1605 #else
1606    switch (slrn_get_response ("FfSsNnCc\007",
1607 			      "Select scoring mode: \001Full, \001Simple, \001None, \001Cancel"))
1608 #endif
1609      {
1610       case 'F':
1611       case 'f':
1612 	Slrn_Perform_Scoring = SLRN_XOVER_SCORING | SLRN_EXPENSIVE_SCORING;
1613 	slrn_message ("Full Header Scoring enabled.");
1614 	break;
1615 
1616       case 'S':
1617       case 's':
1618 	Slrn_Perform_Scoring = SLRN_XOVER_SCORING;
1619 	slrn_message ("Expensive Scoring disabled.");
1620 	break;
1621 
1622       case 'N':
1623       case 'n':
1624 	Slrn_Perform_Scoring = 0;
1625 	slrn_message ("Scoring disabled.");
1626 	break;
1627 
1628       default:
1629 	slrn_clear_message ();
1630 	break;
1631      }
1632 }
1633 
1634 /*}}}*/
1635 
save_newsrc_cmd(void)1636 static void save_newsrc_cmd (void) /*{{{*/
1637 {
1638    if (Slrn_Groups_Dirty)
1639      {
1640 	slrn_write_newsrc ();
1641      }
1642    else
1643      {
1644 #ifdef KANJI
1645 	slrn_message (Slrn_Japanese_Messages
1646 		      ? "�����֤��٤��ѹ��Ϥ���ޤ���"
1647 		      : "No changes need to be saved.");
1648 #else
1649 	slrn_message ("No changes need to be saved.");
1650 #endif
1651      }
1652    slrn_smg_refresh ();
1653 }
1654 
1655 /*}}}*/
1656 
1657 /*}}}*/
1658 
1659 /*{{{ Group Mode Initialization/Keybindings */
1660 
slrn_group_hup(int sig)1661 static void slrn_group_hup (int sig)
1662 {
1663    slrn_write_newsrc ();
1664    slrn_quit (sig);
1665 }
1666 
enter_group_mode_hook(void)1667 static void enter_group_mode_hook (void)
1668 {
1669 #if SLRN_HAS_SLANG
1670    SLang_run_hooks ("group_mode_hook", 0);
1671 #endif
1672 }
1673 
group_winch_sig(int old_r,int old_c)1674 static void group_winch_sig (int old_r, int old_c)
1675 {
1676    (void) old_r; (void) old_c;
1677 
1678    if (SLtt_Screen_Rows > 3)
1679      Group_Window.nrows = SLtt_Screen_Rows - 3;
1680    else
1681      Group_Window.nrows = 1;
1682 }
1683 
1684 
1685 static Slrn_Mode_Type Group_Mode_Cap =
1686 {
1687    NULL,
1688    group_update_screen,		       /* redraw */
1689    group_winch_sig,		       /* sig winch hook */
1690    slrn_group_hup,		       /* hangup hook */
1691    enter_group_mode_hook,	       /* enter_mode_hook */
1692    SLRN_GROUP_MODE
1693 };
1694 
1695 /*{{{ Group Mode Keybindings */
1696 
1697 #define A_KEY(s, f)  {s, (int (*)(void)) f}
1698 static SLKeymap_Function_Type Group_Functions [] = /*{{{*/
1699 {
1700    A_KEY("digit_arg", slrn_digit_arg),
1701    A_KEY("move_group", move_group_cmd),
1702    A_KEY("uncatch_up", uncatch_up),
1703    A_KEY("toggle_scoring", toggle_scoring),
1704    A_KEY("toggle_group_display", toggle_group_display),
1705    A_KEY("refresh_groups", refresh_groups_cmd),
1706    A_KEY("save_newsrc", save_newsrc_cmd),
1707    A_KEY("group_search", group_search_cmd),
1708    A_KEY("group_search_forward", group_search_cmd),
1709    A_KEY("toggle_list_all", toggle_list_all_groups),
1710    A_KEY("add_group", add_group_cmd),
1711    A_KEY("group_bob", group_bob),
1712    A_KEY("bob", group_bob),
1713    A_KEY("catchup", catch_up),
1714    A_KEY("down", group_down),
1715    A_KEY("eob", group_eob),
1716    A_KEY("group_eob", group_eob),
1717    A_KEY("help", slrn_group_help),
1718    A_KEY("pagedown", group_pagedown),
1719    A_KEY("pageup", group_pageup),
1720    A_KEY("post", slrn_post_cmd),
1721    A_KEY("quit", slrn_group_quit),
1722    A_KEY("redraw", slrn_redraw),
1723    A_KEY("select_group", select_group_cmd),
1724    A_KEY("subscribe", subscribe),
1725    A_KEY("suspend", slrn_suspend_cmd),
1726    A_KEY("toggle_hidden", toggle_hide_groups),
1727    A_KEY("unsubscribe", unsubscribe),
1728    A_KEY("up", group_up),
1729    A_KEY("repeat_last_key", slrn_repeat_last_key),
1730    A_KEY("transpose_groups", transpose_groups),
1731    A_KEY("post_postponed", slrn_post_postponed),
1732    A_KEY(NULL, NULL)
1733 };
1734 
1735 /*}}}*/
1736 
1737 /*{{{ Mouse Functions*/
1738 
1739 
1740 /* actions for different regions:
1741  *	- top status line (help)
1742  *	- normal region
1743  *	- bottom status line
1744  */
group_mouse(void (* top_status)(void),void (* bot_status)(void),void (* normal_region)(void))1745 static void group_mouse (void (*top_status)(void),
1746 			 void (*bot_status)(void),
1747 			 void (*normal_region)(void)
1748 			 )
1749 {
1750    int r,c;
1751 
1752    slrn_get_mouse_rc (&r, &c);
1753 
1754    /* take top status line into account */
1755    if (r == 1)
1756      {
1757 	if (Slrn_Use_Mouse)
1758 	  slrn_execute_menu (c);
1759 	else
1760 	  if (NULL != top_status) (*top_status) ();
1761  	return;
1762      }
1763 
1764    if (r >= SLtt_Screen_Rows)
1765      return;
1766 
1767    /* bottom status line */
1768    if (r == SLtt_Screen_Rows - 1)
1769      {
1770 	if (NULL != bot_status) (*bot_status) ();
1771 	return;
1772      }
1773 
1774    r -= (1 + Last_Cursor_Row);
1775    if (r < 0)
1776      {
1777 	r = -r;
1778 	if (r != (int) slrn_group_up_n (r)) return;
1779      }
1780    else if (r != (int) slrn_group_down_n (r)) return;
1781 
1782    if (NULL != normal_region) (*normal_region) ();
1783 }
1784 
group_mouse_left(void)1785 static void group_mouse_left (void)
1786 {
1787    group_mouse (slrn_group_help, group_pagedown, select_group_cmd);
1788 }
1789 
group_mouse_middle(void)1790 static void group_mouse_middle (void)
1791 {
1792    group_mouse (toggle_group_display, toggle_hide_groups, select_group_cmd);
1793 #if 1
1794    /* Make up for buggy rxvt which have problems with the middle key. */
1795    if (NULL != getenv ("COLORTERM"))
1796      {
1797 	if (SLang_input_pending (7))
1798 	  {
1799 	     while (SLang_input_pending (0)) SLang_getkey ();
1800 	  }
1801      }
1802 #endif
1803 }
1804 
group_mouse_right(void)1805 static void group_mouse_right (void)
1806 {
1807    group_mouse (slrn_group_help, group_pageup, select_group_cmd);
1808 }
1809 
1810 
1811 /*}}}*/
1812 
1813 /*}}}*/
1814 
1815 #define USE_TEST_FUNCTION 0
1816 #if USE_TEST_FUNCTION
test_function(void)1817 static void test_function (void)
1818 {
1819    char *file;
1820    file = slrn_browse_dir (".");
1821    if (file != NULL)
1822      {
1823 	slrn_message (file);
1824 	slrn_free (file);
1825      }
1826 }
1827 #endif
1828 
slrn_init_group_mode(void)1829 void slrn_init_group_mode (void) /*{{{*/
1830 {
1831    char  *err = "Unable to create group keymap!";
1832 
1833    if (NULL == (Slrn_Group_Keymap = SLang_create_keymap ("group", NULL)))
1834      slrn_exit_error (err);
1835 
1836    Group_Mode_Cap.keymap = Slrn_Group_Keymap;
1837 
1838    Slrn_Group_Keymap->functions = Group_Functions;
1839 
1840    SLkm_define_key ("\0331", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1841    SLkm_define_key ("\0332", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1842    SLkm_define_key ("\0333", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1843    SLkm_define_key ("\0334", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1844    SLkm_define_key ("\0335", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1845    SLkm_define_key ("\0336", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1846    SLkm_define_key ("\0337", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1847    SLkm_define_key ("\0338", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1848    SLkm_define_key ("\0339", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1849    SLkm_define_key ("\0330", (FVOID_STAR) slrn_digit_arg, Slrn_Group_Keymap);
1850    SLkm_define_key  ("^K\033[A", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
1851    SLkm_define_key  ("^K\033OA", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
1852    SLkm_define_key  ("^K\033[B", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
1853    SLkm_define_key  ("^K\033OB", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
1854    SLkm_define_key  ("\033A", (FVOID_STAR) toggle_group_display, Slrn_Group_Keymap);
1855    SLkm_define_key  ("\033>", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
1856    SLkm_define_key  ("\033<", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
1857    SLkm_define_key  ("^D", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
1858    SLkm_define_key  ("^V", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
1859 #if defined(IBMPC_SYSTEM)
1860    SLkm_define_key  ("^@Q", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
1861    SLkm_define_key  ("\xE0Q", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
1862    SLkm_define_key  ("^@I", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
1863    SLkm_define_key  ("\xE0I", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
1864 #else
1865    SLkm_define_key  ("\033[6~", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
1866    SLkm_define_key  ("\033[5~", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
1867 #endif
1868    SLkm_define_key  ("m", (FVOID_STAR) move_group_cmd, Slrn_Group_Keymap);
1869    SLkm_define_key  ("^U", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
1870    SLkm_define_key  ("\033V", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
1871    SLkm_define_key  ("a", (FVOID_STAR) add_group_cmd, Slrn_Group_Keymap);
1872    SLkm_define_key  ("u", (FVOID_STAR) unsubscribe, Slrn_Group_Keymap);
1873    SLkm_define_key  ("s", (FVOID_STAR) subscribe, Slrn_Group_Keymap);
1874    SLkm_define_key  ("\033u", (FVOID_STAR) uncatch_up, Slrn_Group_Keymap);
1875    SLkm_define_key  ("c", (FVOID_STAR) catch_up, Slrn_Group_Keymap);
1876    SLkm_define_key  ("K", (FVOID_STAR) toggle_scoring, Slrn_Group_Keymap);
1877    SLkm_define_key  ("L", (FVOID_STAR) toggle_list_all_groups, Slrn_Group_Keymap);
1878    SLkm_define_key  ("l", (FVOID_STAR) toggle_hide_groups, Slrn_Group_Keymap);
1879    SLkm_define_key  ("^Z", (FVOID_STAR) slrn_suspend_cmd, Slrn_Group_Keymap);
1880    SLkm_define_key  (" ", (FVOID_STAR) select_group_cmd, Slrn_Group_Keymap);
1881    SLkm_define_key  (".", (FVOID_STAR) slrn_repeat_last_key, Slrn_Group_Keymap);
1882    SLkm_define_key  ("P", (FVOID_STAR) slrn_post_cmd, Slrn_Group_Keymap);
1883    SLkm_define_key  ("\033P", (FVOID_STAR) slrn_post_postponed, Slrn_Group_Keymap);
1884    SLkm_define_key  ("?", (FVOID_STAR) slrn_group_help, Slrn_Group_Keymap);
1885    SLkm_define_key  ("\r", (FVOID_STAR) select_group_cmd, Slrn_Group_Keymap);
1886    SLkm_define_key  ("q", (FVOID_STAR) slrn_group_quit, Slrn_Group_Keymap);
1887    SLkm_define_key  ("^X^C", (FVOID_STAR) slrn_group_quit, Slrn_Group_Keymap);
1888    SLkm_define_key  ("^X^T", (FVOID_STAR) transpose_groups, Slrn_Group_Keymap);
1889    SLkm_define_key  ("^R", (FVOID_STAR) slrn_redraw, Slrn_Group_Keymap);
1890    SLkm_define_key  ("^L", (FVOID_STAR) slrn_redraw, Slrn_Group_Keymap);
1891    SLkm_define_key  ("^P", (FVOID_STAR) group_up, Slrn_Group_Keymap);
1892 #if defined(IBMPC_SYSTEM)
1893    SLkm_define_key  ("^@H", (FVOID_STAR) group_up, Slrn_Group_Keymap);
1894    SLkm_define_key  ("\xE0H", (FVOID_STAR) group_up, Slrn_Group_Keymap);
1895    SLkm_define_key  ("^@P", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1896    SLkm_define_key  ("\xE0P", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1897 #else
1898    SLkm_define_key  ("\033[A", (FVOID_STAR) group_up, Slrn_Group_Keymap);
1899    SLkm_define_key  ("\033OA", (FVOID_STAR) group_up, Slrn_Group_Keymap);
1900    SLkm_define_key  ("\033[B", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1901    SLkm_define_key  ("\033OB", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1902 #endif
1903    SLkm_define_key  ("N", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1904    SLkm_define_key  ("^N", (FVOID_STAR) group_down, Slrn_Group_Keymap);
1905    SLkm_define_key  ("/", (FVOID_STAR) group_search_cmd, Slrn_Group_Keymap);
1906    SLkm_define_key  ("G", (FVOID_STAR) refresh_groups_cmd, Slrn_Group_Keymap);
1907    SLkm_define_key  ("X", (FVOID_STAR) save_newsrc_cmd, Slrn_Group_Keymap);
1908 
1909    /* mouse (left/right/middle) */
1910    SLkm_define_key  ("\033[M\040", (FVOID_STAR) group_mouse_left, Slrn_Group_Keymap);
1911    SLkm_define_key  ("\033[M\041", (FVOID_STAR) group_mouse_middle, Slrn_Group_Keymap);
1912    SLkm_define_key  ("\033[M\042", (FVOID_STAR) group_mouse_right, Slrn_Group_Keymap);
1913 #if USE_TEST_FUNCTION
1914    SLkm_define_key  ("y", (FVOID_STAR) test_function, Slrn_Group_Keymap);
1915 #endif
1916    if (SLang_Error) slrn_exit_error (err);
1917 }
1918 
1919 /*}}}*/
1920 
1921 /*}}}*/
1922 
slrn_select_group_mode(void)1923 int slrn_select_group_mode (void) /*{{{*/
1924 {
1925    init_group_win_struct ();
1926 
1927    Last_Cursor_Row = 0;
1928    group_quick_help ();
1929    Slrn_Full_Screen_Update = 1;
1930 
1931    slrn_push_mode (&Group_Mode_Cap);
1932    return 0;
1933 }
1934 
1935 /*}}}*/
1936 
1937 /*{{{ Read/Write Newsrc, Group Descriptions */
1938 
add_group_description(unsigned char * s,unsigned char * smax,unsigned char * dsc)1939 static void add_group_description (unsigned char *s, unsigned char *smax, /*{{{*/
1940 				   unsigned char *dsc)
1941 {
1942    Slrn_Group_Type *g;
1943    unsigned long hash;
1944 
1945    hash = slrn_compute_hash (s, smax);
1946    g = Group_Hash_Table[hash % GROUP_HASH_TABLE_SIZE];
1947    while (g != NULL)
1948      {
1949 	if ((g->hash == hash) && (!strncmp (g->name, (char *) s, (unsigned int) (smax - s))))
1950 	  {
1951 	     /* Sometimes these get repeated --- not by slrn but on the server! */
1952 	     slrn_free (g->descript);
1953 
1954 	     g->descript = slrn_strmalloc ((char *) dsc, 0);
1955 	     /* Ok to fail. */
1956 	     return;
1957 	  }
1958 	g = g->hash_next;
1959      }
1960 }
1961 
1962 /*}}}*/
slrn_get_group_descriptions(void)1963 void slrn_get_group_descriptions (void) /*{{{*/
1964 {
1965    FILE *fp;
1966    char line[2 * SLRN_MAX_PATH_LEN];
1967    char file[SLRN_MAX_PATH_LEN];
1968    int num;
1969 
1970 #ifdef VMS
1971    sprintf (file, "%s-dsc", Slrn_Newsrc_File);
1972 #else
1973 # if defined(IBMPC_SYSTEM)
1974    sprintf (file, "ds-%s", Slrn_Newsrc_File);
1975 # else
1976    sprintf (file, "%s.dsc", Slrn_Newsrc_File);
1977 # endif
1978 #endif
1979 
1980 
1981    if (NULL == (fp = slrn_open_home_file (file, "w", line, 0)))
1982      {
1983 	slrn_exit_error ("\
1984 Unable to create newsgroup description file:\n%s\n", line);
1985      }
1986 
1987    fprintf (stdout, "\nCreating description file %s.\n", line);
1988    fprintf (stdout, "Getting newsgroup descriptions from server.\n\
1989 Note: This step may take some time if you have a slow connection!!!\n");
1990 
1991    fflush (stdout);
1992 
1993    if (OK_GROUPS != Slrn_Server_Obj->sv_list_newsgroups ())
1994      {
1995 	slrn_error ("Server failed on list newsgroups command.");
1996 	slrn_fclose (fp);
1997 	return;
1998      }
1999 
2000    num = 0;
2001 
2002    while (1)
2003      {
2004 	unsigned char *b, *bmax, *dsc, ch;
2005 	int status;
2006 
2007 	status = Slrn_Server_Obj->sv_read_line (line, sizeof (line));
2008 	if (status == -1)
2009 	  {
2010 	     slrn_error ("Error reading line from server");
2011 	     slrn_fclose (fp);
2012 	     return;
2013 	  }
2014 	if (status == 0)
2015 	  break;
2016 
2017 	num = num % 25;
2018 	if (num == 0)
2019 	  {
2020 	     putc ('.', stdout);
2021 	     fflush (stdout);
2022 	  }
2023 
2024 	/* Check the syntax on this line. They are often corrupt */
2025 	b = (unsigned char *) slrn_skip_whitespace (line);
2026 
2027 	bmax = b;
2028 	while ((ch = *bmax) > ' ') bmax++;
2029 	if ((ch == 0) || (ch == '\n')) continue;
2030 	*bmax = 0;
2031 
2032 	/* News group marked off, now get the description. */
2033 	dsc = bmax + 1;
2034 	while (((ch = *dsc) <= ' ') && (ch != 0)) dsc++;
2035 	if ((ch == 0) || (ch == '?') || (ch == '\n')) continue;
2036 
2037 	/* add_group_description (b, bmax, dsc); */
2038 
2039 	fputs ((char *) b, fp);
2040 	putc(':', fp);
2041 	fputs ((char *) dsc, fp);
2042 	putc('\n', fp);
2043 
2044 	num++;
2045      }
2046    slrn_fclose (fp);
2047    putc ('\n', stdout);
2048 }
2049 
2050 /*}}}*/
slrn_read_group_descriptions(void)2051 int slrn_read_group_descriptions (void) /*{{{*/
2052 {
2053    FILE *fp;
2054    char line[2 * SLRN_MAX_PATH_LEN];
2055    char file[SLRN_MAX_PATH_LEN];
2056 
2057 #ifdef VMS
2058    sprintf (file, "%s-dsc", Slrn_Newsrc_File);
2059 #else
2060 # if defined(IBMPC_SYSTEM)
2061    sprintf (file, "ds-%s", Slrn_Newsrc_File);
2062 # else
2063    sprintf (file, "%s.dsc", Slrn_Newsrc_File);
2064 # endif
2065 #endif
2066 
2067 
2068    if (NULL == (fp = slrn_open_home_file (file, "r", line, 0)))
2069      {
2070 	if (Slrn_Lib_Dir != NULL)
2071 	  {
2072 #ifdef VMS
2073 	     sprintf (file, "%snewsgroups.dsc", Slrn_Lib_Dir);
2074 	     if (NULL == (fp = slrn_open_home_file (file, "r", line, 0)))
2075 	       {
2076 		  sprintf (file, "%snewsgroups-dsc", Slrn_Lib_Dir);
2077 		  fp = slrn_open_home_file (file, "r", line, 0);
2078 	       }
2079 #else
2080 	     sprintf (file, "%s/newsgroups.dsc", Slrn_Lib_Dir);
2081 	     fp = slrn_open_home_file (file, "r", line, 0);
2082 #endif
2083 	  }
2084 	if (fp == NULL) return -1;
2085      }
2086 
2087    while (NULL != fgets (line, sizeof (line) - 1, fp))
2088      {
2089 	unsigned char *bmax, *dsc, ch;
2090 
2091 	bmax = (unsigned char *) line;
2092 	while (((ch = *bmax) != ':') && ch) bmax++;
2093 	if (ch <= ' ') continue;
2094 	*bmax = 0;
2095 
2096 	dsc = bmax + 1;
2097 	add_group_description ((unsigned char *) line, bmax, dsc);
2098      }
2099    slrn_fclose (fp);
2100    return 0;
2101 }
2102 
2103 /*}}}*/
2104 
2105 /* Map 1998 --> 98, 2003 --> 3, etc.... */
rfc977_patchup_year(int year)2106 static int rfc977_patchup_year (int year)
2107 {
2108    return year - 100 * (year / 100);
2109 }
2110 
2111 
slrn_check_new_groups(int create_flag)2112 int slrn_check_new_groups (int create_flag) /*{{{*/
2113 {
2114    FILE *fp;
2115    time_t tloc;
2116    struct tm *tm_struct;
2117    char line[SLRN_MAX_PATH_LEN];
2118    char file[SLRN_MAX_PATH_LEN];
2119    int num;
2120    char *p;
2121    int parse_error = 0;
2122 
2123 #ifdef VMS
2124    sprintf (file, "%s-time", Slrn_Newsrc_File);
2125 #else
2126 # ifdef SLRN_USE_OS2_FAT
2127    slrn_os2_make_fat (file, Slrn_Newsrc_File, ".tim");
2128 # else
2129    sprintf (file, "%s.time", Slrn_Newsrc_File);
2130 # endif
2131 #endif
2132 
2133 
2134    if ((create_flag == 0)
2135        && (NULL != (fp = slrn_open_home_file (file, "r", line, 0))))
2136      {
2137 	char ch;
2138 	int i;
2139 	*line = 0;
2140 	fgets (line, sizeof (line), fp);
2141 	slrn_fclose (fp);
2142 
2143 	time (&tloc);
2144 	parse_error = 1;
2145 
2146 	/* parse this line to make sure it is ok.  If it is bad, issue a warning
2147 	 * and go on.
2148 	 */
2149 	if (strncmp ("NEWGROUPS ", line, 10)) goto parse_error_label;
2150 	p = line + 10;
2151 
2152 	p = slrn_skip_whitespace (p);
2153 
2154 	/* parse yymmdd */
2155 	for (i = 0; i < 6; i++)
2156 	  {
2157 	     ch = p[i];
2158 	     if ((ch < '0') || (ch > '9')) goto parse_error_label;
2159 	  }
2160 	if (p[6] != ' ') goto parse_error_label;
2161 
2162 	ch = p[2];
2163 	if (ch > '1') goto parse_error_label;
2164 	if ((ch == '1') && (p[3] > '2')) goto parse_error_label;
2165 	ch = p[4];
2166 	if (ch > '3') goto parse_error_label;
2167 	if ((ch == '3') && (p[5] > '1')) goto parse_error_label;
2168 
2169 	/* Now the hour: hhmmss */
2170 	p = slrn_skip_whitespace (p + 6);
2171 
2172 	for (i = 0; i < 6; i++)
2173 	  {
2174 	     ch = p[i];
2175 	     if ((ch < '0') || (ch > '9')) goto parse_error_label;
2176 	  }
2177 	ch = p[0];
2178 	if (ch > '2') goto parse_error_label;
2179 	if ((ch == '2') && (p[1] > '3')) goto parse_error_label;
2180 	if ((p[2] > '5') || (p[4] > '5')) goto parse_error_label;
2181 
2182 	p = slrn_skip_whitespace (p + 6);
2183 
2184 	if ((p[0] == 'G') && (p[1] == 'M') && (p[2] == 'T'))
2185 	  p += 3;
2186 	*p = 0;
2187 
2188 	parse_error = 0;
2189 
2190 	switch (Slrn_Server_Obj->sv_put_server_cmd (line, line, sizeof (line)))
2191 	  {
2192 	   case OK_NEWGROUPS:
2193 	     break;
2194 
2195 	   case ERR_FAULT:
2196 	     return -1;
2197 
2198 	   case ERR_COMMAND:
2199 	     slrn_message ("Server does not implement NEWGROUPS command.");
2200 	     return 0;
2201 
2202 	   default:
2203 	     slrn_message ("Server failed to return proper response to NEWGROUPS:\n%s\n",
2204 			   line);
2205 	     goto parse_error_label;
2206 	  }
2207 
2208 	num = 0;
2209 
2210 	while (1)
2211 	  {
2212 	     int status = Slrn_Server_Obj->sv_read_line (line, sizeof (line));
2213 	     if (status == 0)
2214 	       break;
2215 	     if (status == -1)
2216 	       {
2217 		  slrn_error ("Read from server failed");
2218 		  return -1;
2219 	       }
2220 
2221 	     /* line contains new newsgroup name */
2222 	     add_unsubscribed_group (line);
2223 	     num++;
2224 	  }
2225 
2226 	if (num)
2227 	  {
2228 #ifdef KANJI
2229 	     slrn_message (Slrn_Japanese_Messages
2230 			   ? "�������˥塼�����롼�פ� %d ����ޤ�����"
2231 			   : "%d new newsgroup(s) found.", num);
2232 #else
2233 	     slrn_message ("%d new newsgroup(s) found.", num);
2234 #endif
2235 	  }
2236      }
2237    else time (&tloc);
2238 
2239    parse_error_label:
2240    if (parse_error)
2241      {
2242 	slrn_message ("\
2243 %s appears corrupt.\n\
2244 I expected to see see: NEWGROUPS yymmdd hhmmss GMT\n\
2245 I will patch the file up for you.\n", file);
2246      }
2247 
2248 
2249    if (NULL == (fp = slrn_open_home_file (file, "w", line, 1)))
2250      {
2251 	slrn_exit_error ("Unable to open %s to record date.", line);
2252      }
2253 
2254    /* According to rfc977, the year must be in the form YY with the closest
2255     * century specifying the rest of the year, i.e., 99 is 1999, 30 is 2030,
2256     * etc.
2257     */
2258 #if defined(VMS) || defined(__BEOS__)
2259    /* gmtime is broken on BEOS */
2260    tm_struct = localtime (&tloc);
2261    tm_struct->tm_year = rfc977_patchup_year (tm_struct->tm_year + 1900);
2262    fprintf (fp, "NEWGROUPS %02d%02d%02d %02d%02d%02d",
2263             tm_struct->tm_year, 1 + tm_struct->tm_mon,
2264             tm_struct->tm_mday, tm_struct->tm_hour,
2265             tm_struct->tm_min, tm_struct->tm_sec);
2266 #else
2267    tm_struct = gmtime (&tloc);
2268    tm_struct->tm_year = rfc977_patchup_year (tm_struct->tm_year + 1900);
2269    fprintf (fp, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT",
2270 	    tm_struct->tm_year, 1 + tm_struct->tm_mon,
2271 	    tm_struct->tm_mday, tm_struct->tm_hour,
2272 	    tm_struct->tm_min, tm_struct->tm_sec);
2273 #endif
2274    slrn_fclose (fp);
2275    return 0;
2276 }
2277 
2278 /*}}}*/
2279 
2280 /* Rearrange Slrn_Group_Current_Group such that it follows last_group and
2281  * return Slrn_Group_Current_Group.  If last_group is NULL, Slrn_Group_Current_Group
2282  * should be put at top of list.
2283  */
place_group_in_newsrc_order(Slrn_Group_Type * last_group)2284 static Slrn_Group_Type *place_group_in_newsrc_order (Slrn_Group_Type *last_group) /*{{{*/
2285 {
2286    Slrn_Group_Type *next_group, *prev_group;
2287 
2288    next_group = Slrn_Group_Current_Group->next;
2289    prev_group = Slrn_Group_Current_Group->prev;
2290    if (next_group != NULL) next_group->prev = prev_group;
2291    if (prev_group != NULL) prev_group->next = next_group;
2292 
2293    Slrn_Group_Current_Group->prev = last_group;
2294    if (last_group != NULL)
2295      {
2296 	Slrn_Group_Current_Group->next = last_group->next;
2297 
2298 	if (Slrn_Group_Current_Group->next != NULL)
2299 	  Slrn_Group_Current_Group->next->prev = Slrn_Group_Current_Group;
2300 
2301 	last_group->next = Slrn_Group_Current_Group;
2302      }
2303    else if (Slrn_Group_Current_Group != Groups)
2304      {
2305 	Slrn_Group_Current_Group->next = Groups;
2306 	if (Groups != NULL) Groups->prev = Slrn_Group_Current_Group;
2307 	Groups = Slrn_Group_Current_Group;
2308      }
2309    else
2310      {
2311 	/* correct next_group->prev since it was not set correctly above */
2312 	if (next_group != NULL)
2313 	  next_group->prev = Slrn_Group_Current_Group;
2314      }
2315 
2316    return Slrn_Group_Current_Group;
2317 }
2318 
2319 /*}}}*/
2320 
parse_active_line(char * name,unsigned int * lenp,int * minp,int * maxp)2321 static int parse_active_line (char *name, unsigned int *lenp, /*{{{*/
2322 			      int *minp, int *maxp)
2323 {
2324    char *p;
2325 
2326    p = name;
2327    while (*p > ' ') p++;
2328    *lenp = (unsigned int) (p - name);
2329 
2330    while (*p == ' ') p++;
2331    *maxp = atoi (p);
2332    while (*p > ' ') p++;  while (*p == ' ') p++;
2333    *minp = atoi(p);
2334    if (*maxp < *minp) *minp = *maxp + 1;
2335    return 0;
2336 }
2337 
2338 /*}}}*/
2339 
slrn_read_newsrc(int create_flag)2340 int slrn_read_newsrc (int create_flag) /*{{{*/
2341 {
2342    char file[SLRN_MAX_PATH_LEN];
2343    FILE *fp = NULL;
2344    char line[NNTP_BUFFER_SIZE];
2345    register char *p, ch;
2346 
2347    if (create_flag)
2348      {
2349 	if (NULL == (fp = slrn_open_home_file (Slrn_Newsrc_File, "w", file, 1)))
2350 	  {
2351 	     slrn_exit_error ("Unable to create %s.", file);
2352 	  }
2353 
2354 #ifdef KANJI
2355 	if (Slrn_Japanese_Messages)
2356 	   slrn_message_now ("\n--���Υ��ƥåפ� NNTP ��³����®�ξ�礷�Ф餯������ޤ���--\n\n");
2357 	else
2358 #endif
2359 	fputs ("\n--The next step may take a while if the NNTP connection is slow.--\n\n", stdout);
2360 	fprintf (stdout, "Creating %s.", file);
2361 	fflush (stdout);
2362      }
2363 
2364    if (create_flag || Slrn_List_Active_File)
2365      {
2366 	int count = 0;
2367 
2368 	if (OK_GROUPS != Slrn_Server_Obj->sv_list_active ())
2369 	  slrn_exit_error ("Server failed LIST ACTIVE.");
2370 
2371 	while (1)
2372 	  {
2373 	     unsigned int len;
2374 	     int min, max;
2375 	     int status;
2376 
2377 	     status = Slrn_Server_Obj->sv_read_line (line, sizeof(line));
2378 	     if (status == -1)
2379 	       {
2380 		  Slrn_Groups_Dirty = 0;
2381 		  slrn_exit_error ("Read from server failed");
2382 	       }
2383 	     if (status == 0)
2384 	       break;
2385 
2386 	     parse_active_line (line, &len, &min, &max);
2387 
2388 	     if (NULL == create_group_entry (line, len, min, max, 0, 0))
2389 	       continue;
2390 
2391 
2392 	     if (create_flag)
2393 	       {
2394 		  count++;
2395 		  count = count % 50;
2396 		  if (count == 0)
2397 		    {
2398 		       putc ('.', stdout);
2399 		       fflush (stdout);
2400 		    }
2401 
2402 		  if ((*line == 'n')
2403 		      && (!strncmp (line,    "news.newusers.questions", 23)
2404 			  || !strncmp (line, "news.groups.questions", 21)
2405 			  || !strncmp (line, "news.answers", 12)
2406 			  || !strncmp (line, "news.announce.newusers", 22)
2407 			  || !strncmp (line, "news.software.readers", 21)))
2408 		    {
2409 		       add_group (line, len, 0, 1);
2410 		    }
2411 #ifdef KANJI
2412 		  if (((*line == 'f')
2413 		      && (!strncmp (line,    "fj.questions", 12)
2414 			  || !strncmp (line, "fj.news.reader", 14))) ||
2415 		      (*line == 'j' && !strncmp(line, "japan.netnews.reader", 20)))
2416 		    {
2417 		       add_group (line, len, 0, 1);
2418 		    }
2419 #endif
2420 		  else if ((*line == 'a')
2421 			   && (!strncmp (line, "alt.test", 8)))
2422 		    {
2423 		       add_group (line, len, 0, 1);
2424 		    }
2425 		  else add_group (line, len, GROUP_UNSUBSCRIBED, 1);
2426 	       }
2427 	  }
2428      }
2429 
2430    if (create_flag == 0)
2431      {
2432 	Slrn_Group_Type *last_group = NULL;
2433 
2434 	if (Unsubscribed_Groups != NULL)
2435 	  {
2436 	     unsigned int subscribe_flag;
2437 	     Unsubscribed_Slrn_Group_Type *ug = Unsubscribed_Groups, *ugnext;
2438 
2439 	     if (Slrn_Unsubscribe_New_Groups)
2440 	       subscribe_flag = GROUP_UNSUBSCRIBED | GROUP_NEW_GROUP_FLAG;
2441 	     else subscribe_flag = GROUP_NEW_GROUP_FLAG;
2442 
2443 	     while (ug != NULL)
2444 	       {
2445 		  ugnext = ug->next;
2446 
2447 		  if ((-1 != add_group (ug->name, strlen (ug->name), subscribe_flag, 0))
2448 		      && Slrn_List_Active_File)
2449 		    last_group = place_group_in_newsrc_order (last_group);
2450 
2451 		  SLFREE (ug);
2452 		  ug = ugnext;
2453 	       }
2454 	     Unsubscribed_Groups = NULL;
2455 	  }
2456 
2457 	if ((NULL == (fp = slrn_open_home_file (Slrn_Newsrc_File, "r", file, 0)))
2458 	    && (NULL == (fp = slrn_open_home_file (".newsrc", "r", file, 0))))
2459 	  {
2460 	     sprintf (line, "Unable to open %s.", file);
2461 	     slrn_exit_error (line);
2462 	  }
2463 
2464 	while (fgets (line, sizeof(line) - 1, fp) != NULL)
2465 	  {
2466 	     p = line;
2467 	     while (((ch = *p) != '!') && (ch != ':') && (ch != 0))
2468 	       {
2469 		  p++;
2470 	       }
2471 	     if ((ch == 0) || (p == line)) continue;
2472 	     if (-1 == add_group (line, (unsigned int) (p - line),
2473 				  ((ch == '!') ? GROUP_UNSUBSCRIBED : 0),
2474 				  0))
2475 	       continue;
2476 
2477 	     /* perform a re-arrangement to match arrangement in the
2478 	      * newsrc file
2479 	      */
2480 	     if (Slrn_List_Active_File && (ch !=  '!'))
2481 	       {
2482 		  last_group = place_group_in_newsrc_order (last_group);
2483 	       }
2484 	  }
2485      }
2486 
2487    slrn_fclose (fp);
2488 
2489    Slrn_Group_Current_Group = Groups;
2490 
2491    init_group_win_struct ();
2492 
2493    toggle_hide_groups ();
2494 
2495    /* Unhide the new groups.  Do it here so that if there are no unread
2496     * articles, it will be visible but also enables user to toggle them
2497     * so that they will become invisble again.
2498     */
2499    Slrn_Group_Current_Group = Groups;
2500    while ((Slrn_Group_Current_Group != NULL)
2501 	  && (Slrn_Group_Current_Group->flags & GROUP_NEW_GROUP_FLAG))
2502      {
2503 	Slrn_Group_Current_Group->flags &= ~GROUP_HIDDEN;
2504 	Slrn_Group_Current_Group = Slrn_Group_Current_Group->next;
2505      }
2506 
2507    Slrn_Group_Current_Group = Groups;
2508    while ((Slrn_Group_Current_Group != NULL)
2509 	  && (Slrn_Group_Current_Group->flags & GROUP_HIDDEN))
2510      Slrn_Group_Current_Group = Slrn_Group_Current_Group->next;
2511 
2512    find_line_num ();
2513 
2514    if (create_flag)
2515      {
2516 	if (Group_Window.num_lines == 0)
2517 	  {
2518 	     Slrn_Group_Current_Group = Groups;
2519 	     while (Slrn_Group_Current_Group != NULL)
2520 	       {
2521 		  Slrn_Group_Current_Group->flags &= ~GROUP_HIDDEN;
2522 		  Slrn_Group_Current_Group = Slrn_Group_Current_Group->next;
2523 	       }
2524 	     Slrn_Group_Current_Group = Groups;
2525 	     find_line_num ();
2526 	  }
2527 	Slrn_Groups_Dirty = 1;
2528 	Slrn_Write_Newsrc_Flags = 0;
2529      }
2530 
2531    group_bob ();
2532    return 0;
2533 }
2534 
2535 /*}}}*/
2536 
slrn_write_newsrc(void)2537 int slrn_write_newsrc (void) /*{{{*/
2538 {
2539    Slrn_Group_Type *g;
2540    Slrn_Range_Type *r;
2541    char file[SLRN_MAX_PATH_LEN], backup_file[SLRN_MAX_PATH_LEN];
2542    static FILE *fp;
2543    int pass;
2544    int max;
2545    struct stat filestat;
2546    int stat_worked;
2547    int have_backup;
2548 
2549    slrn_init_hangup_signals (0);
2550 
2551    if (Slrn_Groups_Dirty == 0)
2552      {
2553 	slrn_init_hangup_signals (1);
2554 	return 0;
2555      }
2556 
2557    /* In case of hangup and we were writing the file, make sure it is closed.
2558     * This will not hurt since we are going to do it again anyway.
2559     */
2560    if (fp != NULL) slrn_fclose (fp); fp = NULL;
2561 
2562 #ifdef KANJI
2563    slrn_message_now (Slrn_Japanese_Messages
2564 		     ? "%s ��������� ..."
2565 		     : "Writing %s ...", Slrn_Newsrc_File);
2566 #else
2567    slrn_message_now ("Writing %s ...", Slrn_Newsrc_File);
2568 #endif
2569 
2570    slrn_make_home_filename (Slrn_Newsrc_File, file);
2571 
2572    /* Try to preserve .newsrc permissions and owner/group.  This also
2573     * confirms existence of file.
2574     */
2575    stat_worked = (-1 != stat (file, &filestat));
2576 
2577 #ifdef VMS
2578    sprintf (backup_file, "%s-bak", file);
2579 #else
2580 # ifdef SLRN_USE_OS2_FAT
2581    slrn_os2_make_fat (backup_file, file, ".bak");
2582 # else
2583    sprintf (backup_file, "%s~", file);
2584 # endif
2585 #endif
2586 
2587    /* Create a temp backup file.  Delete it later if user
2588     * does not want backups.
2589     */
2590    (void) slrn_delete_file (backup_file);
2591 
2592    have_backup = 1;
2593    if (-1 == rename (file, backup_file))
2594      {
2595 	have_backup = 0;
2596      }
2597 
2598 
2599    if (NULL == (fp = fopen (file, "w")))
2600      {
2601 	slrn_error ("Unable to open file %s for writing.", file);
2602 	if (have_backup) (void) rename (backup_file, file);
2603 	slrn_init_hangup_signals (1);
2604 	return -1;
2605      }
2606 
2607 #ifdef __unix__
2608 # if !defined(IBMPC_SYSTEM)
2609    /* Try to preserve .newsrc permissions and owner/group */
2610 #  ifndef S_IRUSR
2611 #   define S_IRUSR 0400
2612 #   define S_IWUSR 0200
2613 #   define S_IXUSR 0100
2614 #  endif
2615    if (stat_worked)
2616      {
2617  	if (-1 == chmod (file, filestat.st_mode & (S_IRUSR | S_IWUSR | S_IXUSR)))
2618 	  (void) chmod (file, S_IWUSR | S_IRUSR);
2619 
2620  	(void) chown (file, filestat.st_uid, filestat.st_gid);
2621      }
2622 # endif
2623 #endif
2624 
2625 
2626    /* We are going to do this in 2 passes.  The first pass writes out just
2627     * the subscribed groups.  The second pass takes care of the rest.
2628     */
2629    for (pass = 0; pass < 2; pass++)
2630      {
2631 	g = Groups;
2632 	while (g != NULL)
2633 	  {
2634 	     if (pass == 0)
2635 	       {
2636 		  if (g->flags & GROUP_UNSUBSCRIBED)
2637 		    {
2638 		       g = g->next;
2639 		       continue;
2640 		    }
2641 		  if ((EOF == fputs (g->name, fp))
2642 		      || (EOF == putc (':', fp)))
2643 		    goto write_error;
2644 	       }
2645 	     else if (pass == 1)
2646 	       {
2647 		  if ((g->flags & GROUP_UNSUBSCRIBED) == 0)
2648 		    {
2649 		       g = g->next;
2650 		       continue;
2651 		    }
2652 
2653 		  if (Slrn_Write_Newsrc_Flags)
2654 		    {
2655 		       if ((Slrn_Write_Newsrc_Flags == 1)
2656 			   || ((Slrn_Write_Newsrc_Flags == 2)
2657 			       && (g->range.next == NULL)))
2658 			 {
2659 			    g = g->next;
2660 			    continue;
2661 			 }
2662 		    }
2663 		  if ((EOF == fputs (g->name, fp))
2664 		      || (EOF == putc ('!', fp)))
2665 		    goto write_error;
2666 	       }
2667 
2668 	     r = g->range.next;
2669 	     max = g->range.max;
2670 	     if (r != NULL)
2671 	       {
2672 		  if (EOF == putc (' ', fp))
2673 		    goto write_error;
2674 
2675 		  while (1)
2676 		    {
2677 		       /* Make this check because the unsubscribed group
2678 			* range may not have been initialized from the server.
2679 			*/
2680 		       if ((max != -1) && (g->range.min != -1)
2681 			   && ((g->flags & GROUP_UNSUBSCRIBED) == 0))
2682 			 {
2683 			    if (r->min > max) break;
2684 			    if (r->max > max) r->max = max;
2685 			 }
2686 
2687 		       if (r->min != r->max)
2688 			 {
2689 			    if (fprintf (fp, "%d-%d", r->min, r->max) < 0)
2690 			      goto write_error;
2691 			 }
2692 		       else if (fprintf (fp, "%d", r->min) < 0)
2693 			 goto write_error;
2694 
2695 		       r = r->next;
2696 		       if (r == NULL) break;
2697 		       if (EOF == putc (',', fp))
2698 			 goto write_error;
2699 		    }
2700 	       }
2701 
2702 	     if (EOF == putc ('\n', fp))
2703 	       goto write_error;
2704 	     g = g->next;
2705 	  }
2706      }
2707 
2708    if (-1 == slrn_fclose (fp))
2709      goto write_error;
2710    fp = NULL;
2711 
2712 
2713    if (Slrn_No_Backups)
2714      {
2715 	if (have_backup) slrn_delete_file (backup_file);
2716      }
2717 
2718    Slrn_Groups_Dirty = 0;
2719    if (Slrn_TT_Initialized & SLRN_TTY_INIT)
2720 #ifdef KANJI
2721      slrn_message (Slrn_Japanese_Messages
2722 		   ? "%s ��������� ... ��λ��"
2723 		   : "Writing %s ... done.", Slrn_Newsrc_File);
2724 #else
2725      slrn_message ("Writing %s ... done.", Slrn_Newsrc_File);
2726 #endif
2727 
2728    slrn_init_hangup_signals (1);
2729    return 0;
2730 
2731    write_error:
2732 
2733    slrn_fclose (fp); fp = NULL;
2734    slrn_error ("Write to %s failed! Disk Full?", Slrn_Newsrc_File);
2735 
2736    if (stat_worked)
2737      {
2738 	/* Put back orginal file */
2739 	(void) slrn_delete_file (file);
2740 	if (have_backup) (void) rename (backup_file, file);
2741      }
2742 
2743    slrn_init_hangup_signals (1);
2744    return -1;
2745 }
2746 
2747 /*}}}*/
2748 
2749 /*}}}*/
2750 
group_quick_help(void)2751 static void group_quick_help (void) /*{{{*/
2752 {
2753    char *hlp = "SPC:Select  p:Post  c:CatchUp  l:List  q:Quit  ^R:Redraw  (u)s:(Un)Subscribe";
2754 
2755    if (Slrn_Batch)
2756      return;
2757 
2758    if (Slrn_Group_Help_Line != NULL)
2759      hlp = Slrn_Group_Help_Line;
2760 
2761    if (0 == slrn_message (hlp))
2762      Slrn_Message_Present = 0;
2763 }
2764 
2765 /*}}}*/
group_update_screen(void)2766 static void group_update_screen (void) /*{{{*/
2767 {
2768    Slrn_Group_Type *g;
2769    int height = (int) Group_Window.nrows;
2770    int row;
2771 
2772    /* erase last cursor */
2773    if (Last_Cursor_Row && !Slrn_Full_Screen_Update)
2774      {
2775 	SLsmg_gotorc (Last_Cursor_Row, 0);
2776 	SLsmg_write_string ("  ");
2777      }
2778 
2779    g = (Slrn_Group_Type *) Group_Window.top_window_line;
2780    (void) SLscroll_find_top (&Group_Window);
2781 
2782    if (g != (Slrn_Group_Type *) Group_Window.top_window_line)
2783      {
2784 	Slrn_Full_Screen_Update = 1;
2785 	g = (Slrn_Group_Type *) Group_Window.top_window_line;
2786      }
2787 
2788    for (row = 0; row < height; row++)
2789      {
2790 	while ((g != NULL) && (g->flags & GROUP_HIDDEN))
2791 	  g = g->next;
2792 
2793 	if (g != NULL)
2794 	  {
2795 	     if (Slrn_Full_Screen_Update || (g->flags & GROUP_TOUCHED))
2796 	       {
2797 		  SLsmg_gotorc (row + 1, 0);
2798 		  SLsmg_printf ("  %c%5d  ",
2799 				((g->flags & GROUP_UNSUBSCRIBED) ? 'U'
2800 				 : ((g->flags & GROUP_NEW_GROUP_FLAG) ? 'N' : ' ')),
2801 				g->unread);
2802 		  slrn_set_color (GROUP_COLOR);
2803 		  SLsmg_printf ("%s", g->name);
2804 		  slrn_set_color (0);
2805 		  SLsmg_erase_eol ();
2806 		  if (Slrn_Group_Display_Descriptions)
2807 		    {
2808 		       if (g->descript != NULL)
2809 			 {
2810 			    int dsc_row;
2811 
2812 			    dsc_row = SLsmg_get_column ();
2813 
2814 			    if (dsc_row < Slrn_Group_Description_Column)
2815 			      dsc_row = Slrn_Group_Description_Column;
2816 			    else dsc_row++;
2817 			    SLsmg_gotorc (row + 1, dsc_row);
2818 			    SLsmg_set_color (GROUP_DESCR_COLOR);
2819 			    SLsmg_write_string (g->descript);
2820 			    SLsmg_set_color (0);
2821 			 }
2822 		    }
2823 		  else if (g->range.max != -1)
2824 		    {
2825 		       SLsmg_gotorc (row + 1, 63);
2826 		       SLsmg_printf ("%7d-%-7d", g->range.min, g->range.max);
2827 		    }
2828 		  g->flags &= ~GROUP_TOUCHED;
2829 	       }
2830 	     g = g->next;
2831 	  }
2832 	else if (Slrn_Full_Screen_Update)
2833 	  {
2834 	     SLsmg_gotorc (row + 1, 0);
2835 	     SLsmg_erase_eol ();
2836 	  }
2837      }
2838 
2839    SLsmg_gotorc (SLtt_Screen_Rows - 2, 0);
2840    slrn_set_color (STATUS_COLOR);
2841    if (Slrn_Groups_Dirty)
2842      SLsmg_write_string ("-*-News Groups: ");
2843    else SLsmg_write_string ("---News Groups: ");
2844 
2845    if (Slrn_Server_Obj->sv_name != NULL)
2846      SLsmg_write_string (Slrn_Server_Obj->sv_name);
2847 
2848    slrn_print_percent (SLtt_Screen_Rows - 2, 60, &Group_Window);
2849 
2850    if (Slrn_Use_Mouse) slrn_update_group_menu ();
2851    else slrn_update_top_status_line ();
2852 
2853    if (Slrn_Message_Present == 0) group_quick_help ();
2854 
2855    Last_Cursor_Row = 1 + Group_Window.window_row;
2856    SLsmg_gotorc (Last_Cursor_Row, 0);
2857 
2858    slrn_set_color (CURSOR_COLOR);
2859 
2860 #if SLANG_VERSION > 10003
2861    if (Slrn_Display_Cursor_Bar)
2862      SLsmg_set_color_in_region (CURSOR_COLOR, Last_Cursor_Row, 0, 1, SLtt_Screen_Cols);
2863    else
2864 #endif
2865      SLsmg_write_string ("->");
2866 
2867    slrn_set_color (0);
2868    Slrn_Full_Screen_Update = 0;
2869 }
2870 
2871 /*}}}*/
2872