home *** CD-ROM | disk | FTP | other *** search
- /* $Id: mt-read.c,v 2.3 1992/12/14 00:14:04 davison Trn $
- */
-
- #include "EXTERN.h"
- #include "common.h"
- #include "threads.h"
- #include "mthreads.h"
-
- extern char *lib, *rnlib, *mtlib, *spool, *threaddir, *homedir;
- extern long first;
-
- static FILE *fp_in;
-
- void tweak_roots ANSI((void));
- void wrap_it_up ANSI((int));
-
- /* 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 (filename && (fp_in = fos2open(filename, FOPEN_RB)) != Nullfp) {
- #ifdef SETBUFFER
- setbuffer(fp_in, rwbuf, RWBUFSIZ);
- #else
- # ifdef SETVBUF
- setvbuf(fp_in, rwbuf, _IOFBF, RWBUFSIZ);
- # endif
- #endif
- if (fread((char*)&total,1,sizeof (TOTAL),fp_in) == sizeof (TOTAL)) {
- #ifdef TMPTHREAD
- lp_bmap(&total.first, 4);
- wp_bmap(&total.root, 5);
- #endif
-
- /* If the data looks ok, return success. */
- if (total.last - total.first >= 0 && total.author > 0
- && total.article > 0 && total.subject > 0 && total.domain > 0
- && total.subject <= total.article && total.author <= total.article
- && total.root <= total.article && total.domain <= total.article
- && total.string1 > 0 && total.string2 > 0) {
- return 1;
- }
- }
- bzero(&total, sizeof (TOTAL));
- total.first = first;
- total.last = first - 1;
- return 1;
- }
- bzero(&total, sizeof (TOTAL));
- return 0;
- }
-
- /* 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 this is an empty thread file, simply return success. */
- if (!total.root) {
- fclose(fp_in);
- return 1;
- }
-
- 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 some items
- ** were already freed. Any partially-allocated structures were freed
- ** before we got here. All other structures are cleaned up.
- */
- if (root_root) {
- register ROOT *root, *next_root;
- register SUBJECT *subj, *next_subj;
-
- for (root = root_root; root; root = next_root) {
- for (subj = root->subjects; subj; subj = next_subj) {
- next_subj = subj->link;
- free(subj->str);
- free(subj);
- }
- next_root = root->link;
- free(root);
- }
- root_root = Null(ROOT*);
- }
- if (author_array) {
- register int count;
-
- for (count = total.author; count--;) {
- free(author_array[count]->name);
- free(author_array[count]);
- }
- safefree(&author_array);
- author_root = Null(AUTHOR*);
- }
- if (article_array) {
- register int count;
-
- for (count = total.article; count--;) {
- free(article_array[count]);
- }
- safefree(&article_array);
- }
- safefree(&strings);
- safefree(&subject_cnts);
- safefree(&subject_array);
- safefree(&author_cnts);
- safefree(&root_array);
- safefree(&ids);
- unk_domain.ids = Nullart;
- unk_domain.link = Null(DOMAIN*);
- 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);
- bzero(&total, sizeof (TOTAL));
- }
- }
-
- #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, *string_end;
-
- /* 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, author_tally;
- 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)) {
- /* (Error already logged.) */
- return 0;
- }
- #ifdef TMPTHREAD
- wp_bmap(author_cnts, total.author);
- #endif
-
- string_ptr = strings;
- string_end = string_ptr + total.string1;
- if (string_end[-1] != '\0') {
- log_error("first string table is invalid.\n");
- 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;
-
- author_tally = 0;
- #ifndef lint
- last_author = (AUTHOR*)&author_root;
- #else
- last_author = Null(AUTHOR*);
- #endif
- for (count = total.author; count; count--) {
- if (string_ptr >= string_end) {
- break;
- }
- *author_ptr++ = author = (AUTHOR*)safemalloc(sizeof (AUTHOR));
- last_author->link = author;
- give_string_to(author->name);
- author_tally += *authp;
- author->count = *authp++;
- last_author = author;
- }
- last_author->link = Null(AUTHOR*);
-
- subject_strings = string_ptr;
-
- safefree(&author_cnts);
-
- if (count || author_tally > total.article) {
- log_error("author unpacking failed.\n");
- for (; count < total.author; count++) {
- free((*--author_ptr)->name);
- free(*author_ptr);
- }
- safefree(&author_array);
- return 0;
- }
- 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 require while being unpacked.
- */
- int
- read_subjects()
- {
- if (!read_item(&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))) {
- /* (Error already logged.) */
- return 0;
- }
- #ifdef TMPTHREAD
- wp_bmap(subject_cnts, total.subject);
- #endif
- 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, subj_tally;
- 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 offset. */
- subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*));
- subj_ptr = subject_array;
- /* And this array points the article's root offset at the right spot. */
- root_array = (ROOT**)safemalloc(total.root * sizeof (ROOT*));
- root_ptr = root_array;
-
- subjp = subject_cnts;
- string_ptr = subject_strings; /* string_end is already set */
-
- subj_tally = 0;
- #ifndef lint
- last_root = (ROOT*)&root_root;
- #else
- last_root = Null(ROOT*);
- #endif
- for (count = total.root; count--;) {
- ret = fread((char*)&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));
- return 0;
- }
- #ifdef TMPTHREAD
- lp_bmap(&p_root.root_num, 1);
- wp_bmap(&p_root.articles, 3);
- #endif
- if (p_root.articles < 0 || p_root.articles >= total.article
- || subj_ptr - subject_array + p_root.subject_cnt > total.subject) {
- log_error("root has invalid values.\n");
- return 0;
- }
- *root_ptr++ = root = (ROOT*)safemalloc(sizeof (ROOT));
- root->link = Null(ROOT*);
- root->articles = Nullart;
- 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_root->link = root;
- last_root = root;
-
- #ifndef lint
- last_subject = (SUBJECT*)&root->subjects;
- #else
- last_subject = Null(SUBJECT*);
- #endif
- while (p_root.subject_cnt--) {
- if (string_ptr >= string_end) {
- log_error("error unpacking subject strings.\n");
- last_subject->link = Null(SUBJECT*);
- return 0;
- }
- *subj_ptr++ = subject = (SUBJECT*)safemalloc(sizeof (SUBJECT));
- last_subject->link = subject;
- give_string_to(subject->str);
- subj_tally += *subjp;
- subject->count = *subjp++;
- last_subject = subject;
- }
- last_subject->link = Null(SUBJECT*);
- }
- if (subj_ptr != subject_array + total.subject
- || subj_tally > total.article
- || string_ptr != string_end) {
- log_error("subject data is invalid.\n");
- return 0;
- }
- safefree(&subject_cnts);
- safefree(&strings);
-
- return 1;
- }
-
- bool invalid_data;
-
- /* 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 thread file: %d [%ld]\n", num, art_num);
- invalid_data = TRUE;
- 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 thread file: %d [%ld]\n", num, art_num);
- invalid_data = TRUE;
- 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.
- */
- ARTICLE *
- valid_node(relative_offset, num)
- WORD relative_offset;
- int num;
- {
- if (!relative_offset) {
- return Nullart;
- }
- num += relative_offset;
- if (num < 0 || num >= total.article) {
- log_error("invalid node offset in thread file.\n");
- invalid_data = TRUE;
- return Nullart;
- }
- return article_array[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 unread
- ** siblings as we go.
- */
- for (count = total.article; count--;) {
- *article_ptr++ = (ARTICLE*)safemalloc(sizeof (ARTICLE));
- }
- invalid_data = FALSE;
- article_ptr = article_array;
- for (count = 0; count < total.article; count++) {
- ret = fread((char*)&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));
- return 0;
- }
- #ifdef TMPTHREAD
- lp_bmap(&p_article.num, 2);
- wp_bmap(&p_article.subject, 8);
- #endif
-
- 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 = valid_node(article->child_cnt ? 1 : 0, count);
- article->siblings = valid_node(p_article.siblings, count);
- if (p_article.root < 0 || p_article.root >= total.root) {
- log_error("invalid root offset in thread file.\n");
- return 0;
- }
- article->root = root_array[p_article.root];
- if (invalid_data) {
- /* (Error already logged.) */
- return 0;
- }
- }
-
- /* We're done with most of the pointer arrays. */
- safefree(&root_array);
- safefree(&subject_array);
-
- return 1;
- }
-
- /* 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)
- || !read_item(&ids,
- (MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) {
- return 0;
- }
- #ifdef TMPTHREAD
- wp_bmap(ids, total.article + total.domain + 1);
- #endif
-
- string_ptr = strings;
- string_end = string_ptr + total.string2;
-
- if (string_end[-1] != '\0') {
- log_error("second string table is invalid.\n");
- return 0;
- }
-
- last = &unk_domain;
- for (i = 0, count = total.domain + 1; count--; i++) {
- if (i) {
- if (string_ptr >= string_end) {
- log_error("error unpacking domain strings.\n");
- free_partial:
- last->link = Null(DOMAIN*);
- article = unk_domain.ids;
- while (article) {
- safefree(&article->id);
- article = article->id_link;
- }
- domain = unk_domain.link;
- while (domain) {
- free(domain->name);
- article = domain->ids;
- while (article) {
- safefree(&article->id);
- article = article->id_link;
- }
- last = domain;
- domain = domain->link;
- free(last);
- }
- return 0;
- }
- domain = (DOMAIN*)safemalloc(sizeof (DOMAIN));
- give_string_to(domain->name);
- last->link = domain;
- } else {
- domain = &unk_domain;
- }
- if (ids[i] == -1) {
- domain->ids = Nullart;
- } else {
- if (ids[i] < 0 || ids[i] >= total.article) {
- id_error:
- log_error("error in id array.\n");
- domain->ids = Nullart;
- goto free_partial;
- }
- article = article_array[ids[i]];
- domain->ids = article;
- for (;;) {
- if (string_ptr >= string_end) {
- log_error("error unpacking domain strings.\n");
- article->id = Nullch;
- article->id_link = Nullart;
- goto free_partial;
- }
- give_string_to(article->id);
- article->domain = domain;
- if (++i >= total.article + total.domain + !count) {
- log_error("overran id array unpacking domains.\n");
- article->id_link = Nullart;
- goto free_partial;
- }
- if (ids[i] != -1) {
- if (ids[i] < 0 || ids[i] >= total.article) {
- goto id_error;
- }
- article = article->id_link = article_array[ids[i]];
- } else {
- article->id_link = Nullart;
- break;
- }
- }
- }
- last = domain;
- }
- last->link = Null(DOMAIN*);
- safefree(&ids);
- safefree(&strings);
-
- 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];
- }
- safefree(&author_array);
- safefree(&article_array);
- }
-
- /* A shorthand 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;
- }
-