This is group.c in view mode; [Download] [Up]
/* Copyright (c) 1995 John E. Davis (davis@space.mit.edu)
* All rights reserved.
*/
#include "config.h"
#include "features.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef VMS
# include <sys/types.h>
# include <sys/stat.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <slang.h>
#include "jdmacros.h"
/* #include "clientlib.h" */
#include "slrn.h"
#include "group.h"
#include "art.h"
#include "misc.h"
#include "post.h"
#include "server.h"
#include "hash.h"
#include "score.h"
#include "menu.h"
#define GROUP_HASH_TABLE_SIZE 1250
static Slrn_Group_Type *Group_Hash_Table [GROUP_HASH_TABLE_SIZE];
int Slrn_Query_Group_Cutoff = 100;
int Slrn_Groups_Dirty; /* greater than 0 if need to write newsrc */
int Slrn_List_Active_File = 0;
int Slrn_Use_Xgtitle = 0;
int Slrn_Write_Newsrc_Flags = 0; /* if 1, do not save unsubscribed
* if 2, do not save new unsubscribed.
*/
int Slrn_Group_Description_Column = 40;/* column where group descr start */
static Slrn_Group_Type *Groups, *Current_Group;
static Slrn_Group_Type *Top_Group; /* group at top of screen */
static Slrn_Group_Type *Bottom_Group; /* group at bottom of screen */
static int Last_Cursor_Row;
static int Num_Groups;
static int Line_Num;
static int Groups_Hidden; /* if true, hide groups with no arts */
int Slrn_Group_Window_Size; /* num rows in group window */
int Slrn_Group_Display_Descriptions = 1;
int Slrn_No_Backups = 0;
int Slrn_Prompt_Next_Group = 1;
static void group_update_screen (void);
static void quick_help (void)
{
if (0 == slrn_message (
"SPC:Select p:Post c:CatchUp l:List q:Quit ^R:Redraw (u)s:(Un)Subscribe"))
Slrn_Message_Present = 0;
}
#if 0
static void slrn_dump_groups (void)
{
Slrn_Group_Type *g;
g = Groups;
while (g != NULL)
{
fprintf (stdout, "%s %d-%d\n", g->name, g->range.min, g->range.max);
g = g->next;
}
fflush (stdout);
}
#endif
/* Note: This routine is NOT very robust. It assumes that this function
* is called in an ordered way such that the implied range is always increasing.
* This is why the range is re-built in art.c:update_ranges. Yes, it is ugly.
* See also slrn_group_mark_article_as_read for something more random.
*/
void slrn_add_group_ranges (Slrn_Group_Type *g, int min, int max)
{
Slrn_Range_Type *r, *next;
int unread;
if ((max < min) || (g == NULL)) return;
/* The first one is range of articles on server so expand max to cover
* the range of articles nolonger available.
*/
next = &g->range;
if (max < next->min)
{
/* If we have already expanded the range up to range currently available
* at server and we are now trying to add another range below available
* range, do not bother.
*/
if (next->next != NULL) return;
max = next->min - 1;
}
/* Count number unread */
unread = next->max;
while (next->next != NULL)
{
next = next->next;
unread -= next->max - next->min + 1;
}
/* check to see if a merge is possible */
if ((min <= next->max + 1)
&& (next != &g->range))
{
next->max = max;
}
else
{
if (NULL == (r = (Slrn_Range_Type *) SLMALLOC (sizeof(Slrn_Range_Type))))
{
slrn_exit_error ("Memory allocation error.");
}
r->next = next->next;
next->next = r;
r->prev = next;
r->min = min;
r->max = max;
/* For this case, min should be 1 */
if (next == &g->range)
{
min = r->min = 1;
}
}
unread -= max - min + 1;
if (unread < 0) unread = 0;
g->unread = unread;
Slrn_Groups_Dirty = 1;
}
static void group_mark_article_as_read (Slrn_Group_Type *g, long num)
{
Slrn_Range_Type *r, *r1, *newr;
r1 = &g->range;
if (r1->max < num) /* not at server yet so update our data */
{
r1->max = num;
g->unread += 1;
}
r = r1->next;
while (r != NULL)
{
/* Already read */
if ((num <= r->max) && (num >= r->min)) return;
if (num < r->min) break;
r1 = r;
r = r->next;
}
if (g->unread > 0) g->unread -= 1;
Slrn_Groups_Dirty = 1;
if ((r != NULL) && (r->min == num + 1))
{
r->min = num;
return;
}
if ((r1->max + 1 == num) && (r1 != &g->range))
{
r1->max = num;
return;
}
if (NULL == (newr = (Slrn_Range_Type *) SLMALLOC (sizeof (Slrn_Range_Type))))
{
slrn_exit_error ("Memory allocation error");
}
newr->min = newr->max = num;
newr->next = r;
if (r != NULL) r->prev = newr;
newr->prev = r1;
r1->next = newr;
}
void slrn_mark_article_as_read (char *group, long num)
{
Slrn_Group_Type *g;
unsigned long hash;
if (group == NULL)
{
group_mark_article_as_read (Current_Group, num);
return;
}
hash = slrn_compute_hash ((unsigned char *) group,
(unsigned char *) group + strlen (group));
g = Group_Hash_Table[hash % GROUP_HASH_TABLE_SIZE];
while (g != NULL)
{
if ((g->hash == hash) && !strcmp (group, g->name))
{
/* If it looks like we have read this group, mark it read. */
if (((g->flags & GROUP_UNSUBSCRIBED) == 0)
|| (g->range.next != NULL))
group_mark_article_as_read (g, num);
break;
}
g = g->hash_next;
}
}
static Slrn_Group_Type *find_group_entry (char *name, unsigned int len)
{
int hash_index;
unsigned long hash;
Slrn_Group_Type *g;
hash = slrn_compute_hash ((unsigned char *) name,
(unsigned char *) name + len);
hash_index = hash % GROUP_HASH_TABLE_SIZE;
g = Group_Hash_Table[hash_index];
while (g != NULL)
{
if ((g->hash == hash) && !strncmp (name, g->name, len))
{
if (len == strlen (g->name)) break;;
}
g = g->hash_next;
}
return g;
}
static Slrn_Group_Type *create_group_entry (char *name, unsigned int len,
int min, int max, int query_server,
int skip_find)
{
int hash_index;
unsigned long hash;
Slrn_Group_Type *g;
if (skip_find == 0)
{
g = find_group_entry (name, len);
if (g != NULL) return g;
}
if (NULL == (g = (Slrn_Group_Type *) SLMALLOC (sizeof (Slrn_Group_Type))))
{
slrn_exit_error ("Memory allocation failure.");
}
SLMEMSET ((char *) g, 0, sizeof (Slrn_Group_Type));
if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
strncpy (g->name, name, len);
g->name [len] = 0;
if (query_server)
{
if (0 != Slrn_Server_Obj->sv_select_group (g->name, &min, &max))
{
Slrn_Groups_Dirty = 1;
if (Slrn_TT_Initialized == 0)
slrn_error ("Group %s is bogus.\r\n", g->name);
else
slrn_error ("Group %s is bogus.\n", g->name);
SLFREE (g);
return NULL;
}
}
g->range.min = min;
g->range.max = max;
if (max > 0)
g->unread = max - min + 1;
else g->unread = 0;
g->flags = (GROUP_UNSUBSCRIBED | GROUP_HIDDEN);
hash = slrn_compute_hash ((unsigned char *) name,
(unsigned char *) name + len);
hash_index = hash % GROUP_HASH_TABLE_SIZE;
g->hash = hash;
g->hash_next = Group_Hash_Table[hash_index];
Group_Hash_Table[hash_index] = g;
if (Groups == NULL)
{
Current_Group = Groups = g;
}
else
{
if (Current_Group == NULL) Current_Group = Groups;
g->next = Current_Group->next;
if (g->next != NULL) g->next->prev = g;
Current_Group->next = g;
g->prev = Current_Group;
}
Current_Group = g;
return g;
}
static int parse_active_line (char *name, unsigned int *lenp,
int *minp, int *maxp)
{
char *p;
p = name;
while (*p > ' ') p++;
*lenp = (unsigned int) (p - name);
while (*p == ' ') p++;
*maxp = atoi (p);
while (*p > ' ') p++; while (*p == ' ') p++;
*minp = atoi(p);
if (*maxp < *minp) *minp = *maxp + 1;
return 0;
}
static int add_group (char *name, unsigned int len,
unsigned int subscribe_flag, int create_flag)
{
char ch;
Slrn_Group_Type *g;
g = find_group_entry (name, len);
if (g == NULL)
{
if (Slrn_List_Active_File)
{
char namebuf[MAX_GROUP_NAME_LEN + 1];
if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
strncpy (namebuf, name, len);
namebuf[len] = 0;
fprintf (stderr, "Group %s is bogus, Removing it.\r\n", namebuf);
return -1;
}
else g = create_group_entry (name, len, -1, -1,
!(subscribe_flag & GROUP_UNSUBSCRIBED),
0);
if (g == NULL) return -1;
}
Slrn_Groups_Dirty = 1;
/* If we have already processed this, then the group is duplicated in
* the newsrc file. Throw it out now.
*/
if (g->flags & GROUP_PROCESSED) return -1;
Current_Group = g;
g->flags = subscribe_flag;
g->flags |= GROUP_PROCESSED;
if (subscribe_flag & GROUP_UNSUBSCRIBED)
{
g->unread = 0;
g->flags |= GROUP_HIDDEN;
/* if (Slrn_List_Active_File == 0) return 0; */
}
if (create_flag) return 0;
/* find ranges for this */
name += len; /* skip past name */
if (*name) name++; /* skip colon */
while (1)
{
int min, max;
/* skip white space and delimiters */
while (((ch = *name) != 0) && ((ch <= ' ') || (ch == ','))) name++;
if ((ch < '0') || (ch > '9')) break;
min = atoi (name++);
while (((ch = *name) != 0) && (ch >= '0') && (ch <= '9')) name++;
if (ch == '-')
{
name++;
max = atoi (name);
while (((ch = *name) != 0) && (ch >= '0') && (ch <= '9')) name++;
}
else max = min;
slrn_add_group_ranges (Current_Group, min, max);
}
return 0;
}
static void find_line_num (void)
{
Slrn_Group_Type *g;
int n;
n = 1;
if (Current_Group == NULL) Current_Group = Groups;
/* Move to a group that is not hidden */
while (Current_Group != NULL)
{
if ((Current_Group->flags & GROUP_HIDDEN) == 0) break;
Current_Group = Current_Group->next;
}
if (Current_Group != NULL)
{
g = Groups;
while (g != Current_Group)
{
if (0 == (g->flags & GROUP_HIDDEN)) n++;
g = g->next;
}
}
else if (Groups_Hidden == 0)
{
Current_Group = Groups;
}
Line_Num = n;
g = Current_Group;
Num_Groups = n - 1;
while (g != NULL)
{
if (0 == (g->flags & GROUP_HIDDEN)) Num_Groups++;
g = g->next;
}
}
static void toggle_hide_groups (void)
{
Slrn_Group_Type *g;
int n = 0;
Groups_Hidden = !Groups_Hidden;
g = Groups;
if (Groups_Hidden)
{
while (g != NULL)
{
if ((g->unread == 0)
&& ((g->flags & GROUP_UNSUBSCRIBED) == 0))
g->flags |= GROUP_HIDDEN;
g = g->next;
}
}
else
{
while (g != NULL)
{
if ((g->unread == 0)
&& ((g->flags & GROUP_UNSUBSCRIBED) == 0))
g->flags &= ~GROUP_HIDDEN;
g = g->next;
}
}
g = Groups;
n = 0;
while (g != NULL)
{
if ((g->flags & GROUP_HIDDEN) == 0) n++;
g = g->next;
}
Num_Groups = n;
g = Current_Group;
while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->next;
if ((g == NULL) && (Current_Group != NULL))
{
g = Current_Group -> prev;
while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->prev;
}
Current_Group = g;
find_line_num ();
Top_Group = NULL;
Slrn_Full_Screen_Update = 1;
}
static SLRegexp_Type *read_group_regexp (char *prompt)
{
static char pattern[256];
if (slrn_read_input (prompt, pattern, 1) <= 0) return NULL;
return slrn_compile_regexp_pattern (slrn_fix_regexp (pattern));
}
static Slrn_Group_Type *process_xgtitle_info (void)
{
char buf [NNTP_BUFFER_SIZE];
Slrn_Group_Type *first = NULL, *save = Current_Group;
while (Slrn_Server_Obj->sv_read_line(buf, sizeof(buf)) != NULL)
{
char *b, ch;
unsigned int len;
Slrn_Group_Type *g;
b = buf;
while (((ch = *b) != 0)
&& (ch != ' ')
&& (ch != '\n')
&& (ch != '\t'))
b++;
len = (unsigned int) (b - buf);
if (len == 0) continue;
*b = 0;
g = create_group_entry (buf, len,
-1, -1, 0, 0);
if (g != NULL)
{
g->flags &= ~GROUP_HIDDEN;
if ((first == NULL) && (g->flags & GROUP_UNSUBSCRIBED))
first = g;
}
}
if (save != Current_Group)
{
Current_Group = save;
find_line_num ();
}
return first;
}
static void toggle_list_all_groups1 (int hide_flag)
{
Slrn_Group_Type *g, *first_found = NULL;
int n = 0;
static int all_hidden = 1;
g = Groups;
if (hide_flag != -1)
{
all_hidden = hide_flag;
}
else all_hidden = !all_hidden;
if (all_hidden)
{
while (g != NULL)
{
if (g->flags & GROUP_UNSUBSCRIBED) g->flags |= GROUP_HIDDEN;
g = g->next;
}
}
#if 0
else if (prompt == 0)
{
while (g != NULL)
{
if (g->flags & GROUP_UNSUBSCRIBED)
{
g->flags &= ~GROUP_HIDDEN;
}
g = g->next;
}
}
#endif
else
{
SLRegexp_Type *re;
if (NULL == (re = read_group_regexp ("List Groups (e.g., comp*unix*): ")))
{
all_hidden = 1;
return;
}
if ((Slrn_List_Active_File == 0)
&& Slrn_Use_Xgtitle
&& (-1 != Slrn_Server_Obj->sv_xgtitle_cmd ((char *) re->pat)))
{
first_found = process_xgtitle_info ();
}
else while (g != NULL)
{
if (g->flags & GROUP_UNSUBSCRIBED)
{
if (NULL != slrn_regexp_match (re, g->name))
{
if (first_found == NULL) first_found = g;
g->flags &= ~GROUP_HIDDEN;
}
}
g = g->next;
}
}
g = Groups;
n = 0;
while (g != NULL)
{
if ((g->flags & GROUP_HIDDEN) == 0) n++;
g = g->next;
}
Num_Groups = n;
g = Current_Group;
if (first_found != NULL)
{
g = first_found;
}
else
{
while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->next;
if ((g == NULL) && (Current_Group != NULL))
{
g = Current_Group -> prev;
while ((g != NULL) && (g->flags & GROUP_HIDDEN)) g = g->prev;
}
}
Current_Group = g;
if (g != NULL)
{
g = Groups;
n = 1;
while (g != Current_Group)
{
if (0 == (g->flags & GROUP_HIDDEN)) n++;
g = g->next;
}
}
else n = 1;
Top_Group = NULL;
Slrn_Full_Screen_Update = 1;
if ((all_hidden == 0) && (Current_Group == NULL))
{
Current_Group = Groups;
if ((Current_Group != NULL)
&& (Current_Group->flags & GROUP_HIDDEN))
{
Current_Group = NULL;
}
n = 1;
}
Line_Num = n;
}
static int find_group (char *name)
{
Slrn_Group_Type *g = find_group_entry (name, strlen (name));
if (g == NULL) return 0;
g->flags &= ~GROUP_HIDDEN;
Current_Group = g;
find_line_num ();
return 1;
}
static void group_search (void)
{
static char search_str[256];
SLsearch_Type st;
Slrn_Group_Type *g;
int n = 0, wrapped = 0;
g = Current_Group;
if (g == NULL) return;
if (slrn_read_input ("Search: ", search_str, 1) <= 0) return;
SLsearch_init (search_str, 1, 0, &st);
do
{
g = g->next;
if (g == NULL)
{
g = Groups;
n = 0;
wrapped = 1;
}
if ((g->flags & GROUP_HIDDEN) == 0)
{
n++;
if (NULL != SLsearch ((unsigned char *) g->name,
(unsigned char *) g->name + strlen (g->name),
&st))
{
break;
}
}
}
while (g != Current_Group);
if (g == Current_Group) slrn_error ("Not found.");
else
{
Current_Group = g;
if (wrapped)
{
Line_Num = 0;
slrn_message ("Search wrapped.");
}
Line_Num += n;
}
}
static void add_group_cmd (void)
{
char group[256];
*group = 0;
if (slrn_read_input ("Add group: ", group, 1) > 0)
{
if (!find_group (group)
&& (Slrn_List_Active_File == 0))
add_group (group, strlen (group), 0, 0);
Slrn_Groups_Dirty = 1;
Slrn_Full_Screen_Update = 1;
}
}
static void catch_up_for_real (void)
{
Slrn_Range_Type *r, *rnext;
if (Current_Group == NULL) return;
r = Current_Group->range.next;
while (r != NULL)
{
rnext = r->next;
SLFREE (r);
r = rnext;
}
Current_Group->range.next = NULL;
slrn_add_group_ranges (Current_Group, 1, Current_Group->range.max);
Current_Group->flags |= GROUP_TOUCHED;
slrn_message ("Group marked as read.");
}
/* TODO: check for write errors!!! */
int slrn_write_newsrc (void)
{
Slrn_Group_Type *g;
Slrn_Range_Type *r;
char file[SLRN_MAX_PATH_LEN], backup_file[SLRN_MAX_PATH_LEN];
static FILE *fp;
int pass;
int max;
#ifdef unix
# ifndef __os2__
struct stat filestat;
int stat_worked;
# endif
#endif
slrn_init_hangup_signals (0);
if (Slrn_Groups_Dirty == 0)
{
slrn_init_hangup_signals (1);
return 0;
}
/* In case of hangup and we were writing the file, make sure it is closed.
* This will not hurt since we are going to do it again anyway.
*/
if (fp != NULL) slrn_fclose (fp); fp = NULL;
slrn_message ("Writing %s...", Slrn_Newsrc_File);
slrn_smg_refresh ();
slrn_make_home_filename (Slrn_Newsrc_File, file);
#ifdef unix
# ifndef __os2__
/* Try to preserve .newsrc permissions and owner/group */
stat_worked = (-1 != stat (file, &filestat));
# endif
#endif
if (Slrn_No_Backups == 0)
{
#ifdef VMS
sprintf (backup_file, "%s-bak", file);
#else
sprintf (backup_file, "%s~", file);
#endif
(void) slrn_delete_file (backup_file);
(void) rename (file, backup_file);
}
if (NULL == (fp = fopen (file, "w")))
{
slrn_error ("Unable to save to file %s.", file);
if (Slrn_No_Backups == 0) (void) rename (backup_file, file);
slrn_init_hangup_signals (1);
return -1;
}
/* We are going to do this in 2 passes. The first pass writes out just
* the subscribed groups. The second pass takes care of the rest.
*/
for (pass = 0; pass < 2; pass++)
{
g = Groups;
while (g != NULL)
{
if (pass == 0)
{
if (g->flags & GROUP_UNSUBSCRIBED)
{
g = g->next;
continue;
}
fputs (g->name, fp);
putc (':', fp);
}
else if (pass == 1)
{
if ((g->flags & GROUP_UNSUBSCRIBED) == 0)
{
g = g->next;
continue;
}
if (Slrn_Write_Newsrc_Flags)
{
if ((Slrn_Write_Newsrc_Flags == 1)
|| ((Slrn_Write_Newsrc_Flags == 2)
&& (g->range.next == NULL)))
{
g = g->next;
continue;
}
}
fputs (g->name, fp);
putc ('!', fp);
}
r = g->range.next;
max = g->range.max;
if (r != NULL)
{
putc (' ', fp);
while (1)
{
/* Make this check because the unsubscribed group
* range may not have been initialized from the server.
*/
if ((max != -1) && (g->range.min != -1)
&& ((g->flags & GROUP_UNSUBSCRIBED) == 0))
{
if (r->min > max) break;
if (r->max > max) r->max = max;
}
if (r->min != r->max)
{
fprintf (fp, "%d-%d", r->min, r->max);
}
else fprintf (fp, "%d", r->min);
r = r->next;
if (r == NULL) break;
putc (',', fp);
}
}
putc ('\n', fp);
g = g->next;
}
}
slrn_fclose (fp); fp = NULL;
#ifdef unix
# ifndef __os2__
/* Try to preserve .newsrc permissions and owner/group */
if (stat_worked)
{
if (-1 == chmod (file, filestat.st_mode & 0777))
(void) chmod (file, 0600);
(void) chown (file, filestat.st_uid, filestat.st_gid);
}
else
(void) chmod (file, 0600 );
# endif
#endif
Slrn_Groups_Dirty = 0;
if (Slrn_TT_Initialized & 1)
slrn_message ("Writing %s... done.", Slrn_Newsrc_File);
slrn_init_hangup_signals (1);
return 0;
}
static void free_all_groups (void)
{
Slrn_Group_Type *g = Groups;
Slrn_Group_Type *nextg;
Slrn_Range_Type *r, *rnext;
int i;
while (g != NULL)
{
nextg = g->next;
if (g->descript != NULL) SLFREE (g->descript);
r = g->range.next;
while (r != NULL)
{
rnext = r->next;
SLFREE (r);
r = rnext;
}
SLFREE(g);
g = nextg;
}
Groups = NULL;
Current_Group = NULL;
Top_Group = Bottom_Group = NULL;
Num_Groups = Line_Num = 0;
for (i = 0; i < GROUP_HASH_TABLE_SIZE; i++) Group_Hash_Table[i] = NULL;
}
static void save_newsrc (void)
{
if (Slrn_Groups_Dirty)
{
slrn_write_newsrc ();
}
else
{
slrn_message ("No changes need to be saved.");
}
slrn_smg_refresh ();
}
static void refresh_groups (void)
{
char name[MAX_GROUP_NAME_LEN + 1];
*name = 0;
if (Current_Group != NULL) strcpy (name, Current_Group->name);
slrn_set_suspension (1);
save_newsrc ();
free_all_groups ();
/*
* Groups_Hidden gets toggled in slrn_read_newsrc, which gets called
* in slrn_get_new_news. Therefore, pre-toggle Groups_Hidden ahead of
* time.
*/
Groups_Hidden = !Groups_Hidden;
if (-1 == slrn_get_new_news (1, 0))
{
slrn_quit (0);
}
if (*name)
(void) find_group (name);
slrn_set_suspension (0);
quick_help ();
}
typedef struct Unsubscribed_Slrn_Group_Type
{
char name[MAX_GROUP_NAME_LEN + 1];
struct Unsubscribed_Slrn_Group_Type *next;
}
Unsubscribed_Slrn_Group_Type;
static Unsubscribed_Slrn_Group_Type *Unsubscribed_Groups;
static void add_unsubscribed_group (char *name)
{
Unsubscribed_Slrn_Group_Type *g;
char *p;
unsigned int len;
if (NULL == (g = (Unsubscribed_Slrn_Group_Type *) SLMALLOC (sizeof (Unsubscribed_Slrn_Group_Type))))
{
slrn_exit_error ("Memory allocation error.");
}
g->next = Unsubscribed_Groups;
Unsubscribed_Groups = g;
p = name;
while (*p > ' ') p++;
*p = 0;
len = p - name;
if (len > MAX_GROUP_NAME_LEN) len = MAX_GROUP_NAME_LEN;
strncpy (g->name, name, len);
g->name[len] = 0;
}
static int group_up_n (int n)
{
Slrn_Group_Type *g;
int i;
g = Current_Group;
i = 0;
if (g != NULL) while (i < n)
{
g = g->prev;
while ((g != NULL) && (g->flags & GROUP_HIDDEN))
{
g = g->prev;
}
if (g == NULL) break;
i++;
Line_Num--;
Current_Group = g;
}
return i;
}
static void group_up (void)
{
if (0 == group_up_n (1))
{
slrn_error ("Top of buffer.");
}
}
static int group_down_n (int n)
{
Slrn_Group_Type *g;
int i;
g = Current_Group;
i = 0;
if (g != NULL) while (i < n)
{
g = g->next;
while ((g != NULL) && (g->flags & GROUP_HIDDEN))
{
g = g->next;
}
if (g == NULL) break;
Line_Num++;
Current_Group = g;
i++;
}
return i;
}
static void group_down (void)
{
if (1 != group_down_n (1))
{
slrn_error ("End of Buffer.");
}
}
static void transpose_groups (void)
{
Slrn_Group_Type *g = Current_Group, *g1, *tmp;
if (g == NULL) return;
if (1 != group_up_n (1))
{
return;
}
g1 = Current_Group;
tmp = g1->next;
/* Two cases to consider */
if (tmp != g)
{
g1->next = g->next;
if (g1->next != NULL) g1->next->prev = g1;
g->next = tmp;
tmp->prev = g; /* tmp cannot be NULL */
tmp = g1->prev;
g1->prev = g->prev;
g1->prev->next = g1; /* g1->prev cannot be NULL */
g->prev = tmp;
if (tmp != NULL) tmp->next = g;
}
else /* g1->next == g, g->prev == g1 */
{
g->prev = g1->prev;
if (g->prev != NULL) g->prev->next = g;
g1->next = g->next;
if (g1->next != NULL) g1->next->prev = g1;
g->next = g1;
g1->prev = g;
}
if (g1 == Groups) Groups = g;
find_line_num ();
(void) group_down_n (1);
Slrn_Full_Screen_Update = 1;
Slrn_Groups_Dirty = 1;
}
static void move_group_cmd (void)
{
SLang_Key_Type *key;
void (*f)(void);
Slrn_Group_Type *g;
if (Current_Group == NULL) return;
g = Current_Group;
while (1)
{
slrn_message ("Moving %s. Press RETURN when finished.", Current_Group->name);
slrn_smg_refresh ();
key = SLang_do_key (Slrn_Group_Keymap, (int (*)(void)) SLang_getkey);
if ((key == NULL)
|| (key->type == SLKEY_F_INTERPRET))
f = NULL;
else f = (void (*)(void)) key->f.f;
if (f == group_up)
{
transpose_groups ();
if (2 != group_up_n (2))
return;
}
else if (f == group_down)
{
if (1 != group_down_n (1))
return;
transpose_groups ();
if (1 != group_up_n (1))
return;
}
else return;
if (g != Current_Group)
{
Current_Group = g;
find_line_num ();
}
group_update_screen ();
slrn_smg_refresh ();
}
}
static void subscribe (void)
{
SLRegexp_Type *re;
Slrn_Group_Type *g;
if (Current_Group == NULL) return;
if (Slrn_Prefix_Arg_Ptr == NULL)
{
Current_Group->flags &= ~GROUP_UNSUBSCRIBED;
Current_Group->flags |= GROUP_TOUCHED;
group_down_n (1);
Slrn_Groups_Dirty = 1;
return;
}
Slrn_Prefix_Arg_Ptr = NULL;
if (NULL == (re = read_group_regexp ("Subscribe pattern: ")))
return;
g = Groups;
while (g != NULL)
{
if (g->flags & GROUP_UNSUBSCRIBED)
{
if (NULL != slrn_regexp_match (re, g->name))
{
g->flags &= ~GROUP_HIDDEN;
g->flags &= ~GROUP_UNSUBSCRIBED;
g->flags |= GROUP_TOUCHED;
}
}
g = g->next;
}
find_line_num ();
Slrn_Full_Screen_Update = 1;
}
static void catch_up (void)
{
if (Current_Group == NULL) return;
if (Slrn_User_Wants_Confirmation &&
slrn_get_yesno(1, "Mark %s as read", Current_Group->name) <= 0) return;
catch_up_for_real ();
(void) group_down_n (1);
}
static void uncatch_up (void)
{
Slrn_Range_Type *r, *rnext;
if (Current_Group == NULL) return;
if (Slrn_User_Wants_Confirmation &&
slrn_get_yesno(1, "Mark %s as un-read", Current_Group->name) <= 0) return;
r = Current_Group->range.next;
while (r != NULL)
{
rnext = r->next;
SLFREE (r);
r = rnext;
}
Current_Group->range.next = NULL;
slrn_add_group_ranges (Current_Group, 1, 1);
Current_Group->flags |= GROUP_TOUCHED;
slrn_message ("Group marked as un-read.");
(void) group_down_n (1);
}
static void unsubscribe (void)
{
SLRegexp_Type *re;
Slrn_Group_Type *g;
if (Current_Group == NULL) return;
if (Slrn_Prefix_Arg_Ptr == NULL)
{
Current_Group->flags |= GROUP_UNSUBSCRIBED | GROUP_TOUCHED;
group_down_n (1);
Slrn_Groups_Dirty = 1;
return;
}
Slrn_Prefix_Arg_Ptr = NULL;
if (NULL == (re = read_group_regexp ("Un-Subscribe pattern: ")))
return;
g = Groups;
while (g != NULL)
{
if ((g->flags & GROUP_UNSUBSCRIBED) == 0)
{
if (NULL != (slrn_regexp_match (re, g->name)))
{
g->flags &= ~GROUP_HIDDEN;
g->flags |= (GROUP_TOUCHED | GROUP_UNSUBSCRIBED);
}
}
g = g->next;
}
find_line_num ();
Slrn_Full_Screen_Update = 1;
}
SLKeyMap_List_Type *Slrn_Group_Keymap;
int *Slrn_Prefix_Arg_Ptr;
void slrn_set_prefix_argument (int rep)
{
static int repeat;
repeat = rep;
Slrn_Prefix_Arg_Ptr = &repeat;
}
void slrn_digit_arg (SLKeyMap_List_Type *kmap)
{
char buf[20];
unsigned char key;
int i;
i = 0;
buf[i++] = (char) SLang_Last_Key_Char;
while(1)
{
buf[i] = 0;
key = (unsigned char) SLang_getkey ();
if ((key < '0') || (key > '9')) break;
buf[i++] = (char) key;
}
slrn_set_prefix_argument (atoi (buf));
SLang_ungetkey (key);
slrn_do_key (kmap);
Slrn_Prefix_Arg_Ptr = NULL;
}
static void digit_arg (void)
{
slrn_digit_arg (Slrn_Group_Keymap);
}
static int group_sync_group_with_server (Slrn_Group_Type *g, int *minp, int *maxp)
{
int min, max, n, max_available;
char *group;
Slrn_Range_Type *r;
if (g == NULL) return -1;
group = g->name;
slrn_message ("Selecting %s...", group);
slrn_smg_refresh ();
slrn_set_suspension (1);
if (Slrn_Server_Obj->sv_select_group (group, minp, maxp) < 0)
{
g->flags &= ~GROUP_UNSUBSCRIBED;
slrn_error ("This group appears to be bogus.");
slrn_set_suspension (0);
return -1;
}
slrn_set_suspension (0);
min = *minp;
max = *maxp;
if (max == 0)
{
int nmax, nmin;
nmax = g->range.max;
nmin = g->range.min;
/* Server database inconsistent. */
for (n = nmin; n <= nmax; n++)
group_mark_article_as_read (g, n);
/* g->unread = 0; */
slrn_message ("No articles to read.");
Slrn_Full_Screen_Update = 1;
Slrn_Groups_Dirty = 1;
return -1;
}
g->range.min = min;
if (max < g->range.max)
{
/* There is only one way for this to happen that I am aware of:
* an article has been cancelled-- update the ranges.
*/
int nmax = g->range.max;
for (n = max + 1; n <= nmax; n++)
group_mark_article_as_read (g, n);
}
else g->range.max = max;
/* In case more articles arrived at the server between the time that
* slrn was first started and when the server was just queried, update
* the ranges of read/unread articles.
*/
max_available = g->range.max - g->range.min + 1;
r = &g->range;
if (r->next != NULL)
{
if (r->next->min <= r->min)
r->next->min = 1;
n = r->max;
while (r->next != NULL)
{
r = r->next;
n -= r->max - r->min + 1;
}
if (n < 0) n = 0;
if (n > max_available) n = max_available;
g->unread = n;
}
return 0;
}
static void group_select_group (void)
{
int min, max, n, max_available;
int ret;
Slrn_Range_Type *r;
int prefix;
if (Slrn_Prefix_Arg_Ptr != NULL)
{
prefix = *Slrn_Prefix_Arg_Ptr;
Slrn_Prefix_Arg_Ptr = NULL;
}
else prefix = 0;
if (-1 == group_sync_group_with_server (Current_Group, &min, &max))
return;
n = Current_Group->unread;
max_available = Current_Group->range.max - Current_Group->range.min + 1;
if ((prefix & 1) || (n == 0))
n = max_available;
if ((prefix & 1) ||
((n > Slrn_Query_Group_Cutoff)
&& (Slrn_Query_Group_Cutoff > 0)))
{
char int_prompt_buf[256];
sprintf (int_prompt_buf, "%s: Read how many?", Current_Group->name);
if ((-1 == slrn_read_integer (int_prompt_buf, &n, &n))
|| (n <= 0))
{
slrn_clear_message ();
Slrn_Full_Screen_Update = 1;
return;
}
if ((0 == (prefix & 1))
&& (Current_Group->unread != 0))
{
r = Current_Group->range.next;
if (r != NULL)
{
while (r->next != NULL) r = r->next;
if (r->max + n > max)
n = -n; /* special treatment in article mode
* because we will need to query the
* server about articles in a group
* that we have already read.
*/
}
}
}
else if ((0 == (prefix & 1)) && (Current_Group->unread != 0))
n = 0;
ret = slrn_select_article_mode (Current_Group, n, ((prefix & 2) == 0));
if (ret == -2) catch_up_for_real ();
}
void slrn_select_next_group (void)
{
if (Current_Group == NULL)
return;
while (1 == group_down_n (1))
{
if (Current_Group->unread == 0)
continue;
group_select_group ();
break;
#if 0
if (Slrn_Current_Mode->mode == SLRN_ARTICLE_MODE)
break;
#endif
}
}
void slrn_select_prev_group (void)
{
}
void slrn_post_cmd (void)
{
int ret;
char *name, *dist = NULL; /* "world"; */
char group[256];
char subj[256];
if (Slrn_Post_Obj->po_can_post == 0)
{
slrn_error ("Posting not allowed.");
return;
}
if (Slrn_User_Wants_Confirmation &&
(slrn_get_yesno (1, "Are you sure that you want to post") <= 0))
return;
if (Current_Group == NULL) name = ""; else name = Current_Group->name;
strcpy (group, name);
if (slrn_read_input ("Newsgroup: ", group, 1) <= 0) return;
*subj = 0; if (slrn_read_input ("Subject: ", subj, 1) <= 0) return;
ret = slrn_post (group, subj, dist);
if (ret < 0)
{
slrn_error ("Posting failed.");
}
}
void slrn_group_quit (void)
{
if (Slrn_User_Wants_Confirmation
&& (slrn_get_yesno (1, "Do you really want to quit") <= 0)) return;
if (Slrn_Groups_Dirty) slrn_write_newsrc ();
slrn_quit (0);
}
#if 0
static void group_refresh (void)
{
if (Slrn_Groups_Dirty) slrn_write_newsrc ();
Slrn_Hangup_Hook = NULL;
slrn_quit (2);
}
#endif
static void group_pageup (void)
{
Slrn_Group_Type *g;
int n;
if (Top_Group != NULL)
{
Slrn_Full_Screen_Update = 1;
/* Move to top of window, then go up one page */
n = 0;
g = Current_Group;
while ((g != Top_Group) && (g != NULL))
{
g = g->prev;
if (0 == (g->flags & GROUP_HIDDEN)) n++;
}
if (g != NULL)
{
Current_Group = g;
Line_Num -= n;
n = Line_Num; /* save this */
/* Now compute new top group */
group_up_n (Slrn_Group_Window_Size - 1);
Top_Group = Current_Group;
Current_Group = g; Line_Num = n;
return;
}
}
if (0 == group_up_n (Slrn_Group_Window_Size - 1))
slrn_error ("Top of Buffer.");
}
static void group_pagedown (void)
{
Slrn_Group_Type *g;
int n;
if (Bottom_Group != NULL)
{
Slrn_Full_Screen_Update = 1;
n = 0;
g = Current_Group;
while ((g != Bottom_Group) && (g != NULL))
{
g = g->next;
if (0 == (g->flags & GROUP_HIDDEN)) n++;
}
if (g != NULL)
{
Top_Group = Current_Group = g;
Line_Num += n;
return;
}
}
if (0 == group_down_n (Slrn_Group_Window_Size - 1))
slrn_error ("End of Buffer.");
}
static void group_bob (void)
{
while (group_up_n (1000));
}
static void group_eob (void)
{
while (group_down_n (1000));
}
static void toggle_list_all_groups (void)
{
int mode;
if (Slrn_Prefix_Arg_Ptr != NULL)
mode = 1;
else mode = -1;
toggle_list_all_groups1 (mode);
}
static void toggle_group_display (void)
{
Slrn_Group_Display_Descriptions = !Slrn_Group_Display_Descriptions;
Slrn_Full_Screen_Update = 1;
}
void slrn_repeat_last_key (void)
{
SLtt_beep ();
}
static void toggle_scoring (void)
{
switch (slrn_get_response ("FfSsNnCc",
"Select scoring mode: F-ull, S-imple, N-one, C-ancel"))
{
case 'F':
case 'f':
Slrn_Perform_Scoring = SLRN_XOVER_SCORING | SLRN_EXPENSIVE_SCORING;
slrn_message ("Full Header Scoring enabled.");
break;
case 'S':
case 's':
Slrn_Perform_Scoring = SLRN_XOVER_SCORING;
slrn_message ("Expensive Scoring disabled.");
break;
case 'N':
case 'n':
Slrn_Perform_Scoring = 0;
slrn_message ("Scoring disabled.");
break;
default:
slrn_clear_message ();
break;
}
}
#define A_KEY(s, f) {s, (int (*)(void)) f}
static SLKeymap_Function_Type Group_Functions [] =
{
A_KEY("move_group", move_group_cmd),
A_KEY("uncatch_up", uncatch_up),
A_KEY("toggle_scoring", toggle_scoring),
A_KEY("toggle_group_display", toggle_group_display),
A_KEY("refresh_groups", refresh_groups),
A_KEY("save_newsrc", save_newsrc),
A_KEY("group_search", group_search),
A_KEY("group_search_forward", group_search),
A_KEY("toggle_list_all", toggle_list_all_groups),
A_KEY("add_group", add_group_cmd),
A_KEY("bob", group_bob),
A_KEY("catchup", catch_up),
A_KEY("down", group_down),
A_KEY("eob", group_eob),
A_KEY("help", slrn_group_help),
A_KEY("pagedown", group_pagedown),
A_KEY("pageup", group_pageup),
A_KEY("post", slrn_post_cmd),
A_KEY("quit", slrn_group_quit),
A_KEY("redraw", slrn_redraw),
A_KEY("select_group", group_select_group),
A_KEY("subscribe", subscribe),
A_KEY("suspend", slrn_suspend_cmd),
A_KEY("toggle_hidden", toggle_hide_groups),
A_KEY("unsubscribe", unsubscribe),
A_KEY("up", group_up),
A_KEY("repeat_last_key", slrn_repeat_last_key),
A_KEY("transpose_groups", transpose_groups),
A_KEY(NULL, NULL)
};
/* actions for different regions:
* - top status line (help)
* - normal region
* - bottom status line
*/
static void group_mouse (void (*top_status)(void),
void (*bot_status)(void),
void (*normal_region)(void)
)
{
int r,c;
slrn_get_mouse_rc (&r, &c);
/* take top status line into account */
if (r == 1)
{
if (Slrn_Use_Mouse)
slrn_execute_menu (c, Group_Functions);
else
if (NULL != top_status) (*top_status) ();
return;
}
if (r >= SLtt_Screen_Rows)
return;
/* bottom status line */
if (r == SLtt_Screen_Rows - 1)
{
if (NULL != bot_status) (*bot_status) ();
return;
}
r -= (1 + Last_Cursor_Row);
if (r < 0)
{
r = -r;
if (r != group_up_n (r)) return;
}
else if (r != group_down_n (r)) return;
if (NULL != normal_region) (*normal_region) ();
}
static void group_mouse_left (void)
{
group_mouse (slrn_group_help, group_pagedown, group_select_group);
}
static void group_mouse_middle (void)
{
group_mouse (toggle_group_display, toggle_hide_groups, group_select_group);
#if 1
/* Make up for buggy rxvt which have problems with the middle key. */
if (NULL != getenv ("COLORTERM"))
{
if (SLang_input_pending (7))
{
while (SLang_input_pending (0)) SLang_getkey ();
}
}
#endif
}
static void group_mouse_right (void)
{
group_mouse (slrn_group_help, group_pageup, group_select_group);
}
static void slrn_group_hup (int sig)
{
slrn_write_newsrc ();
slrn_quit (sig);
}
static Slrn_Mode_Type Group_Mode_Cap =
{
NULL,
group_update_screen, /* redraw */
NULL, /* sig winch hook */
slrn_group_hup, /* hangup hook */
SLRN_GROUP_MODE
};
void slrn_init_group_mode (void)
{
char *err = "Unable to create group keymap!";
if (NULL == (Slrn_Group_Keymap = SLang_create_keymap ("group", NULL)))
slrn_exit_error (err);
Group_Mode_Cap.keymap = Slrn_Group_Keymap;
Slrn_Group_Keymap->functions = Group_Functions;
SLkm_define_key ("\0331", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0332", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0333", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0334", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0335", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0336", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0337", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0338", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0339", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("\0330", (FVOID_STAR) digit_arg, Slrn_Group_Keymap);
SLkm_define_key ("^K\033[A", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
SLkm_define_key ("^K\033OA", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
SLkm_define_key ("^K\033[B", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
SLkm_define_key ("^K\033OB", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
SLkm_define_key ("\033A", (FVOID_STAR) toggle_group_display, Slrn_Group_Keymap);
SLkm_define_key ("\033>", (FVOID_STAR) group_eob, Slrn_Group_Keymap);
SLkm_define_key ("\033<", (FVOID_STAR) group_bob, Slrn_Group_Keymap);
SLkm_define_key ("^D", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
SLkm_define_key ("^V", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
#ifdef __os2__
SLkm_define_key ("^@Q", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
SLkm_define_key ("\xE0Q", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
SLkm_define_key ("^@I", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
SLkm_define_key ("\xE0I", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
#else
SLkm_define_key ("\033[6~", (FVOID_STAR) group_pagedown, Slrn_Group_Keymap);
SLkm_define_key ("\033[5~", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
#endif
SLkm_define_key ("m", (FVOID_STAR) move_group_cmd, Slrn_Group_Keymap);
SLkm_define_key ("^U", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
SLkm_define_key ("\033V", (FVOID_STAR) group_pageup, Slrn_Group_Keymap);
SLkm_define_key ("a", (FVOID_STAR) add_group_cmd, Slrn_Group_Keymap);
SLkm_define_key ("u", (FVOID_STAR) unsubscribe, Slrn_Group_Keymap);
SLkm_define_key ("s", (FVOID_STAR) subscribe, Slrn_Group_Keymap);
SLkm_define_key ("\033u", (FVOID_STAR) uncatch_up, Slrn_Group_Keymap);
SLkm_define_key ("c", (FVOID_STAR) catch_up, Slrn_Group_Keymap);
SLkm_define_key ("K", (FVOID_STAR) toggle_scoring, Slrn_Group_Keymap);
SLkm_define_key ("L", (FVOID_STAR) toggle_list_all_groups, Slrn_Group_Keymap);
SLkm_define_key ("l", (FVOID_STAR) toggle_hide_groups, Slrn_Group_Keymap);
SLkm_define_key ("^Z", (FVOID_STAR) slrn_suspend_cmd, Slrn_Group_Keymap);
SLkm_define_key (" ", (FVOID_STAR) group_select_group, Slrn_Group_Keymap);
SLkm_define_key (".", (FVOID_STAR) slrn_repeat_last_key, Slrn_Group_Keymap);
SLkm_define_key ("P", (FVOID_STAR) slrn_post_cmd, Slrn_Group_Keymap);
SLkm_define_key ("?", (FVOID_STAR) slrn_group_help, Slrn_Group_Keymap);
SLkm_define_key ("\r", (FVOID_STAR) group_select_group, Slrn_Group_Keymap);
SLkm_define_key ("q", (FVOID_STAR) slrn_group_quit, Slrn_Group_Keymap);
SLkm_define_key ("^X^C", (FVOID_STAR) slrn_group_quit, Slrn_Group_Keymap);
SLkm_define_key ("^X^T", (FVOID_STAR) transpose_groups, Slrn_Group_Keymap);
SLkm_define_key ("^R", (FVOID_STAR) slrn_redraw, Slrn_Group_Keymap);
SLkm_define_key ("^L", (FVOID_STAR) slrn_redraw, Slrn_Group_Keymap);
SLkm_define_key ("^P", (FVOID_STAR) group_up, Slrn_Group_Keymap);
#ifdef __os2__
SLkm_define_key ("^@H", (FVOID_STAR) group_up, Slrn_Group_Keymap);
SLkm_define_key ("\xE0H", (FVOID_STAR) group_up, Slrn_Group_Keymap);
SLkm_define_key ("^@P", (FVOID_STAR) group_down, Slrn_Group_Keymap);
SLkm_define_key ("\xE0P", (FVOID_STAR) group_down, Slrn_Group_Keymap);
#else
SLkm_define_key ("\033[A", (FVOID_STAR) group_up, Slrn_Group_Keymap);
SLkm_define_key ("\033OA", (FVOID_STAR) group_up, Slrn_Group_Keymap);
SLkm_define_key ("\033[B", (FVOID_STAR) group_down, Slrn_Group_Keymap);
SLkm_define_key ("\033OB", (FVOID_STAR) group_down, Slrn_Group_Keymap);
#endif
SLkm_define_key ("N", (FVOID_STAR) group_down, Slrn_Group_Keymap);
SLkm_define_key ("^N", (FVOID_STAR) group_down, Slrn_Group_Keymap);
SLkm_define_key ("/", (FVOID_STAR) group_search, Slrn_Group_Keymap);
SLkm_define_key ("G", (FVOID_STAR) refresh_groups, Slrn_Group_Keymap);
SLkm_define_key ("X", (FVOID_STAR) save_newsrc, Slrn_Group_Keymap);
/* mouse (left/right/middle) */
SLkm_define_key ("\033[M\040", (FVOID_STAR) group_mouse_left, Slrn_Group_Keymap);
SLkm_define_key ("\033[M\041", (FVOID_STAR) group_mouse_middle, Slrn_Group_Keymap);
SLkm_define_key ("\033[M\042", (FVOID_STAR) group_mouse_right, Slrn_Group_Keymap);
if (SLang_Error) slrn_exit_error (err);
}
static void add_group_description (unsigned char *s, unsigned char *smax,
unsigned char *dsc)
{
Slrn_Group_Type *g;
unsigned long hash;
hash = slrn_compute_hash (s, smax);
g = Group_Hash_Table[hash % GROUP_HASH_TABLE_SIZE];
while (g != NULL)
{
if ((g->hash == hash) && (!strncmp (g->name, (char *) s, (unsigned int) (smax - s))))
{
/* Sometimes these get repeated --- not by slrn but on the server! */
if (g->descript != NULL)
SLFREE (g->descript);
g->descript = (char *) SLMALLOC (strlen ((char *) dsc) + 1);
if (g->descript != NULL)
{
strcpy (g->descript, (char *) dsc);
}
return;
}
g = g->hash_next;
}
}
void slrn_get_group_descriptions (void)
{
FILE *fp;
char line[2 * SLRN_MAX_PATH_LEN];
char file[SLRN_MAX_PATH_LEN];
int num;
#ifdef VMS
sprintf (file, "%s-dsc", Slrn_Newsrc_File);
#else
# ifdef __os2__
sprintf (file, "ds-%s", Slrn_Newsrc_File);
# else
sprintf (file, "%s.dsc", Slrn_Newsrc_File);
# endif
#endif
if (NULL == (fp = slrn_open_home_file (file, "w", line, 0)))
{
slrn_exit_error ("\
Unable to create newsgroup description file:\n%s\n", line);
}
fprintf (stdout, "Creating description file %s.\n", line);
Slrn_Server_Obj->sv_list_newsgroups ();
fprintf (stdout, "Getting newsgroup descriptions from server.\n\
Note: This step may take some time if you have a slow connection!!!\n");
fflush (stdout);
num = 0;
while (NULL != Slrn_Server_Obj->sv_read_line (line, sizeof (line) - 1))
{
unsigned char *b, *bmax, *dsc, ch;
num = num % 25;
if (num == 0)
{
putc ('.', stdout);
fflush (stdout);
}
/* Check the syntax on this line. They are often corrupt */
b = (unsigned char *) slrn_skip_whitespace (line);
bmax = b;
while ((ch = *bmax) > ' ') bmax++;
if ((ch == 0) || (ch == '\n')) continue;
*bmax = 0;
/* News group marked off, now get the description. */
dsc = bmax + 1;
while (((ch = *dsc) <= ' ') && (ch != 0)) dsc++;
if ((ch == 0) || (ch == '?') || (ch == '\n')) continue;
/* add_group_description (b, bmax, dsc); */
fputs ((char *) b, fp);
putc(':', fp);
fputs ((char *) dsc, fp);
putc('\n', fp);
num++;
}
slrn_fclose (fp);
putc ('\n', stdout);
}
int slrn_read_group_descriptions (void)
{
FILE *fp;
char line[2 * SLRN_MAX_PATH_LEN];
char file[SLRN_MAX_PATH_LEN];
#ifdef VMS
sprintf (file, "%s-dsc", Slrn_Newsrc_File);
#else
# ifdef __os2__
sprintf (file, "ds-%s", Slrn_Newsrc_File);
# else
sprintf (file, "%s.dsc", Slrn_Newsrc_File);
# endif
#endif
if (NULL == (fp = slrn_open_home_file (file, "r", line, 0)))
{
if (Slrn_Lib_Dir != NULL)
{
#ifdef VMS
sprintf (file, "%snewsgroups.dsc", Slrn_Lib_Dir);
if (NULL == (fp = slrn_open_home_file (file, "r", line, 0)))
{
sprintf (file, "%snewsgroups-dsc", Slrn_Lib_Dir);
fp = slrn_open_home_file (file, "r", line, 0);
}
#else
sprintf (file, "%s/newsgroups.dsc", Slrn_Lib_Dir);
fp = slrn_open_home_file (file, "r", line, 0);
#endif
}
if (fp == NULL) return -1;
}
while (NULL != fgets (line, sizeof (line) - 1, fp))
{
unsigned char *bmax, *dsc, ch;
bmax = (unsigned char *) line;
while (((ch = *bmax) != ':') && ch) bmax++;
if (ch <= ' ') continue;
*bmax = 0;
dsc = bmax + 1;
add_group_description ((unsigned char *) line, bmax, dsc);
}
slrn_fclose (fp);
return 0;
}
int Slrn_Unsubscribe_New_Groups = 0;
void slrn_check_new_groups (int create_flag)
{
FILE *fp;
time_t tloc;
struct tm *tm_struct;
char line[SLRN_MAX_PATH_LEN];
char file[SLRN_MAX_PATH_LEN];
int num;
char *p;
int parse_error = 0;
#ifdef VMS
sprintf (file, "%s-time", Slrn_Newsrc_File);
#else
#ifdef __os2__
sprintf (file, "tm-%s", Slrn_Newsrc_File);
#else
sprintf (file, "%s.time", Slrn_Newsrc_File);
#endif
#endif
if ((create_flag == 0)
&& (NULL != (fp = slrn_open_home_file (file, "r", line, 0))))
{
char ch;
int i;
*line = 0;
fgets (line, sizeof (line), fp);
slrn_fclose (fp);
time (&tloc);
parse_error = 1;
/* parse this line to make sure it is ok. If it is bad, issue a warning
* and go on.
*/
if (strncmp ("NEWGROUPS ", line, 10)) goto parse_error_label;
p = line + 10;
p = slrn_skip_whitespace (p);
/* parse yymmdd */
for (i = 0; i < 6; i++)
{
ch = p[i];
if ((ch < '0') || (ch > '9')) goto parse_error_label;
}
if (p[6] != ' ') goto parse_error_label;
ch = p[2];
if (ch > '1') goto parse_error_label;
if ((ch == '1') && (p[3] > '2')) goto parse_error_label;
ch = p[4];
if (ch > '3') goto parse_error_label;
if ((ch == '3') && (p[5] > '1')) goto parse_error_label;
/* Now the hour: hhmmss */
p = slrn_skip_whitespace (p + 6);
for (i = 0; i < 6; i++)
{
ch = p[i];
if ((ch < '0') || (ch > '9')) goto parse_error_label;
}
ch = p[0];
if (ch > '2') goto parse_error_label;
if ((ch == '2') && (p[1] > '3')) goto parse_error_label;
if ((p[2] > '5') || (p[4] > '5')) goto parse_error_label;
p = slrn_skip_whitespace (p + 6);
if ((p[0] == 'G') && (p[1] == 'M') && (p[2] == 'T'))
p += 3;
*p = 0;
parse_error = 0;
switch (Slrn_Server_Obj->sv_put_server_cmd (line, line, sizeof (line)))
{
case OK_NEWGROUPS:
break;
case ERR_FAULT:
slrn_message ("Server does not implement NEWGROUPS command.");
return;
default:
slrn_message ("Server failed to return proper response to NEWGROUPS:\n%s\n",
line);
goto parse_error_label;
}
num = 0;
while (NULL != Slrn_Server_Obj->sv_read_line (line, sizeof (line)))
{
/* line contains new newsgroup name */
add_unsubscribed_group (line);
num++;
}
if (num)
{
if (Slrn_TT_Initialized & 1)
{
slrn_message ("%d new newsgroup(s) found.", num);
slrn_smg_refresh ();
}
else
slrn_tty_message (2, "%d new newsgroup(s) found.", num);
}
}
else time (&tloc);
parse_error_label:
if (parse_error)
{
slrn_message ("\
%s appears corrupt.\n\
I expected to see see: NEWGROUPS yymmdd hhmmss GMT\n\
I will patch the file up for you.\n", file);
}
if (NULL == (fp = slrn_open_home_file (file, "w", line, 1)))
{
slrn_exit_error ("Unable to open %s to record date.", line);
}
#ifdef VMS
tm_struct = localtime (&tloc);
fprintf (fp, "NEWGROUPS %02d%02d%02d %02d%02d%02d",
tm_struct->tm_year, 1 + tm_struct->tm_mon,
tm_struct->tm_mday, tm_struct->tm_hour,
tm_struct->tm_min, tm_struct->tm_sec);
#else
tm_struct = gmtime (&tloc);
fprintf (fp, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT",
tm_struct->tm_year, 1 + tm_struct->tm_mon,
tm_struct->tm_mday, tm_struct->tm_hour,
tm_struct->tm_min, tm_struct->tm_sec);
#endif
slrn_fclose (fp);
}
static Slrn_Group_Type *place_group_in_newsrc_order (Slrn_Group_Type *last_group)
{
Slrn_Group_Type *next_group, *prev_group;
next_group = Current_Group->next;
prev_group = Current_Group->prev;
if (next_group != NULL) next_group->prev = prev_group;
if (prev_group != NULL) prev_group->next = next_group;
Current_Group->prev = last_group;
if (last_group != NULL)
{
Current_Group->next = last_group->next;
if (Current_Group->next != NULL)
Current_Group->next->prev = Current_Group;
last_group->next = Current_Group;
}
else if (Current_Group != Groups)
{
Current_Group->next = Groups;
if (Groups != NULL) Groups->prev = Current_Group;
Groups = Current_Group;
}
return Current_Group;
}
int slrn_read_newsrc (int create_flag)
{
char file[SLRN_MAX_PATH_LEN];
FILE *fp = NULL;
char line[NNTP_BUFFER_SIZE];
register char *p, ch;
Line_Num = 0;
if (create_flag)
{
if (NULL == (fp = slrn_open_home_file (Slrn_Newsrc_File, "w", file, 1)))
{
slrn_exit_error ("Unable to create %s.", file);
}
fputs ("\n--The next step may take a while if the NNTP connection is slow.--\n\n", stdout);
fprintf (stdout, "Creating %s.", file);
fflush (stdout);
}
if (create_flag || Slrn_List_Active_File)
{
int count = 0;
(void) Slrn_Server_Obj->sv_list_active ();
while (NULL != Slrn_Server_Obj->sv_read_line (line, sizeof(line)))
{
unsigned int len;
int min, max;
parse_active_line (line, &len, &min, &max);
if (NULL == create_group_entry (line, len, min, max, 0, 0))
continue;
if (create_flag)
{
count++;
count = count % 50;
if (count == 0)
{
putc ('.', stdout);
fflush (stdout);
}
if ((*line == 'n')
&& (!strncmp (line, "news.newusers.questions", 23)
|| !strncmp (line, "news.groups.questions", 21)
|| !strncmp (line, "news.answers", 12)
|| !strncmp (line, "news.announce.newusers", 22)
|| !strncmp (line, "news.software.readers", 21)))
{
add_group (line, len, 0, 1);
}
else if ((*line == 'a')
&& (!strncmp (line, "alt.test", 8)))
{
add_group (line, len, 0, 1);
}
else add_group (line, len, GROUP_UNSUBSCRIBED, 1);
}
}
}
if (create_flag == 0)
{
Slrn_Group_Type *last_group = NULL;
if (Unsubscribed_Groups != NULL)
{
unsigned int subscribe_flag;
Unsubscribed_Slrn_Group_Type *ug = Unsubscribed_Groups, *ugnext;
if (Slrn_Unsubscribe_New_Groups)
subscribe_flag = GROUP_UNSUBSCRIBED | GROUP_NEW_GROUP_FLAG;
else subscribe_flag = GROUP_NEW_GROUP_FLAG;
while (ug != NULL)
{
ugnext = ug->next;
if ((-1 != add_group (ug->name, strlen (ug->name), subscribe_flag, 0))
&& Slrn_List_Active_File)
last_group = place_group_in_newsrc_order (last_group);
SLFREE (ug);
ug = ugnext;
}
Unsubscribed_Groups = NULL;
}
if ((NULL == (fp = slrn_open_home_file (Slrn_Newsrc_File, "r", file, 0)))
&& (NULL == (fp = slrn_open_home_file (".newsrc", "r", file, 0))))
{
sprintf (line, "Unable to open %s.", file);
slrn_exit_error (line);
}
while (fgets (line, sizeof(line) - 1, fp) != NULL)
{
p = line;
while (((ch = *p) != '!') && (ch != ':') && (ch != 0))
{
p++;
}
if ((ch == 0) || (p == line)) continue;
if (-1 == add_group (line, (unsigned int) (p - line),
((ch == '!') ? GROUP_UNSUBSCRIBED : 0),
0))
continue;
/* perform a re-arrangement to match arrangement in the
* newsrc file
*/
if (Slrn_List_Active_File && (ch != '!'))
{
last_group = place_group_in_newsrc_order (last_group);
}
}
}
slrn_fclose (fp);
Current_Group = Groups;
find_line_num ();
toggle_hide_groups ();
/* Unhide the new groups. Do it here so that if there are no unread
* articles, it will be visible but also enables user to toggle them
* so that they will become invisble again.
*/
Current_Group = Groups;
while ((Current_Group != NULL)
&& (Current_Group->flags & GROUP_NEW_GROUP_FLAG))
{
Current_Group->flags &= ~GROUP_HIDDEN;
Current_Group = Current_Group->next;
}
Current_Group = Groups;
find_line_num ();
if (create_flag)
{
/* toggle_list_all_groups1 (0); */
}
if (create_flag) Slrn_Groups_Dirty = 1;
group_bob ();
return 0;
}
int slrn_select_group_mode (void)
{
Slrn_Current_Mode = &Group_Mode_Cap;
quick_help ();
Last_Cursor_Row = 0;
Top_Group = NULL;
Slrn_Full_Screen_Update = 1;
return 0;
}
/* ----------------------- update screen for group mode ---------- */
static void group_update_screen (void)
{
Slrn_Group_Type *g;
int height = Slrn_Group_Window_Size;
int row;
int curr_row = 0;
/* erase last cursor */
if (Last_Cursor_Row && !Slrn_Full_Screen_Update)
{
SLsmg_gotorc (Last_Cursor_Row, 0);
SLsmg_write_string (" ");
}
g = Top_Group;
/* Find out if the group is already visible */
row = 0;
while ((g != NULL) && (g != Current_Group) && (row < height))
{
if (g->flags & GROUP_HIDDEN)
{
while ((g != NULL) && (g->flags & GROUP_HIDDEN))
{
g = g->next;
}
}
else
{
g = g->next;
row++;
}
}
if (row == height) g = NULL;
Last_Cursor_Row = row + 1;
if (g == NULL)
{
Top_Group = g = Current_Group;
Slrn_Full_Screen_Update = 1;
}
else g = Top_Group;
for (row = 0; row < height; row++)
{
while ((g != NULL) && (g->flags & GROUP_HIDDEN))
g = g->next;
if (g == Current_Group)
{
curr_row = row;
}
if (g != NULL)
{
if (Slrn_Full_Screen_Update || (g->flags & GROUP_TOUCHED))
{
SLsmg_gotorc (row + 1, 0);
SLsmg_printf (" %c%5d ",
((g->flags & GROUP_UNSUBSCRIBED) ? 'U'
: ((g->flags & GROUP_NEW_GROUP_FLAG) ? 'N' : ' ')),
g->unread);
slrn_set_color (GROUP_COLOR);
SLsmg_printf ("%s", g->name);
slrn_set_color (0);
SLsmg_erase_eol ();
if (Slrn_Group_Display_Descriptions)
{
if (g->descript != NULL)
{
int dsc_row;
dsc_row = SLsmg_get_column ();
if (dsc_row < Slrn_Group_Description_Column)
dsc_row = Slrn_Group_Description_Column;
else dsc_row++;
SLsmg_gotorc (row + 1, dsc_row);
SLsmg_set_color (GROUP_DESCR_COLOR);
SLsmg_write_string (g->descript);
SLsmg_set_color (0);
}
}
else if (g->range.max != -1)
{
SLsmg_gotorc (row + 1, 63);
SLsmg_printf ("%7d-%-7d", g->range.min, g->range.max);
}
g->flags &= ~GROUP_TOUCHED;
if (g == Current_Group) Last_Cursor_Row = row + 1;
}
Bottom_Group = g;
g = g->next;
}
else if (Slrn_Full_Screen_Update)
{
SLsmg_gotorc (row + 1, 0);
SLsmg_erase_eol ();
Bottom_Group = NULL;
}
}
SLsmg_gotorc (SLtt_Screen_Rows - 2, 0);
slrn_set_color (STATUS_COLOR);
if (Slrn_Groups_Dirty)
SLsmg_write_string ("-*-News Groups: ");
else SLsmg_write_string ("---News Groups: ");
if (Slrn_Server_Obj->sv_name != NULL)
SLsmg_write_string (Slrn_Server_Obj->sv_name);
slrn_print_percent (SLtt_Screen_Rows - 2, 60, Line_Num, Num_Groups, height - curr_row);
if (Slrn_Use_Mouse) slrn_update_group_menu ();
else update_top_status_line ();
if (Slrn_Message_Present == 0) quick_help ();
SLsmg_gotorc (Last_Cursor_Row, 0);
slrn_set_color (CURSOR_COLOR);
SLsmg_write_string ("->");
slrn_set_color (0);
Slrn_Full_Screen_Update = 0;
}
char *slrn_current_group (void)
{
if (Current_Group == NULL)
return "";
return Current_Group->name;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.