home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Professional
/
OS2PRO194.ISO
/
os2
/
com
/
utils
/
smplnews
/
snews.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-08
|
39KB
|
1,466 lines
/*
SNEWS 2.0
snews - a simple threaded news reader
Copyright (C) 1991 John McCombs, Christchurch, NEW ZEALAND
john@ahuriri.gen.nz
PO Box 2708, Christchurch, NEW ZEALAND
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 1, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
See the file COPYING, which contains a copy of the GNU General
Public License.
*/
#include "defs.h"
#include "snews.h"
INFO my_stuff;
char iobuf[IOBUFSIZE];
char search_text[256];
/*------------------------------- main --------------------------------*/
void main(void)
{
ACTIVE *gp, *head;
int done;
ignore_signals();
printf("loading config...\n");
if (load_stuff()) {
printf("loading active...\n");
head = load_active_file();
printf("loading read list...\n");
load_read_list();
printf("loading history...\n");
load_history_list();
scrinit();
clrscr();
done = FALSE;
gp = NULL;
while (!done) {
if ((gp = select_group(head, gp)) != NULL) {
done |= read_group(gp);
} else {
done = TRUE;
}
}
screxit();
clrscr();
free_hist_list();
save_read_list();
close_active_file();
} else {
fprintf(stderr, "Couldn't find necessary item in the .rc files\n");
}
}
/*-------------------------- find which group to read -----------------------*/
ACTIVE *select_group(ACTIVE *head, ACTIVE *current)
{
/*
* Present the list of groups, and allow him to move up and down with
* the arrow and PgUp and PgDn keys.
*/
ACTIVE *top; /* newsgroup at the top of the page */
ACTIVE *this; /* current newsgroup */
ACTIVE *tmp_ng;
int exit_code; /* why we are exiting the loop */
int ch, i, j, articles, unread;
this = (current == NULL) ? head : current;
top = head;
exit_code = 0;
show_groups(&top, this, TRUE, head);
while (exit_code == 0) {
ch = getch();
switch (ch) {
case 0 :
case 0xE0 :
ch = getch();
switch (ch) {
case F1 :
show_help(HELP_GROUP);
show_groups(&top, this, TRUE, head);
break;
case UP_ARR :
if (this->last != NULL) this = this->last;
break;
case DN_ARR :
if (this->next != NULL) this = this->next;
break;
case PGUP :
for (i = 0; i < PAGE_LENGTH; i++) {
if (this->last == NULL) break;
this = this->last;
}
break;
case PGDN :
for (i = 0; i < PAGE_LENGTH; i++) {
if (this->next == NULL) break;
this = this->next;
}
break;
case HOME :
top = this = head;
show_groups(&top, this, TRUE, head);
break;
case END :
this = head;
while (this->next != NULL)
this = this->next;
break;
}
break;
case 'p' :
case 'P' :
post(NULL, this->group);
show_groups(&top, this, TRUE, head);
break;
case 'h' :
case 'H' :
show_help(HELP_GROUP);
show_groups(&top, this, TRUE, head);
break;
case 'c' :
case 'C' :
mark_group_as_read(this);
show_groups(&top, this, TRUE, head);
break;
case TAB :
tmp_ng = this->next;
unread = 0;
while (tmp_ng != NULL) {
articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
for (j = 0; j < articles; j++) {
if ( *((tmp_ng->read_list)+j) == FALSE)
unread++;
}
if (unread > 0) break;
tmp_ng = tmp_ng->next;
}
if (unread > 0) {
this = tmp_ng;
} else {
message("-- No more articles to read --");
}
break;
case ENTER :
exit_code = EX_DONE;
break;
case ESCAPE :
exit_code = EX_QUIT;
break;
};
if (exit_code == 0)
show_groups(&top, this, FALSE, head);
}
if (exit_code == EX_DONE)
return(this);
else
return(NULL);
}
/*---------------------------- help screen ----------------------------------*/
void help(char *text)
{
printf("%*s", _columns / 2 - 40, "");
while (*text) {
if (*text == '\n')
clreol(), _line++;
putch(*text++);
}
}
void show_help(int h)
{
char *type[] = {"Group Help", "Thread Help", "Article Help"};
char *msg = COPYRIGHT;
int x;
gotoxy(1,1);
textbackground(LIGHTGRAY); textcolor(BLACK);
printf("%-18s %*s", type[h], _columns - 19, VERSION);
gotoxy(1,2);
x = _columns / 2 - strlen(msg) / 2;
printf("%*s%s", x, "", msg);
clreol();
textbackground(BLACK); textcolor(LIGHTGRAY);
putch('\n');
help("\n");
help(" PageUp - move display up one page\n");
help(" PageDn - move display down one page\n");
help(" Home - move to top of list/article\n");
help(" End - move to bottom of list/article\n");
help(" Up - move up one line\n");
help(" Down - move down one line\n");
switch (h) {
case HELP_GROUP :
help("\n");
help(" Tab - go to next newgroup with unread articles\n");
help(" Enter - enter selected newsgroup\n\n");
help(" c - mark all articles in selected group as read\n");
help(" p - post article to selected group\n");
break;
case HELP_THREAD :
help("\n");
help(" Tab - go to next thread with unread articles or\n");
help(" enter this thread if it is already selected\n");
help(" and move to the first unread article in the thread\n");
help(" Enter - read the selected thread from the beginning\n");
help(" Backspace - go to the last article in the selected thread\n\n");
help(" c - mark all articles in selected thread as read\n");
help(" s - save selected thread to disk\n");
help(" p - post article to this group\n\n");
help(" + - search for text in subjects\n");
help(" / - search for text in article bodies\n");
break;
case HELP_ARTICLES:
help(" Left - scroll right\n");
help(" Right - scroll left\n\n");
help(" Tab - go to next unread article\n");
help(" Enter - go to next article in thread\n");
help(" Backspace - go to previous article in thread\n\n");
help(" s - save this article to disk\n");
help(" p - post new article to current group\n");
help(" f - post follow-up to this article\n");
help(" r - reply to sender of this article via e-mail\n");
help(" m - mail this article to someone\n");
help(" + / - search for text in following articles\n");
help(" - ? - search for text in preceding articles\n");
break;
};
clreos();
message("-- Press any key to continue --");
getch();
}
/*-------------------- show the list of active groups -----------------------*/
void show_groups(ACTIVE **top, ACTIVE *this, int force, ACTIVE *head)
{
/*
* This routine takes 'top', a pointer to the first line on the screen
* and 'this' a pointer to where we want to be, and updates the screen.
* A maker to this is maintained, and the screen is repainted, where
* necessary
*/
static last_y;
static last_index;
int i, ur;
ACTIVE *that;
char buf[32];
/*
* If 'this' is above the 'top' or it is more than a screen length below,
* or'this and 'top' are both zero, ie first time repaint the screen
*/
/* if ((((*top)->index == 0) && (this->index == 0)) || force ||
((*top)->index > this->index) ||
(this->index - (*top)->index) > PAGE_LENGTH-1) { */
if ( force ||
((*top)->index > this->index) ||
(this->index - (*top)->index) > PAGE_LENGTH-1) {
gotoxy(1,1);
textbackground(LIGHTGRAY); textcolor(BLACK);
sprintf(buf, "Group %d of %d", last_index + 1, head->groups);
printf("Select Group %*s", _columns - 13, buf);
gotoxy(1,2);
clreol();
textbackground(BLACK); textcolor(LIGHTGRAY);
/* now adjust the top */
*top = this;
for (i = 0; i < PAGE_LENGTH/2; i++) {
if ((*top)->last == NULL) break;
*top = (*top)->last;
}
that = *top;
for (i = 0; i < PAGE_LENGTH+1; i++) {
gotoxy(1, i+PAGE_HEADER);
if (that == NULL) break;
ur = count_unread_in_group(that);
if (ur > 0)
printf(" %4d %4ld %s", ur,
that->hi_num - that->lo_num, that->group);
else
printf(" %4ld %s",
that->hi_num - that->lo_num, that->group);
clreol();
that = that->next;
}
clreos();
last_y = this->index - (*top)->index;
gotoxy(2, last_y + PAGE_HEADER);
puts("->");
last_index = this->index;
} else {
gotoxy(2, last_y + PAGE_HEADER);
puts(" ");
last_y += (this->index - last_index);
gotoxy(2, last_y + PAGE_HEADER);
puts("->");
last_index = this->index;
}
gotoxy(_columns - 16,1);
textbackground(LIGHTGRAY); textcolor(BLACK);
sprintf(buf, "Group %d of %d", last_index + 1, head->groups);
printf("%17s", buf);
textbackground(BLACK); textcolor(LIGHTGRAY);
message("ESC=quit TAB=next unread group ENTER=read group F1=help");
}
/*--------------------------- process message -------------------------------*/
int read_group(ACTIVE *gp)
{
/*
* We now have newsgroup. Access the directory and try to read
* the newsgroup articles, extracting the headers.
*/
ARTICLE *start;
if (gp->lo_num < gp->hi_num) {
start = get_headers(gp);
select_thread(gp, start);
free_header(start);
}
return(0);
}
/*-------------------- show the list of active groups -----------------------*/
void show_threads(ACTIVE *gp, ARTICLE **top, ARTICLE *this, int force,
ARTICLE *head)
{
/*
* This routine takes 'top', a pointer to the first line on the screen
* and 'this' a pointer to where we want to be, and updates the screen.
* A maker to this is maintained, and the screen is repainted, where
* necessary
*/
static last_y;
static last_index;
int i;
ARTICLE *that;
int ur;
char buf[32];
/*
* If 'this' is above the 'top' or it is more than a screen length below,
* or'this and 'top' are both zero, ie first time repaint the screen
*/
/* if ((((*top)->index == 0) && (this->index == 0)) || force ||
((*top)->index > this->index) ||
(this->index - (*top)->index) > PAGE_LENGTH-1) { */
if ( force ||
((*top)->index > this->index) ||
(this->index - (*top)->index) > PAGE_LENGTH-1) {
for (that = head, ur = 0; that; that = that->next)
ur += count_unread_in_thread(gp, that);
gotoxy(1,1);
textbackground(LIGHTGRAY); textcolor(BLACK);
sprintf(buf, "Thread %d of %d", last_index + 1, gp->threads);
printf("Select Thread %*s", _columns - 14, buf);
gotoxy(1,2);
sprintf(buf, "%ld Articles, %d unread",
gp->hi_num - gp->lo_num, ur);
printf("Group: %-*.*s %26s", _columns - 34, _columns - 34,
gp->group, buf);
textbackground(BLACK); textcolor(LIGHTGRAY);
/* now adjust the top */
*top = this;
for (i = 0; i < PAGE_LENGTH/2; i++) {
if ((*top)->last == NULL) break;
*top = (*top)->last;
}
that = *top;
for (i = 0; i < PAGE_LENGTH+1; i++) {
gotoxy(1, i+PAGE_HEADER);
if (that == NULL) break;
ur = count_unread_in_thread(gp, that);
if (ur > 0)
printf(" %4d %4d %s", ur,
that->num_articles, that->header);
else
printf(" %4d %s",
that->num_articles, that->header);
clreol();
that = that->next;
}
clreos();
last_y = this->index - (*top)->index;
gotoxy(2, last_y + PAGE_HEADER);
puts("->");
last_index = this->index;
} else {
gotoxy(2, last_y + PAGE_HEADER);
puts(" ");
last_y += (this->index - last_index);
gotoxy(2, last_y + PAGE_HEADER);
puts("->");
last_index = this->index;
gotoxy(_columns - 17,1);
textbackground(LIGHTGRAY); textcolor(BLACK);
sprintf(buf, "Thread %d of %d", last_index + 1, gp->threads);
printf("%18s", buf);
textbackground(BLACK); textcolor(LIGHTGRAY);
}
message("ESC=select group TAB=next unread ENTER=next article F1=help");
}
/*--------------- search through threads ------------------------------------*/
ARTICLE *search_thread(ACTIVE *gp, ARTICLE *head, int search_body)
{
int offset = 0, found = FALSE, irq = FALSE;
ARTICLE *this;
ART_ID *art;
TEXT *tx;
LINE *text;
char *fn;
char prompt[128], pattern[128];
if (head == NULL || head->next == NULL)
return head;
sprintf(prompt, "Search %s for? [%s] ",
search_body ? "articles" : "subjects", search_text);
lmessage(prompt);
if (gets(pattern) == NULL)
return head;
if (strlen(pattern) > 0)
strcpy(search_text, pattern);
else
strcpy(pattern, search_text);
strlwr(pattern);
if (search_body) {
message("*** searching - please wait (press ESC to abort) ***");
tflush();
for (this = head->next; this != NULL; this = this->next) {
/* for each article */
for (art = this->art_num; art != NULL; art = art->next_art) {
fn = make_news_group_name(gp->group);
tx = load_article(fn, art->art_off);
for (text = tx->start; !found && text != NULL;
text = text->next) {
strlwr(text->data);
found = (strstr(text->data, pattern) != NULL);
}
free_article(tx);
if (kbhit())
irq = (getch() == 27);
if (found || irq)
break;
}
if (found || irq)
break;
}
} else {
for (this = head->next; this != NULL; this = this->next) {
strcpy(prompt, this->header);
strlwr(prompt);
found = (strstr(prompt, pattern) != NULL);
if (found)
break;
}
}
if (!found && !irq) {
sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
message(prompt);
getch();
}
return found ? this : head;
}
/*-------------------------- find which thread to read ----------------------*/
void select_thread(ACTIVE *gp, ARTICLE *head)
{
/*
* Present the list of threads, and allow him to move up and down with
* the arrow and PgUp and PgDn keys.
*/
ARTICLE *top; /* thread at the top of the page */
ARTICLE *this; /* current thread */
ARTICLE *th;
ART_ID *art;
int exit_code; /* why we are exiting the loop */
int ch, i, idx, hit, a_ct;
this = head;
top = head;
exit_code = 0;
show_threads(gp, &top, this, TRUE, head);
while (exit_code == 0) {
ch = getch();
switch (ch) {
case 0 :
case 0xE0 :
ch = getch();
switch (ch) {
case F1 :
show_help(HELP_THREAD);
show_threads(gp, &top, this, TRUE, head);
break;
case UP_ARR :
if (this->last != NULL) this = this->last;
break;
case DN_ARR :
if (this->next != NULL) this = this->next;
break;
case PGUP :
for (i = 0; i < PAGE_LENGTH; i++) {
if (this->last == NULL) break;
this = this->last;
}
break;
case PGDN :
for (i = 0; i < PAGE_LENGTH; i++) {
if (this->next == NULL) break;
this = this->next;
}
break;
case HOME :
top = this = head;
show_threads(gp, &top, this, TRUE, head);
break;
case END :
this = head;
while (this->next != NULL)
this = this->next;
break;
}
break;
case 's' :
case 'S' :
save_thread_to_disk(gp, this);
break;
case 'p' :
case 'P' :
post(NULL, gp->group);
show_threads(gp, &top, this, TRUE, head);
break;
case 'h' :
case 'H' :
show_help(HELP_THREAD);
show_threads(gp, &top, this, TRUE, head);
break;
case TAB :
/*
* Go to the next unread article. Work through each
* thread, looking at each article to see if it's been
* read
*/
/* for each thread */
th = this;
hit = FALSE;
while (th != NULL) {
art = th->art_num;
a_ct = 0;
/* for each article */
while (art != NULL) {
idx = (int)(art->id - gp->lo_num - 1);
a_ct++;
if ( *((gp->read_list)+idx) == FALSE) {
hit = TRUE;
break;
}
art = art->next_art;
}
if (hit) break;
th = th->next;
}
if (hit) {
if (this == th )
read_thread(gp, this, art, a_ct);
this = th;
show_threads(gp, &top, this, TRUE, head);
}
break;
case 'c' :
case 'C' :
mark_thread_as_read(gp, this);
show_threads(gp, &top, this, TRUE, head);
break;
case ENTER :
read_thread(gp, this, this->art_num, 1);
show_threads(gp, &top, this, TRUE, head);
break;
case BACKSP :
art = this->art_num;
a_ct = 1;
while (art->next_art != NULL) {
a_ct++;
art = art->next_art;
}
read_thread(gp, this, art, a_ct);
show_threads(gp, &top, this, TRUE, head);
break;
case '+' :
case '/' :
this = search_thread(gp, this, ch == '/');
show_threads(gp, &top, this, TRUE, head);
break;
case ESCAPE :
exit_code = EX_QUIT;
break;
};
if (exit_code == 0)
show_threads(gp, &top, this, FALSE, head);
}
}
/*------------------------ save a thread ------------------------------*/
int cmp(TEXT **art1, TEXT **art2)
{
char *subj1 = (*art1) -> subject, *subj2 = (*art2) -> subject;
while (isspace(*subj1)) subj1++;
while (isspace(*subj2)) subj2++;
return strcmp(subj1, subj2);
}
void save_thread_to_disk(ACTIVE *gp, ARTICLE *this)
{
ART_ID *id;
char *fn;
TEXT *tx;
LINE *ln;
char fnx[256];
int ch, art, idx;
TEXT *text[MAXART];
FILE *tmp = NULL;
time_t now;
struct tm *tmnow;
char timestr[64];
lmessage("Enter filename? ");
gets(fnx);
if (access(fnx, 0) == 0) {
message("File exists - append(y/n)? ");
while (((ch = getch()) != 'y') && (ch != 'n'));
if (ch == 'y') {
if ((tmp = fopen(fnx, "at")) == NULL) {
message("*** cannot open file for appending - "
"please any key ***");
getch();
}
}
} else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** cannot open file for output - press any key ***");
getch();
}
setvbuf(tmp, iobuf, _IOFBF, IOBUFSIZE);
}
if (tmp != NULL) {
fn = make_news_group_name(gp->group);
id = this->art_num;
idx = 0;
while (id != NULL && idx < MAXART) {
text[idx++] = load_article(fn, id->art_off);
id = id->next_art;
}
qsort(text, idx, sizeof(TEXT *), cmp);
time(&now);
tmnow = localtime(&now);
strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %z %Y", tmnow);
for ( art = 0; art < idx; art++ ) {
tx = text[art];
ln = tx->top;
while (ln != NULL && strncmp(ln->data, "Path: ", 6)) {
ln = ln->next;
}
strcpy(fn, ln->data + 6);
for ( ch = strlen(fn); fn[ch - 1] == '\n' || fn[ch - 1] == '\r'; ch--)
fn[ch - 1] = 0;
fprintf(tmp, "From %s %s\n", fn, timestr);
ln = tx->top;
while (ln != NULL) {
fputs(ln->data, tmp);
ln = ln->next;
}
fputs("\n\n", tmp);
free_article(tx);
}
fclose(tmp);
}
}
/*------------------------ search through articles --------------------*/
int search_message(char *fn, ART_ID *current, int forward)
{
int offset = 0, found = FALSE;
TEXT *tx;
LINE *text;
char prompt[128], pattern[128];
sprintf(prompt, "Search %s for? [%s] ",
forward ? "forward" : "backward", search_text);
lmessage(prompt);
if (gets(pattern) == NULL)
return 0;
if (strlen(pattern) > 0)
strcpy(search_text, pattern);
else
strcpy(pattern, search_text);
strlwr(pattern);
for (;;) {
current = forward ? current->next_art : current->last_art;
if (current == NULL)
break;
offset++;
tx = load_article(fn, current->art_off);
for (text = tx->start; !found && text != NULL; text = text->next) {
strlwr(text->data);
found = (strstr(text->data, pattern) != NULL);
}
free_article(tx);
if (found)
break;
}
if (!found) {
sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
message(prompt);
getch();
}
return found ? offset : 0;
}
/*------------------------ read a thread ------------------------------*/
int read_thread(ACTIVE *gp, ARTICLE *this, ART_ID *first, int a_ct)
{
ART_ID *id;
char *fn;
int idx, res;
TEXT *tx;
char author[128], msg_id[128];
CROSS_POSTS *h, *h0;
ACTIVE *gx;
fn = make_news_group_name(gp->group);
id = first;
while (id != NULL) {
tx = load_article(fn, id->art_off);
res = read_article(gp, tx, a_ct, this->num_articles);
/* mark this article as read */
idx = (int) ((id->id) - gp->lo_num - 1);
(gp->read_list)[idx] = TRUE;
/* mark the crossposts */
get_his_stuff(tx, author, msg_id);
if ((h0 = look_up_history(msg_id, gp->group)) != NULL) {
h = h0;
while (h != NULL) {
gx = find_news_group(h->group);
idx = (int) ((h->art_num) - gx->lo_num - 1);
if ( 0 <= idx && idx < gx->hi_num )
(gx->read_list)[idx] = TRUE;
h = h->next;
}
free_cross_post_list(h0);
}
free_article(tx);
if (res == EX_QUIT) break;
if (res == EX_NEXT_UNREAD) {
while (id != NULL) {
idx = (int)(id->id - gp->lo_num - 1);
if ( *((gp->read_list)+idx) == FALSE) {
break;
}
a_ct++;
id = id->next_art;
}
} else if (res == EX_SEARCH_FORW) {
res = search_message(fn, id, 1);
while (res--) {
a_ct++;
id = id->next_art;
}
} else if (res == EX_SEARCH_BACKW) {
res = search_message(fn, id, 0);
while (res--) {
a_ct--;
id = id->last_art;
}
} else if (res == EX_PREVIOUS) {
a_ct--;
id = id->last_art;
} else {
a_ct++;
id = id->next_art;
}
}
return(res);
}
/*------------------------- read the headers --------------------------*/
int strip_off_part(char *str)
{
char *ptr = str + strlen(str);
/* strip off (case-insensitive) things like:
- "Part01/10"
- "Part 01/10"
- "Part 01 of 10"
- "[1/10]"
- "(1 of 10)"
- "1 of 10"
- "Patch02a/04"
- "Patch20"
*/
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str && (ptr[-1] == ')' || ptr[-1] == ']') )
ptr--;
while ( ptr > str && isdigit(ptr[-1]) )
ptr--;
if ( !isdigit(*ptr) )
return 0;
if ( ptr > str && ptr[-1] == '/' )
ptr--;
else if ( ptr > str + 3 && strnicmp(ptr - 4, " of ", 4) == 0 )
ptr -= 4;
else if ( ptr > str + 4 && strnicmp(ptr - 5, "Patch", 5) == 0 )
{
ptr -= 5;
goto label;
}
else if ( ptr > str + 5 && strnicmp(ptr - 6, "Patch ", 6) == 0 )
{
ptr -= 6;
goto label;
}
else
return 0;
if ( ptr > str && 'a' <= ptr[-1] && ptr[-1] <= 'z' )
ptr--;
while ( ptr > str && isdigit(ptr[-1]) )
ptr--;
if ( !isdigit(*ptr) )
return 0;
if ( ptr > str && (ptr[-1] == '(' || ptr[-1] == '[') )
ptr--;
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str + 3 && strnicmp(ptr - 4, "Part", 4) == 0 )
ptr -= 4;
label:
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str && ptr[-1] == ',' )
ptr--;
else if ( ptr > str && ptr[-1] == ':' )
ptr--;
*ptr = 0;
return 1;
}
char *skip_vi(char *str)
{
char *ptr = str;
/* skip things like "v02i0027: " */
while ( isspace(*ptr) )
ptr++;
if ( *ptr++ != 'v' )
return str;
if ( !isdigit(*ptr) )
return str;
while ( isdigit(*ptr) )
ptr++;
if ( *ptr++ != 'i' )
return str;
if ( !isdigit(*ptr) )
return str;
while ( isdigit(*ptr) )
ptr++;
if ( *ptr++ != ':' )
return str;
if ( *ptr++ != ' ' )
return str;
while ( isspace(*ptr) )
ptr++;
return ptr;
}
int smartcmp(char *str1, char *str2)
{
char s1[256], s2[256];
strcpy(s1, str1);
strcpy(s2, str2);
if ( strip_off_part(s1) && strip_off_part(s2) )
{
str1 = skip_vi(s1);
str2 = skip_vi(s2);
}
return stricmp(str1, str2);
}
ARTICLE *get_headers(ACTIVE *gp)
{
/*
* Read the files and get the headers
*/
char *fn;
char buf[MAXLINE], fnx[256], *buf_p;
long g, n_read;
FILE *tmp_file;
ARTICLE *start, *that, *tmp;
ARTICLE **ptr;
ART_ID *art_this, *new;
int ct_art, cmp;
n_read = 0;
ct_art = 0;
start = NULL;
fn = make_news_group_name(gp->group);
sprintf(fnx, "%s.IDX", fn);
if ((tmp_file = flockopen(fnx, "rb")) != NULL) {
setvbuf(tmp_file, iobuf, _IOFBF, IOBUFSIZE);
for (g = gp->lo_num+1; g <= gp->hi_num; g++) {
if ((n_read++ % 100) == 0) {
gotoxy(1,PAGE_SIZE);
textbackground(LIGHTGRAY); textcolor(BLACK);
printf("%d articles processed", n_read-1);
clreol();
textbackground(BLACK); textcolor(LIGHTGRAY);
tflush();
}
/*
* Read the index
* Search the linked list for the subject
* - allocate a new subject if necessary
* - add to the existing list
*/
if (fgets(buf, sizeof(buf), tmp_file) == NULL) {
gotoxy(1,PAGE_SIZE);
fprintf(stderr, "\nsnews: index file is corrupt\n");
exit(1);
}
/* check all is in sync */
if (g != atol(buf+9)) {
gotoxy(1,PAGE_SIZE);
fprintf(stderr, "\nsnews: article %ld found when %ld"
"expected\n", atol(buf+9), g);
exit(1);
}
/* skip the two eight digit numbers and the 9 and the spaces */
buf_p = buf+28;
eat_gunk(buf_p);
for ( tmp = start; tmp != NULL; ) {
cmp = smartcmp(buf_p, tmp->header);
if (cmp > 0) {
if (tmp->left)
tmp = tmp->left;
else {
ptr = &(tmp->left);
tmp = NULL;
break;
}
}
else if (cmp < 0) {
if (tmp->right)
tmp = tmp->right;
else {
ptr = &(tmp->right);
tmp = NULL;
break;
}
}
else {
/* found this subject */
tmp->num_articles++;
/* allocate new article number */
new = xmalloc(sizeof (ART_ID));
new->id = g;
new->art_off = atol(buf);
/* place it at the end */
art_this = tmp->art_num;
while (art_this->next_art != NULL) {
art_this = art_this->next_art;
}
art_this->next_art = new;
new->last_art = art_this;
new->next_art = NULL;
break;
}
}
if (tmp == NULL) {
/* not found - allocate new thread */
if (start == NULL) {
start = xmalloc(sizeof (ARTICLE));
that = start;
that->last = NULL;
} else {
ct_art++;
that->next = xmalloc(sizeof (ARTICLE));
that->next->last = that;
that = that->next;
*ptr = that;
}
that->next = NULL;
that->index = ct_art;
that->left = that->right = NULL;
/* store article data */
that->header = xmalloc(strlen(buf_p) + 1);
strcpy(that->header, buf_p);
that->num_articles = 1;
that->art_num = xmalloc(sizeof (ART_ID));
that->art_num->last_art = NULL;
that->art_num->next_art = NULL;
that->art_num->id = g;
that->art_num->art_off = atol(buf);
}
}
fclose(tmp_file);
} else {
gotoxy(1,PAGE_SIZE);
fprintf(stderr, "\nsnews: can't open index file %s\n", fnx);
exit(1);
}
gp->threads = ct_art + 1;
return(start);
}
/*------------------------ clean up subject line ----------------------------*/
void eat_gunk(char *buf)
{
/*
* This routine take the header line, and strips the
* word header word, 'Re:', and any extra blanks
*/
char *p, tmp[MAXLINE];
/* strip the Subject: etc off the front */
while ( /*(strstr(buf, "Re:") != NULL) ||*/
(strnicmp(buf, "From:", 5) == 0) ||
(strnicmp(buf, "Path:", 5) == 0) ||
(strnicmp(buf, "Subject:", 8) == 0) ||
(strnicmp(buf, "Organization:", 13) == 0) ||
(strnicmp(buf, "Organisation:", 13) == 0) ||
(strnicmp(buf, "Followup-To:", 12) == 0)) {
p = strchr(buf, ':');
strcpy(buf, p+1);
}
strcpy(tmp, buf);
*buf = '\x00';
p = strtok(tmp, " \n\r");
while (p != NULL) {
if (stricmp(p, "Re:") != 0) {
strcat(buf, p);
strcat(buf, " ");
}
p = strtok(NULL, " \n\r");
}
if (strlen(buf) <= 1)
strcpy(buf, "** no subject **");
}
/*-------------------- release the subject structures ---------------------*/
void free_header(ARTICLE *start)
{
/*
* Work our way through the subject structure releasing all the
* memory
*/
ARTICLE *a, *t;
ART_ID *u, *v;
a = start;
while (a != NULL) {
u = a->art_num;
while (u != NULL) {
v = u->next_art;
free(u);
u = v;
}
t = a->next;
free(a->header);
free(a);
a = t;
}
}
/*------------------- count unread articles in a thread --------------------*/
int count_unread_in_thread(ACTIVE *gp, ARTICLE *a)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
ART_ID *id;
int ct, idx;
ct = 0;
id = a->art_num;
while (id != NULL) {
idx = (int)(id->id - gp->lo_num - 1);
if (*((gp->read_list)+idx) == FALSE)
ct++;
id = id->next_art;
}
return(ct);
}
/*------------------- count unread articles in a group --------------------*/
int count_unread_in_group(ACTIVE *gp)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
int articles, ct, i;
ct = 0;
articles = (int)(gp->hi_num - gp->lo_num);
if (gp->read_list)
for (i = 0; i < articles; i++) {
if (*((gp->read_list)+i) == FALSE)
ct++;
}
return(ct);
}
/*------------------- mark articles as read ---------------------------------*/
void mark_group_as_read(ACTIVE *gp)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
int articles, i, ch;
message("Mark all articles in this group as read (y/n)? ");
while (((ch = getch()) != 'y') && (ch != 'n') && (ch != ESCAPE));
if (ch == 'y') {
articles = (int)(gp->hi_num - gp->lo_num);
for (i = 0; i < articles; i++)
*((gp->read_list)+i) = TRUE;
}
}
void mark_thread_as_read(ACTIVE *gp, ARTICLE *a)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
ART_ID *id;
int idx, ch;
message("Mark all articles in this thread as read (y/n)? ");
while (((ch = getch()) != 'y') && (ch != 'n') && (ch != ESCAPE));
if (ch == 'y') {
id = a->art_num;
while (id != NULL) {
idx = (int)(id->id - gp->lo_num - 1);
*((gp->read_list)+idx) = TRUE;
id = id->next_art;
}
}
}
/*-------------------------- status message ---------------------------------*/
void message(char *msg)
{
int x;
x = _columns / 2 - (strlen(msg)/2);
gotoxy(1,PAGE_SIZE);
textbackground(LIGHTGRAY); textcolor(BLACK);
printf("%*s%s", x, "", msg);
clreol();
textbackground(BLACK); textcolor(LIGHTGRAY);
}
/*-------------------------- status message ---------------------------------*/
void lmessage(char *msg)
{
gotoxy(1,PAGE_SIZE);
textbackground(LIGHTGRAY); textcolor(BLACK);
printf("%s", msg);
clreol();
textbackground(BLACK); textcolor(LIGHTGRAY);
}