home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 35 Internet
/
35-Internet.zip
/
vsoup128.zip
/
newsrc.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1997-02-06
|
17KB
|
767 lines
// $Id: newsrc.cc 1.22 1997/02/06 09:55:30 hardy Exp $
//
// This progam/module was written by Hardy Griech based on ideas and
// pieces of code from Chin Huang (cthuang@io.org). Bug reports should
// be submitted to rgriech@ibm.net.
//
// This file is part of soup++ for OS/2. Soup++ including this file
// is freeware. There is no warranty of any kind implied. The terms
// of the GNU Gernal Public Licence are valid for this piece of software.
//
// rg110796:
// - introduction of a hash list because performance was real bad
// - grpFirst/grpNext now returning only subscribed groups
// - newsrc is written if there were any changes (onyl then!)
//
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mts.hh"
#include "newsrc.hh"
#include "util.hh"
TNewsrc::TNewsrc( void )
{
int i;
#ifdef TRACE_ALL
printfT( "TNewsrc::TNewsrc()\n" );
#endif
groups = NULL;
filename = xstrdup("");
cacheGroup = NULL;
cacheGroupName = NULL;
addGroupP = NULL;
fileRead = 0;
for (i = 0; i < NEWSRC_HASHSIZE; ++i)
hashTab[i] = (pGroup)NULL;
} // TNewsrc::TNewsrc
TNewsrc::~TNewsrc()
{
Range *rp1, *rp2;
pGroup gp1, gp2;
//// delete filename;
gp1 = groups;
while (gp1 != NULL) {
rp1 = gp1->readList;
while (rp1 != NULL) {
rp2 = rp1->next;
//// delete rp1;
rp1 = rp2;
}
gp2 = gp1->next;
//// delete gp1->name;
//// delete gp1;
gp1 = gp2;
}
} // TNewsrc::~TNewsrc
TNewsrc::Range *TNewsrc::getReadList( TFile &nrcFile )
//
// Read the article numbers from a .newsrc line.
//
{
Range *pLast, *rp, *head;
int c;
long lo,hi;
char buf[10];
/* Initialize subscription list */
pLast = NULL;
head = NULL;
/* Expect [ \n] */
c = nrcFile.getcc();
while (c != '\n' && c != EOF) {
/* Expect number */
if (nrcFile.scanf("%*[ \t]%ld",&lo) != 1) {
nrcFile.fgets( buf,sizeof(buf),1 );
break;
}
lo = (lo >= 0) ? lo : 0;
/* Get space for new list entry */
rp = new Range;
rp->next = NULL;
/* Expect [-,\n] */
c = nrcFile.getcc();
if (c == '-') {
/* Is a range */
/* Expect number */
if (nrcFile.scanf("%*[ \t]%ld",&hi) != 1) {
nrcFile.fgets( buf,sizeof(buf),1 );
break;
}
hi = (hi >= 0) ? hi : 0;
rp->lo = lo;
rp->hi = hi;
/* Reverse them in case they're backwards */
if (hi < lo) {
rp->lo = hi;
rp->hi = lo;
}
if (rp->lo == 0) //???
rp->lo = 1;
/* Expect [,\n] */
c = nrcFile.getcc();
} else {
/* Not a range */
rp->lo = rp->hi = lo;
}
if (rp->lo <= 0) { //???
//// delete rp;
continue;
}
/* Check if range overlaps last one */
if (pLast != NULL && rp->lo <= pLast->hi + 1) {
/* Combine ranges */
if (rp->lo < pLast->lo)
pLast->lo = rp->lo;
if (rp->hi > pLast->hi)
pLast->hi = rp->hi;
/* Free old (ehm new?) one */
//// delete rp;
} else {
/* No overlap, update pointers */
if (pLast == NULL) {
head = rp;
} else {
pLast->next = rp;
}
pLast = rp;
}
}
return head;
} // TNewsrc::getReadList
int TNewsrc::readFile( const char *newsrcFile )
//
// Read the .newsrc file.
// Return 0 on error (otherwise 1)
//
{
TFile nrcFile;
char groupName[BUFSIZ];
char buf[10];
#ifdef TRACE_ALL
printfT("TNewsrc::readFile(%s)\n",newsrcFile);
#endif
assert( !fileRead );
filename = xstrdup(newsrcFile);
/* Open it */
if ( !nrcFile.open(newsrcFile,TFile::mread,TFile::otext)) {
hprintfT( STDERR_FILENO, "cannot open %s\n", newsrcFile );
fileRead = 1;
return 0;
}
sema.Request();
/* Read newsgroup entry */
for (;;) {
pGroup np;
int res;
res = nrcFile.scanf("%[^:! \t\n]%*[ \t]", groupName);
if (res == 0) {
nrcFile.fgets( buf,sizeof(buf),1 );
continue;
}
if (res != 1)
break;
#ifdef DEBUG_ALL
printfT( "TNewsrc::readfile(): %s\n",groupName );
#endif
if (strchr(groupName,',') != NULL) {
hprintfT( STDERR_FILENO,"ill groupname in %s: %s\n", newsrcFile,groupName );
nrcFile.fgets( buf,sizeof(buf),1 );
continue;
}
else if (grpExists(groupName)) {
hprintfT( STDERR_FILENO,"groupname %s found twice in\n\t%s - second occurence removed\n",
groupName,newsrcFile );
nrcFile.fgets( buf,sizeof(buf),1 );
continue;
}
if (groupName[0] == '\0')
break;
//
// Allocate a new entry
//
np = (pGroup)grpAdd( groupName );
nrcFile.scanf( "%*[ \t]%1[:!]",buf );
if (*buf == '\0') {
/* The user didn't end the line with a colon. */
nrcFile.fgets( buf,sizeof(buf),1 );
np->subscribed = 1;
} else {
/* Parse subscription list */
np->subscribed = (*buf == ':');
np->readList = getReadList(nrcFile);
}
}
nrcFile.close();
fileRead = 1;
fileChanged = 0;
sema.Release();
return 1;
} // TNewsrc::readFile
void TNewsrc::putReadList( TFile &fd, Range *head )
//
// Write the article numbers for a .newsrc entry.
//
{
if (head == NULL)
fd.putcc('0');
else {
while (head != NULL) {
if (head->lo == head->hi)
fd.printf("%ld", head->lo);
else
fd.printf("%ld-%ld", head->lo, head->hi);
head = head->next;
if (head != NULL)
fd.putcc(',');
}
}
fd.putcc('\n');
} // TNewsrc::putReadList
int TNewsrc::writeFile(void)
//
// Rewrite the updated .newsrc file
//
{
char oldFile[FILENAME_MAX];
TFile nrcFile;
pGroup np;
#ifdef TRACE_ALL
printfT( "TNewsrc::writeFile()\n" );
#endif
if (filename[0] == '\0')
return 1; // successful (cause nothing to do)
if (groups == NULL || !fileRead || !fileChanged)
return 1;
sema.Request();
//
// Back up old .newsrc file.
//
sprintfT(oldFile, "%s.old", filename);
removeT(oldFile);
renameT(filename, oldFile);
if ( !nrcFile.open(filename,TFile::mwrite,TFile::otext,1)) {
hprintfT( STDERR_FILENO, "cannot write %s\n", filename );
sema.Release();
return 0;
}
for (np = groups; np != NULL; np = np->next) {
nrcFile.printf( "%s%c ", np->name, np->subscribed ? ':' : '!' );
putReadList( nrcFile, np->readList );
}
nrcFile.close();
sema.Release();
return 1;
} // TNewsrc::writeFile
//--------------------------------------------------------------------------------
TNewsrc::pGroup TNewsrc::getGroupP( const char *groupName )
//
// check, if groupName exists (return NULL, if not, otherwise pGroup)
// "cache" is updated
//
{
pGroup np;
pGroup res;
#ifdef TRACE_ALL
// printfT( "TNewsrc::getGroupP(%s)\n", groupName );
#endif
sema.Request();
if (cacheGroupName == NULL || stricmp(groupName,cacheGroupName) != 0) {
int hasho;
hasho = hashi(groupName,NEWSRC_HASHSIZE);
for (np = hashTab[hasho]; np != NULL; np = np->hashNext) {
if (stricmp(np->name, groupName) == 0) {
cacheGroupName = np->name;
cacheGroup = np;
break;
}
}
if (np == NULL) {
cacheGroupName = NULL;
cacheGroup = NULL;
}
}
res = cacheGroup;
sema.Release();
#ifdef TRACE_ALL
// printfT( "TNewsrc::getGroupP(%s) = %p\n", groupName,cacheGroup );
#endif
return res;
} // TNewsrc::getGroupP
void TNewsrc::grpFixReadList( const char *groupName, long groupLo, long groupHi )
//
// Sanity fixes to the read article number list
//
{
pGroup np = getGroupP( groupName );
Range *rp1, *rp2;
#ifdef TRACE_ALL
printfT( "TNewsrc::grpFixReadList(%s,%ld,%ld)\n",groupName,groupLo,groupHi );
#endif
assert( np != NULL );
sema.Request();
//
// If the highest read article is greater than the highest
// available article, assume the group has been reset.
// But: allow a small threshold due to cancelled articles (4)
//
if (np->readList != NULL) {
for (rp1 = np->readList; rp1->next != NULL; rp1 = rp1->next) // find end of list
;
if (rp1->hi > groupHi + ((groupHi > 100) ? 4 : 0)) {
//
// delete all of the list
//
rp1 = np->readList;
while (rp1 != NULL) {
rp2 = rp1->next;
//// delete rp1;
rp1 = rp2;
}
np->readList = NULL;
}
}
//
// eliminate ranges lower than the lowest available article
// proceed from the beginning of the list...
//
rp1 = np->readList;
while (rp1 != NULL && groupLo > rp1->hi) {
#ifdef DEBUG_ALL
printfT( "ellower: \n" );
#endif
np->readList = rp1->next;
//// delete rp1;
rp1 = np->readList;
}
//
// All entries with a range below groupLo have been eliminated. Also no entry of
// the list has a higher number than groupHi. This means, that all entries are
// in the range of groupLo..groupHi (or the list is empty).
// If the list is empty, an entry from 1..groupLo-1 must be generated. If the
// list is not empty and groupLo is smaller than rp1->lo again 1..groupLo-1 must
// be generated (if groupLo==1, nothing will be done)
//
rp1 = np->readList;
if (rp1 == NULL || groupLo < rp1->lo) {
if (groupLo > 1) {
rp2 = new Range;
rp2->next = rp1;
rp2->lo = 1;
rp2->hi = groupLo-1;
np->readList = rp2;
}
}
else if (rp1 != NULL)
rp1->lo = 1; // old entry can be used!
#ifdef TRACE_ALL
printfT( "grpFixReadList(): ende\n" );
#endif
sema.Release();
//
// fileChanged is not set intentionally
//
return;
} // TNewsrc::grpFixReadList
const char *TNewsrc::grpFirst( void )
//
// return first subscribed group (NULL, if none)
//
{
pGroup pp;
#ifdef TRACE_ALL
printfT( "TNewsrc::grpFirst()\n" );
#endif
for (pp = groups; pp != NULL; pp = pp->next) {
#ifdef TRACE_ALL
printfT( "TNewsrc::grpFirst(): %s,%d\n",pp->name,pp->subscribed );
#endif
if (pp->subscribed)
return pp->name;
}
return NULL;
} // TNewsrc::grpFirst
const char *TNewsrc::grpNext( const char *prevGroupName )
//
//
// return next subscribed group (NULL, if none)
//
{
pGroup np = getGroupP( prevGroupName );
#ifdef TRACE_ALL
printfT( "TNewsrc::grpNext(%s)\n",prevGroupName );
#endif
assert( prevGroupName[0] != '\0' );
assert( np != NULL );
while (np != NULL) {
np = np->next;
if (np != NULL && np->subscribed)
return np->name;
}
return NULL;
} // TNewsrc::grpNext
int TNewsrc::grpSubscribed( const char *groupName )
{
pGroup np = getGroupP( groupName );
#ifdef TRACE_ALL
printfT( "TNewsrc::grpSubscribed(%s)\n",groupName );
#endif
assert( np != NULL );
#ifdef TRACE_ALL
printfT( "TNewsrc::grpSubscribed(%s): %d\n",np->name,np->subscribed );
#endif
return np->subscribed;
} // TNewsrc::grpSubscribed
int TNewsrc::grpExists( const char *groupName )
{
pGroup np = getGroupP( groupName );
#ifdef TRACE_ALL
printfT( "TNewsrc::grpExists(%s)\n", groupName );
#endif
return np != NULL;
} // TNewsrc::grpExists
void TNewsrc::grpUnsubscribe( const char *groupName )
{
pGroup np = getGroupP( groupName );
#ifdef TRACE_ALL
printfT( "TNewsrc::grpUnsubscibe(%s)\n",groupName );
#endif
if (np != NULL) {
np->subscribed = 0;
fileChanged = 1;
}
} // TNewsrc::grpUnsubscribe
long TNewsrc::grpFirstUnread( const char *groupName, long groupLo )
//
// Get first unread article number.
//
{
pGroup np = getGroupP( groupName );
long res;
#ifdef TRACE_ALL
printfT( "TNewsrc::grpFirstUnread(%s,%ld)\n",groupName,groupLo );
#endif
assert( np != NULL );
sema.Request();
if (np->readList == NULL)
res = groupLo;
else {
if (groupLo < np->readList->lo)
res = groupLo;
else
res = np->readList->hi + 1;
}
sema.Release();
return res;
} // TNewsrc::grpFirstUnread
int TNewsrc::artIsRead( const char *groupName, long artNum )
//
// Determine if the article number has been read
//
{
Range *head;
pGroup np = getGroupP( groupName );
#ifdef TRACE_ALL
printfT( "TNewsrc::artIsRead(%s,%ld)\n", groupName,artNum );
#endif
assert( np != NULL );
if (artNum <= 0)
return 1;
sema.Request();
head = np->readList;
//
// Look through the list
//
while (head != NULL) {
if (artNum < head->lo) {
sema.Release();
return 0;
}
if (artNum >= head->lo && artNum <= head->hi) {
sema.Release();
return 1;
}
head = head->next;
}
sema.Release();
return 0;
} // TNewsrc::artIsRead
void TNewsrc::artMarkRead( const char *groupName, long artNum )
//
// Mark article as read.
//
{
pGroup np = getGroupP( groupName );
Range *rp, *trp, *lrp;
if (np == NULL) { // might be true for cross referencing
#ifdef TRACE_ALL
printfT( "TNewsrc::artMarkRead(%s,%ld): np==NULL\n",groupName,artNum );
#endif
return;
}
if ( !np->subscribed) // might be true for cross referencing
return;
//
// if article has been already read, do nothing...
//
if (artIsRead(groupName,artNum))
return;
fileChanged = 1;
#ifdef TRACE_ALL
printfT( "TNewsrc::artMarkRead(%s,%ld)\n",groupName,artNum );
#endif
sema.Request();
rp = np->readList;
/* If num is much lower than lowest range, or the list is
empty, we need new entry */
if (rp == NULL || artNum < rp->lo - 1) {
trp = new Range;
trp->lo = trp->hi = artNum;
trp->next = rp;
np->readList = trp;
sema.Release();
return;
}
/* lrp remembers last entry in case we need to add a new entry */
lrp = NULL;
/* Find appropriate entry for this number */
while (rp != NULL) {
/* Have to squeeze one in before this one? */
if (artNum < rp->lo - 1) {
trp = new Range;
trp->lo = trp->hi = artNum;
trp->next = rp;
lrp->next = trp;
sema.Release();
return;
}
/* One less than entry's lo? */
if (artNum == rp->lo - 1) {
rp->lo = artNum;
sema.Release();
return;
}
/* In middle of range, do nothing */
if (artNum >= rp->lo && artNum <= rp->hi) {
sema.Release();
return;
}
/* One too high, must check if we merge with next entry */
if (artNum == rp->hi + 1) {
if (rp->next != NULL && artNum == rp->next->lo - 1) {
trp = rp->next;
rp->hi = trp->hi;
rp->next = trp->next;
//// delete trp;
sema.Release();
return;
} else {
/* No merge */
rp->hi = artNum;
sema.Release();
return;
}
}
lrp = rp;
rp = rp->next;
}
/* We flew off the end and need a new entry */
trp = new Range;
trp->lo = trp->hi = artNum;
trp->next = NULL;
lrp->next = trp;
#ifdef TRACE_ALL
printfT( "TNewsrc::artMarkRead\n" );
#endif
sema.Release();
return;
} // TNewsrc::artMarkRead
void TNewsrc::grpCatchup( const char *groupName, long groupLo, long groupHi, long numKeep )
{
long lo;
#ifdef TRACE_ALL
printfT( "TNewsrc::grpCatchup(%s,%ld,%ld,%ld)\n",groupName,groupLo,groupHi,numKeep );
#endif
lo = groupHi - numKeep + 1;
if (lo < 1)
lo = 1;
if (lo < groupLo)
lo = groupLo;
grpFixReadList( groupName, lo, groupHi );
fileChanged = 1;
} // TNewsrc::grpCatchup
void *TNewsrc::grpAdd( const char *groupName, int subscribe )
//
// Adds new group name (in any case). This means: double entries are possible,
// so check with grpExists() prior to the call to grpAdd()
//
{
pGroup np;
unsigned hasho;
sema.Request();
//
// allocate new entry
//
np = new Group;
np->subscribed = subscribe;
np->readList = NULL;
np->name = xstrdup(groupName);
np->next = NULL;
//
// update hash list
//
hasho = hashi(groupName,NEWSRC_HASHSIZE);
np->hashNext = hashTab[hasho];
hashTab[hasho] = np;
//
// add new group to end of list
//
if (groups == NULL)
groups = np;
else {
pGroup pp = (addGroupP == NULL) ? groups : addGroupP;
while (pp->next != NULL)
pp = pp->next;
pp->next = np;
addGroupP = np;
}
fileChanged = 1;
sema.Release();
return (void *)np;
} // TNewsrc::grpAdd