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