home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Unix System Administration Handbook 1997 October
/
usah_oct97.iso
/
news
/
nn.tar
/
nn-6.5.1
/
articles.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-08-04
|
15KB
|
640 lines
/*
* (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
*
* Basic article access and management
*/
#include "config.h"
#include "db.h"
#include "articles.h"
#include "regexp.h"
#ifdef ART_GREP
#include "news.h"
#endif /* ART_GREP */
/* articles.c */
static void new_thunk __APROTO((thunk *t, char *ptr, long size));
static int grep_article __APROTO((group_header *gh, article_header *ah, char *pattern, int (*fcn)()));
export int seq_cross_filtering = 1;
export int select_leave_next = 0; /* ask to select left over art. */
export int ignore_re = 0;
export int body_search_header = 0;
import int ignore_fancy_select;
import int killed_articles;
extern attr_type test_article();
/*
* memory management
*/
static thunk
dummy_str_t = {
NULL,
NULL,
0L
},
dummy_art_t = {
NULL,
NULL,
0L
};
static thunk *first_str_t = &dummy_str_t;
static thunk *current_str_t = &dummy_str_t;
static thunk *first_art_t = &dummy_art_t;
static thunk *current_art_t = &dummy_art_t;
static long cur_str_size = 0, cur_art_size = 0;
static char *next_str;
static article_header *next_art;
static article_header **art_array = NULL;
static article_number max_articles = 0, mem_offset = 0;
/*
* allocate one article header
*/
#ifndef ART_THUNK_SIZE
#define ART_THUNK_SIZE 127
#endif
static void
new_thunk(t, ptr, size)
thunk *t;
char *ptr;
long size;
{
thunk *new;
new = newobj(thunk, 1);
new->next_thunk = t->next_thunk;
t->next_thunk = new;
new->this_thunk = ptr;
new->thunk_size = size;
}
article_header *alloc_art()
{
if (cur_art_size == 0) {
if (current_art_t->next_thunk == NULL)
new_thunk(current_art_t,
(char *)newobj(article_header, ART_THUNK_SIZE),
(long)ART_THUNK_SIZE);
current_art_t = current_art_t->next_thunk;
next_art = (article_header *)current_art_t->this_thunk;
cur_art_size = current_art_t->thunk_size;
}
cur_art_size--;
return next_art++;
}
/*
* allocate a string of length 'len'
*/
#ifndef STR_THUNK_SIZE
#define STR_THUNK_SIZE ((1<<14) - 32) /* leave room for malloc header */
#endif
char *alloc_str(len)
int len;
{
/* allow space for '\0', and align on word boundary */
int size = (len + sizeof(int)) & ~(sizeof(int) - 1);
char *ret;
if (cur_str_size < size) {
if (current_str_t->next_thunk == NULL)
new_thunk(current_str_t,
newstr(STR_THUNK_SIZE), (long)STR_THUNK_SIZE);
current_str_t = current_str_t->next_thunk;
next_str = current_str_t->this_thunk;
cur_str_size = current_str_t->thunk_size;
}
/* XXX else should do something reasonable */
ret = next_str;
cur_str_size -= size;
next_str += size;
ret[len] = NUL; /* ensure string is null terminated */
return ret;
}
/*
* "free" the allocated memory
*/
void
free_memory()
{
current_str_t = first_str_t;
current_art_t = first_art_t;
cur_str_size = 0;
cur_art_size = 0;
n_articles = 0;
}
/*
* mark/release memory
*/
void
mark_str(str_marker)
string_marker *str_marker;
{
str_marker->sm_cur_t = current_str_t;
str_marker->sm_size = cur_str_size;
str_marker->sm_next = next_str;
}
void
release_str(str_marker)
string_marker *str_marker;
{
current_str_t = str_marker->sm_cur_t;
cur_str_size = str_marker->sm_size;
next_str = str_marker->sm_next;
}
void
mark_memory(mem_marker)
memory_marker *mem_marker;
{
mark_str(&(mem_marker->mm_string));
mem_marker->mm_cur_t = current_art_t;
mem_marker->mm_size = cur_art_size;
mem_marker->mm_next = next_art;
mem_marker->mm_nart = n_articles;
mem_offset += n_articles;
n_articles = 0;
articles = art_array + mem_offset;
}
void
release_memory(mem_marker)
memory_marker *mem_marker;
{
release_str(&(mem_marker->mm_string));
current_art_t = mem_marker->mm_cur_t;
cur_art_size = mem_marker->mm_size;
next_art = mem_marker->mm_next;
n_articles = mem_marker->mm_nart;
mem_offset -= n_articles;
articles = art_array + mem_offset;
}
/*
* merge all memory chunks into one.
*/
void
merge_memory()
{
n_articles += mem_offset;
mem_offset = 0;
articles = art_array;
}
/*
* save article header in 'articles' array
* 'articles' is enlarged if too small
*/
#define FIRST_ART_ARRAY_SIZE 500 /* malloc header */
#define NEXT_ART_ARRAY_SIZE 512
void
add_article(art)
article_header *art;
{
if ((n_articles + mem_offset) == max_articles) {
/* must increase size of 'articles' */
if (max_articles == 0) {
/* allocate initial 'articles' array */
max_articles = FIRST_ART_ARRAY_SIZE;
} else {
max_articles += NEXT_ART_ARRAY_SIZE;
}
art_array = resizeobj(art_array, article_header *, max_articles);
articles = art_array + mem_offset;
}
articles[n_articles] = art;
n_articles++;
}
int
access_group(gh, first_article, last_article, flags, mask)
register group_header *gh;
article_number first_article, last_article;
register flag_type flags;
char *mask;
{
register article_header *ah;
int skip_digest, n;
group_header *cpgh;
#ifdef NOV
int dbstatus;
#else /* NOV */
FILE *data;
off_t data_offset, data_size;
#endif /* NOV */
cross_post_number cross_post;
attr_type leave_attr;
memory_marker mem_marker;
static regexp *rexp = NULL;
static char subptext[80];
if (first_article < gh->first_db_article)
first_article = gh->first_db_article;
if (last_article > gh->last_db_article)
last_article = gh->last_db_article;
if (last_article == 0 || first_article > last_article) return 0;
#ifdef NOV
/*
* Some trickery, since we normally avoid reading all of the
* XOVER or .overview data:
*/
if (flags & ACC_ALSO_READ_ARTICLES)
first_article = gh->first_a_article;
db_read_group(gh, first_article, last_article);
#else
data = open_data_file(gh, 'd', OPEN_READ);
if (data == NULL) return -10;
if ((data_offset = get_data_offset(gh, first_article)) == (off_t)(-1))
return -11;
#endif /* NOV */
if (mask == NULL) {
if (rexp != NULL) {
freeobj(rexp);
rexp = NULL;
}
} else {
if (*mask == '/') {
mask++;
if (rexp != NULL) {
if (strncmp(mask, subptext, 80) != 0) {
freeobj(rexp);
rexp = NULL;
}
}
if (rexp == NULL) {
strncpy(subptext, mask, 80);
rexp = regcomp(mask);
if (rexp == NULL) return -1;
}
/* notice mask is still non-NULL */
}
}
if ((flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
leave_attr = 0;
else if (select_leave_next)
leave_attr = A_READ; /* will prompt */
else
leave_attr = A_LEAVE_NEXT;
if (!(flags & ACC_SPEW_MODE))
use_newsrc(gh, (flags & ACC_MERGED_NEWSRC) ? 2 :
(flags & ACC_ORIG_NEWSRC) ? 1 : 0);
if ((flags & ACC_EXTRA_ARTICLES) == 0)
mark_memory(&mem_marker);
ah = alloc_art();
skip_digest = 0;
#ifdef NOV
/* XXX: db_read_art takes a FILE * in nnmaster version */
while ((dbstatus = db_read_art((FILE *) -1)) > 0) {
#ifdef ART_GREP
if (s_keyboard || s_hangup) break; /* add to allow interrupt of GREP */
#endif
if (db_hdr.dh_number < first_article)
continue;
#else
fseek(data, data_offset, 0);
while (data_offset < gh->data_write_offset) {
#ifdef ART_GREP
if (s_keyboard || s_hangup) break; /* add to allow interrupt of GREP */
#endif
data_size = db_read_art(data);
if (data_size <= 0) {
fclose(data);
if ((flags & ACC_EXTRA_ARTICLES) == 0)
release_memory(&mem_marker);
return -2;
}
data_offset += data_size;
#endif /* NOV */
if (db_hdr.dh_lpos == (off_t)0)
continue; /* article not accessible */
if (db_hdr.dh_number > gh->last_db_article
|| db_hdr.dh_number < gh->first_db_article)
goto data_error;
if (skip_digest && db_data.dh_type == DH_SUB_DIGEST)
continue;
skip_digest = 0;
if (db_hdr.dh_cross_postings && !(flags & ACC_ALSO_CROSS_POSTINGS)) {
for (n = 0; n < (int)db_hdr.dh_cross_postings; n++) {
cross_post = NETW_CROSS_INT(db_data.dh_cross[n]);
if (cross_post < 0 || cross_post >= master.number_of_groups)
continue;
cpgh = ACTIVE_GROUP(cross_post);
if (cpgh == gh) {
if (seq_cross_filtering) continue;
n = db_hdr.dh_cross_postings;
break;
}
if (!(flags & ACC_ALSO_UNSUB_GROUPS))
if (cpgh->group_flag & G_UNSUBSCRIBED) continue;
if (!seq_cross_filtering) break;
if (cpgh->preseq_index > 0 &&
cpgh->preseq_index < gh->preseq_index) break;
}
if (n < (int)db_hdr.dh_cross_postings) {
if (db_data.dh_type == DH_DIGEST_HEADER) skip_digest++;
continue;
}
}
ah->flag = 0;
switch (db_data.dh_type) {
case DH_DIGEST_HEADER:
if (flags & ACC_DONT_SPLIT_DIGESTS)
skip_digest++;
else
if ((flags & ACC_ALSO_FULL_DIGEST) == 0)
continue; /* don't want the full digest when split */
ah->flag |= A_FULL_DIGEST;
break;
case DH_SUB_DIGEST:
ah->flag |= A_DIGEST;
break;
}
ah->a_number = db_hdr.dh_number;
if (ah->a_number > last_article) break;
if (flags & ACC_SPEW_MODE) {
fold_string(db_data.dh_subject);
tprintf("%x:%s\n", (int)(gh->group_num), db_data.dh_subject);
continue;
}
ah->hpos = db_hdr.dh_hpos;
ah->fpos = ah->hpos + (off_t)(db_hdr.dh_fpos);
ah->lpos = db_hdr.dh_lpos;
ah->attr = test_article(ah);
if (flags & ACC_MERGED_NEWSRC) {
if (ah->attr == A_KILL) continue;
} else if (ah->attr != A_READ && (flags & ACC_ONLY_READ_ARTICLES))
continue;
#ifdef ART_GREP
if((flags & ACC_ON_GREP_UNREAD) && (ah->attr == A_READ))
continue;
#endif /* ART_GREP */
if (rexp != NULL) {
if (flags & ACC_ON_SUBJECT)
if (regexec_cf(rexp, db_data.dh_subject)) goto match_ok;
if (flags & ACC_ON_SENDER)
if (regexec_cf(rexp, db_data.dh_sender)) goto match_ok;
#ifdef ART_GREP
if (flags & (ACC_ON_GREP_UNREAD | ACC_ON_GREP_ALL))
if (grep_article(gh, ah, (char *)rexp, regexec_cf)) goto match_ok;
#endif /* ART_GREP */
continue;
} else
if (mask != NULL) {
if (flags & ACC_ON_SUBJECT)
if (strmatch_cf(mask, db_data.dh_subject)) goto match_ok;
if (flags & ACC_ON_SENDER)
if (strmatch_cf(mask, db_data.dh_sender)) goto match_ok;
#ifdef ART_GREP
if (flags & (ACC_ON_GREP_UNREAD | ACC_ON_GREP_ALL))
if (grep_article(gh, ah, mask, strmatch_cf)) goto match_ok;
#endif /* ART_GREP */
continue;
}
match_ok:
attr_again:
switch (ah->attr) {
case A_LEAVE:
if (mask) {
ah->attr = 0;
break;
}
if (leave_attr == A_READ) {
clrdisp();
prompt("Select left over articles in group %s? ", gh->group_name);
leave_attr = yes(0) > 0 ? A_SELECT : A_LEAVE_NEXT;
}
ah->attr = leave_attr;
goto attr_again;
case A_SELECT:
if (mask) ah->attr = 0;
break;
case A_READ:
if (flags & ACC_MERGED_NEWSRC) break;
if (!(flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
if (ah->a_number > gh->last_article)
continue;
/* FALL THRU */
case A_SEEN:
case 0:
if (flags & ACC_DO_KILL) {
ah->replies = db_hdr.dh_replies;
ah->sender = db_data.dh_sender;
ah->subject = db_data.dh_subject;
if (kill_article(ah)) continue;
}
/* The 'P' command to a read group must show articles as read */
if (gh->unread_count <= 0)
ah->attr = A_READ;
break;
default:
break;
}
if (db_hdr.dh_sender_length) {
ah->name_length = db_hdr.dh_sender_length;
ah->sender = alloc_str((int)db_hdr.dh_sender_length);
strcpy(ah->sender, db_data.dh_sender);
} else
ah->sender = "";
if (db_hdr.dh_subject_length) {
ah->subj_length = db_hdr.dh_subject_length;
ah->subject = alloc_str((int)db_hdr.dh_subject_length);
strcpy(ah->subject, db_data.dh_subject);
} else
ah->subject = "";
ah->replies = db_hdr.dh_replies;
ah->lines = db_hdr.dh_lines;
ah->t_stamp = db_hdr.dh_date;
ah->a_group = (flags & ACC_MERGED_MENU) ? current_group : NULL;
add_article(ah);
ah = alloc_art();
}
#ifdef NOV
if (dbstatus < 0) { /* Error reading DB? */
if ((flags & ACC_EXTRA_ARTICLES) == 0)
release_memory(&mem_marker);
return -2;
}
/* else EOF reading DB */
#else
fclose(data);
#endif /* NOV */
if ((flags & ACC_DONT_SORT_ARTICLES) == 0)
sort_articles(-1);
else
no_sort_articles();
if (ignore_re && !ignore_fancy_select && !(flags & ACC_ALSO_READ_ARTICLES)) {
int nexta, roota, killa, newa;
for (nexta = killa = newa = roota = 0; nexta < n_articles; nexta++) {
ah = articles[nexta];
if (ah->flag & A_ROOT_ART) roota = ah->replies & 127 ;
if (roota && ah->replies && !auto_select_article(ah,1)) killa++;
else articles[newa++] = ah;
}
n_articles -= killa;
killed_articles += killa;
}
return n_articles > 0 ? 1 : 0;
data_error:
log_entry('E', "%s: data inconsistency", gh->group_name);
#ifndef NOV
fclose(data);
#endif /* NOV */
if ((flags & ACC_EXTRA_ARTICLES) == 0)
release_memory(&mem_marker);
return -12;
}
#ifdef ART_GREP
/* GREP ARTICLE -- search the article for the pattern using the specified function
Return 1 if pattern occurs in the article else 0
Also return 0 if any malloc or file open or read error occurs
Global data_header db_hdr is set by caller
todo:
doesn't work for folders (hangs)
*/
static int
grep_article(gh, ah, pattern, fcn)
group_header *gh;
article_header *ah;
char *pattern;
int (*fcn)();
{
char *line, *end;
FILE *art;
news_header_buffer buffer1, buffer2;
static char *buf;
static int bufsize, count;
int size;
count++;
art = open_news_article(ah, FILL_OFFSETS |
(body_search_header ? 0 : SKIP_HEADER), buffer1, buffer2);
if(!art) {
/*msg("Cannot open article");*/
return 0;
}
size = ah->lpos - ftell(art) + 1;
if(bufsize < size) {
if(buf)
free(buf);
buf = (char *)malloc(size + 10);
if(!buf) {
msg("Cannot malloc %d bytes", size);
bufsize = 0;
return 0;
}
bufsize = size;
}
if(fread(buf, size-1, 1, art) != 1) {
fclose(art);
/*msg("Cannot read article");*/
return 0;
}
fclose(art);
/* print status message every so often */
if(!(count % 27))
msg("Searching %d of %d",
ah->a_number - gh->first_db_article,
gh->last_db_article - gh->first_db_article );
buf[size] = 0; /* make buf a giant string so strchr below terminates */
line = buf;
while((end = strchr(line, '\n'))) {
*end++ = 0; /* make the line a nul terminated string */
if((*fcn)(pattern, line))
return 1;
line = end;
}
return 0;
}
#endif /* ART_GREP */