home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume23
/
trn
/
part10
/
mt-read.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-22
|
13KB
|
498 lines
/* $Header: mt-read.c,v 4.3.3.2 91/01/16 02:49:15 davison Trn $
**
** $Log: mt-read.c,v $
** Revision 4.3.3.2 91/01/16 02:49:15 davison
** Changed Free() to safefree(). Tweaked fopen for possible binary open mode.
**
** Revision 4.3.3.1 90/07/24 23:51:12 davison
** Initial Trn Release
**
*/
#include "EXTERN.h"
#include "common.h"
#include "mthreads.h"
static FILE *fp_in;
void tweak_roots();
/* Attempt to open the thread file. If it's there, only grab the totals
** from the start of the file. This should give them enough information
** to decide if they need to read the whole thing into memory.
*/
int
init_data( filename )
char *filename;
{
root_root = Null(ROOT*);
author_root = Null(AUTHOR*);
unk_domain.ids = Nullart;
unk_domain.link = Null(DOMAIN*);
if( (fp_in = fopen( filename, FOPEN_RB )) == Nullfp ) {
bzero( &total, sizeof (TOTAL) );
return 0;
}
if( fread( &total, 1, sizeof (TOTAL), fp_in ) < sizeof (TOTAL) ) {
fclose( fp_in );
bzero( &total, sizeof (TOTAL) );
return 0;
}
return 1;
}
/* They want everything. Read in the packed information and transform it
** into a set of linked structures that is easily manipulated.
*/
int
read_data()
{
if( read_authors()
&& read_subjects()
&& read_roots()
&& read_articles()
&& read_ids() )
{
tweak_roots();
fclose( fp_in );
return 1;
}
/* Something failed. Safefree takes care of checking if we're partially
** allocated. Any linked-list structures we created were freed before
** we got here.
*/
safefree( &strings );
safefree( &subject_cnts );
safefree( &author_cnts );
safefree( &root_array );
safefree( &subject_array );
safefree( &article_array );
safefree( &ids );
fclose( fp_in );
return 0;
}
/* They don't want to read the data. Close the file if we opened it.
*/
void
dont_read_data( open_flag )
int open_flag; /* 0 == not opened, 1 == open failed, 2 == open */
{
if( open_flag == 2 ) {
fclose( fp_in );
}
}
#define give_string_to( dest ) /* Comment for makedepend to \
** ignore the backslash above */ \
{\
register MEM_SIZE len = strlen( string_ptr ) + 1;\
dest = safemalloc( len );\
bcopy( string_ptr, dest, (int)len );\
string_ptr += len;\
}
char *subject_strings;
/* The author information is an array of use-counts, followed by all the
** null-terminated strings crammed together. The subject strings are read
** in at the same time, since they are appended to the end of the author
** strings.
*/
int
read_authors()
{
register int count;
register char *string_ptr;
register WORD *authp;
register AUTHOR *author, *last_author, **author_ptr;
if( !read_item( &author_cnts, (MEM_SIZE)total.author * sizeof (WORD) )
|| !read_item( &strings, total.string1 ) ) {
return 0;
}
/* We'll use this array to point each article at its proper author
** (packed values are saved as indexes).
*/
author_array = (AUTHOR**)safemalloc( total.author * sizeof (AUTHOR*) );
author_ptr = author_array;
authp = author_cnts;
string_ptr = strings;
last_author = Null(AUTHOR*);
for( count = total.author; count--; ) {
*author_ptr++ = author = (AUTHOR*)safemalloc( sizeof (AUTHOR) );
if( !last_author ) {
author_root = author;
} else {
last_author->link = author;
}
give_string_to( author->name );
author->count = *authp++;
last_author = author;
}
last_author->link = Null(AUTHOR*);
subject_strings = string_ptr;
free( author_cnts );
author_cnts = Null(WORD*);
return 1;
}
/* The subject values consist of the crammed-together null-terminated strings
** (already read in above) and the use-count array. They were saved in the
** order that the roots will need when they are unpacked.
*/
int
read_subjects()
{
if( !read_item( &subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD) ) ) {
return 0;
}
return 1;
}
/* Read in the packed root structures and recreate the linked list versions,
** processing each root's subjects as we go. Defer interpretation of article
** offsets until we unpack the article structures.
*/
int
read_roots()
{
register int count;
register char *string_ptr;
register WORD *subjp;
ROOT *root, *last_root, **root_ptr;
SUBJECT *subject, *last_subject, **subj_ptr;
int ret;
/* Use this array when unpacking the article's subject offsets. */
subject_array = (SUBJECT**)safemalloc( total.subject * sizeof (SUBJECT*) );
subj_ptr = subject_array;
/* And this array points the article's root offsets that the right spot. */
root_array = (ROOT**)safemalloc( total.root * sizeof (ROOT*) );
root_ptr = root_array;
subjp = subject_cnts;
string_ptr = subject_strings;
#ifndef lint
last_root = (ROOT*)&root_root;
#else
last_root = Null(ROOT*);
#endif
for( count = total.root; count--; ) {
ret = fread( &p_root, 1, sizeof (PACKED_ROOT), fp_in );
if( ret != sizeof (PACKED_ROOT) ) {
log_error( "failed root read -- %d bytes instead of %d.\n",
ret, sizeof (PACKED_ROOT) );
ret = 0;
/* Free the roots we've read so far and their subjects. */
while( root_ptr != root_array ) {
free( *--root_ptr );
}
while( subj_ptr != subject_array ) {
free( (*--subj_ptr)->str );
free( *subj_ptr );
}
goto finish_up;
}
*root_ptr++ = root = (ROOT*)safemalloc( sizeof (ROOT) );
root->link = Null(ROOT*);
root->seq = p_root.articles;
root->root_num = p_root.root_num;
root->thread_cnt = p_root.thread_cnt;
root->subject_cnt = p_root.subject_cnt;
last_subject = Null(SUBJECT*);
while( p_root.subject_cnt-- ) {
*subj_ptr++ = subject = (SUBJECT*)safemalloc( sizeof (SUBJECT) );
if( !last_subject ) {
root->subjects = subject;
} else {
last_subject->link = subject;
}
give_string_to( subject->str );
subject->count = *subjp++;
last_subject = subject;
}
last_subject->link = Null(SUBJECT*);
last_root->link = root;
last_root = root;
}
ret = 1;
finish_up:
free( subject_cnts );
free( strings );
subject_cnts = Null(WORD*);
strings = Nullch;
return ret;
}
/* A simple routine that checks the validity of the article's subject value.
** A -1 means that it is NULL, otherwise it should be an offset into the
** subject array we just unpacked.
*/
SUBJECT *
valid_subject( num, art_num )
WORD num;
long art_num;
{
if( num == -1 ) {
return Null(SUBJECT*);
}
if( num < 0 || num >= total.subject ) {
log_error( "Invalid subject in data file: %d [%ld]\n", num, art_num );
return Null(SUBJECT*);
}
return subject_array[num];
}
/* Ditto for author checking. */
AUTHOR *
valid_author( num, art_num )
WORD num;
long art_num;
{
if( num == -1 ) {
return Null(AUTHOR*);
}
if( num < 0 || num >= total.author ) {
log_error( "Invalid author in data file: %d [%ld]\n", num, art_num );
return Null(AUTHOR*);
}
return author_array[num];
}
/* Our parent/sibling information is a relative offset in the article array.
** zero for none. Child values are always found in the very next array
** element if child_cnt is non-zero.
*/
#define valid_node( rel, num ) (!(rel)? Nullart : article_array[(rel)+(num)])
/* Read the articles into their linked lists. Point everything everywhere. */
int
read_articles()
{
register int count;
register ARTICLE *article, **article_ptr;
int ret;
/* Build an array to interpret interlinkages of articles. */
article_array = (ARTICLE**)safemalloc( total.article * sizeof (ARTICLE*) );
article_ptr = article_array;
/* Allocate all the structures up-front so that we can point to un-read
** siblings as we go.
*/
for( count = total.article; count--; ) {
*article_ptr++ = (ARTICLE*)safemalloc( sizeof (ARTICLE) );
}
article_ptr = article_array;
for( count = 0; count < total.article; count++ ) {
ret = fread( &p_article, 1, sizeof (PACKED_ARTICLE), fp_in );
if( ret != sizeof (PACKED_ARTICLE) ) {
log_error( "failed article read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ARTICLE) );
ret = 0;
goto finish_up;
}
article = *article_ptr++;
article->num = p_article.num;
article->date = p_article.date;
article->subject = valid_subject( p_article.subject, p_article.num );
article->author = valid_author( p_article.author, p_article.num );
article->flags = p_article.flags;
article->child_cnt = p_article.child_cnt;
article->parent = valid_node( p_article.parent, count );
article->children = article->child_cnt?article_array[count+1]:Nullart;
article->siblings = valid_node( p_article.siblings, count );
article->root = root_array[p_article.root];
}
ret = 1;
finish_up:
/* We're done with most of the pointer arrays. */
free( root_array );
free( subject_array );
free( author_array );
root_array = Null(ROOT**);
subject_array = Null(SUBJECT**);
author_array = Null(AUTHOR**);
return ret;
}
/* Read the message-id strings and attach them to each article. The data
** format consists of the mushed-together null-terminated strings (a domain
** name followed by all its unique-id prefixes) and then the article offsets
** to which they belong. The first domain name was omitted, as it is the
** ".unknown." domain for those truly weird message-id's without '@'s.
*/
int
read_ids()
{
register DOMAIN *domain, *last;
register ARTICLE *article;
register char *string_ptr;
register int i, count;
if( !read_item( &strings, total.string2 ) ) {
return 0;
}
if( !read_item( &ids,
(MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD) ) ) {
return 0;
}
string_ptr = strings;
last = Null(DOMAIN*);
for( i = 0, count = total.domain + 1; count--; i++ ) {
if( i ) {
domain = (DOMAIN*)safemalloc( sizeof (DOMAIN) );
give_string_to( domain->name );
} else {
domain = &unk_domain;
}
if( ids[i] == -1 ) {
domain->ids = Nullart;
} else {
article = article_array[ids[i]];
domain->ids = article;
for( ;; ) {
give_string_to( article->id );
article->domain = domain;
if( ids[++i] != -1 ) {
article = article->id_link = article_array[ids[i]];
} else {
article->id_link = Nullart;
break;
}
}
}
if( last ) {
last->link = domain;
}
last = domain;
}
last->link = Null(DOMAIN*);
free( ids );
free( strings );
ids = Null(WORD*);
strings = Nullch;
return 1;
}
/* And finally, point all the roots at their root articles and get rid
** of anything left over that was used to aid our unpacking.
*/
void
tweak_roots()
{
register ROOT *root;
for( root = root_root; root; root = root->link ) {
root->articles = article_array[root->seq];
}
free( article_array );
article_array = Null(ARTICLE**);
}
/* A short-hand for reading a chunk of the file into a malloc'ed array.
*/
int
read_item( dest, len )
char **dest;
MEM_SIZE len;
{
int ret;
*dest = safemalloc( len );
ret = fread( *dest, 1, (int)len, fp_in );
if( ret != len ) {
log_error( "Only read %ld bytes instead of %ld.\n",
(long)ret, (long)len );
free( *dest );
*dest = Nullch;
return 0;
}
return 1;
}
/* Interpret rn's '%X' and '%x' path prefixes without including all their
** source. Names that don't start with '%' or '/' are prefixed with the
** SPOOL directory.
*/
char *
file_exp( name )
char *name;
{
static char name_buff[256];
if( *name == '/' ) { /* fully qualified names are left alone */
return name;
} else if( *name != '%' ) { /* all normal names are relative to SPOOL */
sprintf( name_buff, "%s/%s", SPOOL, name );
} else { /* interpret %x (LIB) & %X (RNLIB) */
if( name[1] == 'x' ) {
strcpy( name_buff, LIB );
} else if( name[1] == 'X' ) {
strcpy( name_buff, RNLIB );
} else {
log_entry( "Unknown expansion: %s", name );
exit( 1 );
}
strcat( name_buff, name+2 );
}
return name_buff;
}
#ifndef lint
/* A malloc that bombs-out when memory is exhausted. */
char *
safemalloc( amount )
MEM_SIZE amount;
{
register char *cp;
extern char *malloc();
if( (cp = malloc( amount )) == Nullch ) {
log_error( "malloc(%ld) failed.\n", (long)amount );
exit( 1 );
}
return cp;
}
#endif
/* Create a malloc'ed copy of a string. */
char *
savestr( str )
char *str;
{
register MEM_SIZE len = strlen( str ) + 1;
register char *newaddr = safemalloc( len );
bcopy( str, newaddr, (int)len );
return newaddr;
}
#ifndef lint
/* Free some memory if it hasn't already been freed. */
void
safefree( pp )
char **pp;
{
if( *pp ) {
free( *pp );
*pp = Nullch;
}
}
#endif