home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / trn / part06 / mt-read.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-02  |  15.2 KB  |  600 lines

  1. /* $Id: mt-read.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
  2. **
  3. ** $Log: mt-read.c,v $
  4. ** Revision 4.4.3.1  1991/11/22  04:12:15  davison
  5. ** Trn Release 2.0
  6. ** 
  7. */
  8.  
  9. #include "EXTERN.h"
  10. #include "common.h"
  11. #include "threads.h"
  12. #include "mthreads.h"
  13.  
  14. extern char *lib, *rnlib, *mtlib, *spool, *threaddir, *homedir;
  15. extern long first;
  16.  
  17. static FILE *fp_in;
  18.  
  19. void tweak_roots ANSI((void));
  20. void wrap_it_up ANSI((int));
  21.  
  22. /* Attempt to open the thread file.  If it's there, only grab the totals
  23. ** from the start of the file.  This should give them enough information
  24. ** to decide if they need to read the whole thing into memory.
  25. */
  26. int
  27. init_data(filename)
  28. char *filename;
  29. {
  30.     root_root = Null(ROOT*);
  31.     author_root = Null(AUTHOR*);
  32.     unk_domain.ids = Nullart;
  33.     unk_domain.link = Null(DOMAIN*);
  34.  
  35.     if (filename && (fp_in = fopen(filename, FOPEN_RB)) != Nullfp) {
  36. #ifdef SETBUFFER
  37.     setbuffer(fp_in, rwbuf, RWBUFSIZ);
  38. #else
  39. # ifdef SETVBUF
  40.     setvbuf(fp_in, rwbuf, _IOFBF, RWBUFSIZ);
  41. # endif
  42. #endif
  43.     if (fread((char*)&total,1,sizeof (TOTAL),fp_in) == sizeof (TOTAL)) {
  44. #ifdef TMPTHREAD
  45.         lp_bmap(&total.first, 4);
  46.         wp_bmap(&total.root, 5);
  47. #endif
  48.  
  49.         /* If the data looks ok, return success. */
  50.         if (total.last - total.first >= 0 && total.author > 0
  51.          && total.article > 0 && total.subject > 0 && total.domain > 0
  52.          && total.subject <= total.article && total.author <= total.article
  53.          && total.root <= total.article && total.domain <= total.article
  54.          && total.string1 > 0 && total.string2 > 0) {
  55.         return 1;
  56.         }
  57.     }
  58.     bzero(&total, sizeof (TOTAL));
  59.     total.first = first;
  60.     total.last = first - 1;
  61.     return 1;
  62.     }
  63.     bzero(&total, sizeof (TOTAL));
  64.     return 0;
  65. }
  66.  
  67. /* They want everything.  Read in the packed information and transform it
  68. ** into a set of linked structures that is easily manipulated.
  69. */
  70. int
  71. read_data()
  72. {
  73.     /* If this is an empty thread file, simply return success. */
  74.     if (!total.root) {
  75.     fclose(fp_in);
  76.     return 1;
  77.     }
  78.  
  79.     if (read_authors()
  80.      && read_subjects()
  81.      && read_roots()
  82.      && read_articles()
  83.      && read_ids())
  84.     {
  85.     tweak_roots();
  86.     fclose(fp_in);
  87.     return 1;
  88.     }
  89.     /* Something failed.  Safefree takes care of checking if some items
  90.     ** were already freed.  Any partially-allocated structures were freed
  91.     ** before we got here.  All other structures are cleaned up.
  92.     */
  93.     if (root_root) {
  94.     register ROOT *root, *next_root;
  95.     register SUBJECT *subj, *next_subj;
  96.  
  97.     for (root = root_root; root; root = next_root) {
  98.         for (subj = root->subjects; subj; subj = next_subj) {
  99.         next_subj = subj->link;
  100.         free(subj->str);
  101.         free(subj);
  102.         }
  103.         next_root = root->link;
  104.         free(root);
  105.     }
  106.     root_root = Null(ROOT*);
  107.     }
  108.     if (author_array) {
  109.     register int count;
  110.  
  111.     for (count = total.author; count--;) {
  112.         free(author_array[count]->name);
  113.         free(author_array[count]);
  114.     }
  115.     safefree(&author_array);
  116.     author_root = Null(AUTHOR*);
  117.     }
  118.     if (article_array) {
  119.     register int count;
  120.  
  121.     for (count = total.article; count--;) {
  122.         free(article_array[count]);
  123.     }
  124.     safefree(&article_array);
  125.     }
  126.     safefree(&strings);
  127.     safefree(&subject_cnts);
  128.     safefree(&subject_array);
  129.     safefree(&author_cnts);
  130.     safefree(&root_array);
  131.     safefree(&ids);
  132.     unk_domain.ids = Nullart;
  133.     unk_domain.link = Null(DOMAIN*);
  134.     fclose(fp_in);
  135.     return 0;
  136. }
  137.  
  138. /* They don't want to read the data.  Close the file if we opened it.
  139. */
  140. void
  141. dont_read_data(open_flag)
  142. int open_flag;        /* 0 == not opened, 1 == open failed, 2 == open */
  143. {
  144.     if (open_flag == 2) {
  145.     fclose(fp_in);
  146.     bzero(&total, sizeof (TOTAL));
  147.     }
  148. }
  149.  
  150. #define give_string_to(dest)    /* Comment for makedepend to     \
  151.                 ** ignore the backslash above */ \
  152. {\
  153.     register MEM_SIZE len = strlen(string_ptr) + 1;\
  154.     dest = safemalloc(len);\
  155.     bcopy(string_ptr, dest, (int)len);\
  156.     string_ptr += len;\
  157. }
  158.  
  159. char *subject_strings, *string_end;
  160.  
  161. /* The author information is an array of use-counts, followed by all the
  162. ** null-terminated strings crammed together.  The subject strings are read
  163. ** in at the same time, since they are appended to the end of the author
  164. ** strings.
  165. */
  166. int
  167. read_authors()
  168. {
  169.     register int count, author_tally;
  170.     register char *string_ptr;
  171.     register WORD *authp;
  172.     register AUTHOR *author, *last_author, **author_ptr;
  173.  
  174.     if (!read_item(&author_cnts, (MEM_SIZE)total.author * sizeof (WORD))
  175.      || !read_item(&strings, total.string1)) {
  176.     /* (Error already logged.) */
  177.     return 0;
  178.     }
  179. #ifdef TMPTHREAD
  180.     wp_bmap(author_cnts, total.author);
  181. #endif
  182.  
  183.     string_ptr = strings;
  184.     string_end = string_ptr + total.string1;
  185.     if (string_end[-1] != '\0') {
  186.     log_error("first string table is invalid.\n");
  187.     return 0;
  188.     }
  189.  
  190.     /* We'll use this array to point each article at its proper author
  191.     ** (packed values are saved as indexes).
  192.     */
  193.     author_array = (AUTHOR**)safemalloc(total.author * sizeof (AUTHOR*));
  194.     author_ptr = author_array;
  195.  
  196.     authp = author_cnts;
  197.  
  198.     author_tally = 0;
  199. #ifndef lint
  200.     last_author = (AUTHOR*)&author_root;
  201. #else
  202.     last_author = Null(AUTHOR*);
  203. #endif
  204.     for (count = total.author; count; count--) {
  205.     if (string_ptr >= string_end) {
  206.         break;
  207.     }
  208.     *author_ptr++ = author = (AUTHOR*)safemalloc(sizeof (AUTHOR));
  209.     last_author->link = author;
  210.     give_string_to(author->name);
  211.     author_tally += *authp;
  212.     author->count = *authp++;
  213.     last_author = author;
  214.     }
  215.     last_author->link = Null(AUTHOR*);
  216.  
  217.     subject_strings = string_ptr;
  218.  
  219.     safefree(&author_cnts);
  220.  
  221.     if (count || author_tally > total.article) {
  222.     log_error("author unpacking failed.\n");
  223.     for (; count < total.author; count++) {
  224.         free((*--author_ptr)->name);
  225.         free(*author_ptr);
  226.     }
  227.     safefree(&author_array);
  228.     return 0;
  229.     }
  230.     return 1;
  231. }
  232.  
  233. /* The subject values consist of the crammed-together null-terminated strings
  234. ** (already read in above) and the use-count array.  They were saved in the
  235. ** order that the roots require while being unpacked.
  236. */
  237. int
  238. read_subjects()
  239. {
  240.     if (!read_item(&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))) {
  241.     /* (Error already logged.) */
  242.     return 0;
  243.     }
  244. #ifdef TMPTHREAD
  245.     wp_bmap(subject_cnts, total.subject);
  246. #endif
  247.     return 1;
  248. }
  249.  
  250. /* Read in the packed root structures and recreate the linked list versions,
  251. ** processing each root's subjects as we go.  Defer interpretation of article
  252. ** offsets until we unpack the article structures.
  253. */
  254. int
  255. read_roots()
  256. {
  257.     register int count, subj_tally;
  258.     register char *string_ptr;
  259.     register WORD *subjp;
  260.     ROOT *root, *last_root, **root_ptr;
  261.     SUBJECT *subject, *last_subject, **subj_ptr;
  262.     int ret;
  263.  
  264.     /* Use this array when unpacking the article's subject offset. */
  265.     subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*));
  266.     subj_ptr = subject_array;
  267.     /* And this array points the article's root offset at the right spot. */
  268.     root_array = (ROOT**)safemalloc(total.root * sizeof (ROOT*));
  269.     root_ptr = root_array;
  270.  
  271.     subjp = subject_cnts;
  272.     string_ptr = subject_strings;    /* string_end is already set */
  273.  
  274.     subj_tally = 0;
  275. #ifndef lint
  276.     last_root = (ROOT*)&root_root;
  277. #else
  278.     last_root = Null(ROOT*);
  279. #endif
  280.     for (count = total.root; count--;) {
  281.     ret = fread((char*)&p_root, 1, sizeof (PACKED_ROOT), fp_in);
  282.     if (ret != sizeof (PACKED_ROOT)) {
  283.         log_error("failed root read -- %d bytes instead of %d.\n",
  284.         ret, sizeof (PACKED_ROOT));
  285.         return 0;
  286.     }
  287. #ifdef TMPTHREAD
  288.     lp_bmap(&p_root.root_num, 1);
  289.     wp_bmap(&p_root.articles, 3);
  290. #endif
  291.     if (p_root.articles < 0 || p_root.articles >= total.article
  292.      || subj_ptr - subject_array + p_root.subject_cnt > total.subject) {
  293.         log_error("root has invalid values.\n");
  294.         return 0;
  295.     }
  296.     *root_ptr++ = root = (ROOT*)safemalloc(sizeof (ROOT));
  297.     root->link = Null(ROOT*);
  298.     root->articles = Nullart;
  299.     root->seq = p_root.articles;
  300.     root->root_num = p_root.root_num;
  301.     root->thread_cnt = p_root.thread_cnt;
  302.     root->subject_cnt = p_root.subject_cnt;
  303.     last_root->link = root;
  304.     last_root = root;
  305.  
  306. #ifndef lint
  307.     last_subject = (SUBJECT*)&root->subjects;
  308. #else
  309.     last_subject = Null(SUBJECT*);
  310. #endif
  311.     while (p_root.subject_cnt--) {
  312.         if (string_ptr >= string_end) {
  313.         log_error("error unpacking subject strings.\n");
  314.         last_subject->link = Null(SUBJECT*);
  315.         return 0;
  316.         }
  317.         *subj_ptr++ = subject = (SUBJECT*)safemalloc(sizeof (SUBJECT));
  318.         last_subject->link = subject;
  319.         give_string_to(subject->str);
  320.         subj_tally += *subjp;
  321.         subject->count = *subjp++;
  322.         last_subject = subject;
  323.     }
  324.     last_subject->link = Null(SUBJECT*);
  325.     }
  326.     if (subj_ptr != subject_array + total.subject
  327.      || subj_tally > total.article
  328.      || string_ptr != string_end) {
  329.     log_error("subject data is invalid.\n");
  330.     return 0;
  331.     }
  332.     safefree(&subject_cnts);
  333.     safefree(&strings);
  334.  
  335.     return 1;
  336. }
  337.  
  338. bool invalid_data;
  339.  
  340. /* A simple routine that checks the validity of the article's subject value.
  341. ** A -1 means that it is NULL, otherwise it should be an offset into the
  342. ** subject array we just unpacked.
  343. */
  344. SUBJECT *
  345. valid_subject(num, art_num)
  346. WORD num;
  347. long art_num;
  348. {
  349.     if (num == -1) {
  350.     return Null(SUBJECT*);
  351.     }
  352.     if (num < 0 || num >= total.subject) {
  353.     log_error("invalid subject in thread file: %d [%ld]\n", num, art_num);
  354.     invalid_data = TRUE;
  355.     return Null(SUBJECT*);
  356.     }
  357.     return subject_array[num];
  358. }
  359.  
  360. /* Ditto for author checking. */
  361. AUTHOR *
  362. valid_author(num, art_num)
  363. WORD num;
  364. long art_num;
  365. {
  366.     if (num == -1) {
  367.     return Null(AUTHOR*);
  368.     }
  369.     if (num < 0 || num >= total.author) {
  370.     log_error("invalid author in thread file: %d [%ld]\n", num, art_num);
  371.     invalid_data = TRUE;
  372.     return Null(AUTHOR*);
  373.     }
  374.     return author_array[num];
  375. }
  376.  
  377. /* Our parent/sibling information is a relative offset in the article array.
  378. ** zero for none.  Child values are always found in the very next array
  379. ** element if child_cnt is non-zero.
  380. */
  381. ARTICLE *
  382. valid_node(relative_offset, num)
  383. WORD relative_offset;
  384. int num;
  385. {
  386.     if (!relative_offset) {
  387.     return Nullart;
  388.     }
  389.     num += relative_offset;
  390.     if (num < 0 || num >= total.article) {
  391.     log_error("invalid node offset in thread file.\n");
  392.     invalid_data = TRUE;
  393.     return Nullart;
  394.     }
  395.     return article_array[num];
  396. }
  397.  
  398. /* Read the articles into their linked lists.  Point everything everywhere. */
  399. int
  400. read_articles()
  401. {
  402.     register int count;
  403.     register ARTICLE *article, **article_ptr;
  404.     int ret;
  405.  
  406.     /* Build an array to interpret interlinkages of articles. */
  407.     article_array = (ARTICLE**)safemalloc(total.article * sizeof (ARTICLE*));
  408.     article_ptr = article_array;
  409.  
  410.     /* Allocate all the structures up-front so that we can point to unread
  411.     ** siblings as we go.
  412.     */
  413.     for (count = total.article; count--;) {
  414.     *article_ptr++ = (ARTICLE*)safemalloc(sizeof (ARTICLE));
  415.     }
  416.     invalid_data = FALSE;
  417.     article_ptr = article_array;
  418.     for (count = 0; count < total.article; count++) {
  419.     ret = fread((char*)&p_article, 1, sizeof (PACKED_ARTICLE), fp_in);
  420.     if (ret != sizeof (PACKED_ARTICLE)) {
  421.         log_error("failed article read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ARTICLE));
  422.         return 0;
  423.     }
  424. #ifdef TMPTHREAD
  425.     lp_bmap(&p_article.num, 2);
  426.     wp_bmap(&p_article.subject, 8);
  427. #endif
  428.  
  429.     article = *article_ptr++;
  430.     article->num = p_article.num;
  431.     article->date = p_article.date;
  432.     article->subject = valid_subject(p_article.subject, p_article.num);
  433.     article->author = valid_author(p_article.author, p_article.num);
  434.     article->flags = p_article.flags;
  435.     article->child_cnt = p_article.child_cnt;
  436.     article->parent = valid_node(p_article.parent, count);
  437.     article->children = valid_node(article->child_cnt ? 1 : 0, count);
  438.     article->siblings = valid_node(p_article.siblings, count);
  439.     if (p_article.root < 0 || p_article.root >= total.root) {
  440.         log_error("invalid root offset in thread file.\n");
  441.         return 0;
  442.     }
  443.     article->root = root_array[p_article.root];
  444.     if (invalid_data) {
  445.         /* (Error already logged.) */
  446.         return 0;
  447.     }
  448.     }
  449.  
  450.     /* We're done with most of the pointer arrays. */
  451.     safefree(&root_array);
  452.     safefree(&subject_array);
  453.  
  454.     return 1;
  455. }
  456.  
  457. /* Read the message-id strings and attach them to each article.  The data
  458. ** format consists of the mushed-together null-terminated strings (a domain
  459. ** name followed by all its unique-id prefixes) and then the article offsets
  460. ** to which they belong.  The first domain name was omitted, as it is the
  461. ** ".unknown." domain for those truly weird message-id's without '@'s.
  462. */
  463. int
  464. read_ids()
  465. {
  466.     register DOMAIN *domain, *last;
  467.     register ARTICLE *article;
  468.     register char *string_ptr;
  469.     register int i, count;
  470.  
  471.     if (!read_item(&strings, total.string2)
  472.      || !read_item(&ids,
  473.         (MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) {
  474.     return 0;
  475.     }
  476. #ifdef TMPTHREAD
  477.     wp_bmap(ids, total.article + total.domain + 1);
  478. #endif
  479.  
  480.     string_ptr = strings;
  481.     string_end = string_ptr + total.string2;
  482.  
  483.     if (string_end[-1] != '\0') {
  484.     log_error("second string table is invalid.\n");
  485.     return 0;
  486.     }
  487.  
  488.     last = &unk_domain;
  489.     for (i = 0, count = total.domain + 1; count--; i++) {
  490.     if (i) {
  491.         if (string_ptr >= string_end) {
  492.         log_error("error unpacking domain strings.\n");
  493.           free_partial:
  494.         last->link = Null(DOMAIN*);
  495.         article = unk_domain.ids;
  496.         while (article) {
  497.             safefree(article->id);
  498.             article = article->id_link;
  499.         }
  500.         domain = unk_domain.link;
  501.         while (domain) {
  502.             free(domain->name);
  503.             article = domain->ids;
  504.             while (article) {
  505.             safefree(article->id);
  506.             article = article->id_link;
  507.             }
  508.             last = domain;
  509.             domain = domain->link;
  510.             free(last);
  511.         }
  512.         return 0;
  513.         }
  514.         domain = (DOMAIN*)safemalloc(sizeof (DOMAIN));
  515.         give_string_to(domain->name);
  516.         last->link = domain;
  517.     } else {
  518.         domain = &unk_domain;
  519.     }
  520.     if (ids[i] == -1) {
  521.         domain->ids = Nullart;
  522.     } else {
  523.         if (ids[i] < 0 || ids[i] >= total.article) {
  524.           id_error:
  525.         log_error("error in id array.\n");
  526.         domain->ids = Nullart;
  527.         goto free_partial;
  528.         }
  529.         article = article_array[ids[i]];
  530.         domain->ids = article;
  531.         for (;;) {
  532.         if (string_ptr >= string_end) {
  533.             log_error("error unpacking domain strings.\n");
  534.             article->id = Nullch;
  535.             article->id_link = Nullart;
  536.             goto free_partial;
  537.         }
  538.         give_string_to(article->id);
  539.         article->domain = domain;
  540.         if (++i >= total.article + total.domain + !count) {
  541.             log_error("overran id array unpacking domains.\n");
  542.             article->id_link = Nullart;
  543.             goto free_partial;
  544.         }
  545.         if (ids[i] != -1) {
  546.             if (ids[i] < 0 || ids[i] >= total.article) {
  547.             goto id_error;
  548.             }
  549.             article = article->id_link = article_array[ids[i]];
  550.         } else {
  551.             article->id_link = Nullart;
  552.             break;
  553.         }
  554.         }
  555.     }
  556.     last = domain;
  557.     }
  558.     last->link = Null(DOMAIN*);
  559.     safefree(&ids);
  560.     safefree(&strings);
  561.  
  562.     return 1;
  563. }
  564.  
  565. /* And finally, point all the roots at their root articles and get rid
  566. ** of anything left over that was used to aid our unpacking.
  567. */
  568. void
  569. tweak_roots()
  570. {
  571.     register ROOT *root;
  572.  
  573.     for (root = root_root; root; root = root->link) {
  574.     root->articles = article_array[root->seq];
  575.     }
  576.     safefree(&author_array);
  577.     safefree(&article_array);
  578. }
  579.  
  580. /* A shorthand for reading a chunk of the file into a malloc'ed array.
  581. */
  582. int
  583. read_item(dest, len)
  584. char **dest;
  585. MEM_SIZE len;
  586. {
  587.     int ret;
  588.  
  589.     *dest = safemalloc(len);
  590.     ret = fread(*dest, 1, (int)len, fp_in);
  591.     if (ret != len) {
  592.     log_error("only read %ld bytes instead of %ld.\n",
  593.         (long)ret, (long)len);
  594.     free(*dest);
  595.     *dest = Nullch;
  596.     return 0;
  597.     }
  598.     return 1;
  599. }
  600.