home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume27 / trn-3.3 / part04 / rt-process.c < prev    next >
C/C++ Source or Header  |  1993-11-27  |  13KB  |  488 lines

  1. /* $Id: rt-process.c,v 3.0 1992/12/14 00:14:13 davison Trn $
  2. */
  3. /* The authors make no claims as to the fitness or correctness of this software
  4.  * for any use whatsoever, and it is provided as is. Any use of this software
  5.  * is at the user's own risk. 
  6.  */
  7.  
  8. #include "EXTERN.h"
  9. #include "common.h"
  10. #include "intrp.h"
  11. #include "trn.h"
  12. #include "cache.h"
  13. #include "bits.h"
  14. #include "final.h"
  15. #include "ng.h"
  16. #include "ngdata.h"
  17. #include "rcln.h"
  18. #include "util.h"
  19. #include "kfile.h"
  20. #include "hash.h"
  21. #include "rthread.h"
  22. #include "rt-select.h"
  23.  
  24. extern HASHTABLE *msgid_hash;
  25.  
  26. static char *valid_message_id _((char*, char*));
  27. static void merge_threads _((SUBJECT*, SUBJECT*));
  28. static void link_child _((ARTICLE*));
  29. static void unlink_child _((ARTICLE*));
  30.  
  31. /* This depends on art being set to the current article number.
  32. */
  33. ARTICLE *
  34. allocate_article(artnum)
  35. ART_NUM artnum;
  36. {
  37.     register ARTICLE *article;
  38.  
  39.     /* create an new article */
  40.     if (artnum >= absfirst) {
  41.     if (artnum > lastart)
  42.         grow_cache(artnum);
  43.     article = article_ptr(artnum);
  44.     } else {
  45.     article = (ARTICLE *)safemalloc(sizeof (ARTICLE));
  46.     bzero((char*)article, sizeof (ARTICLE));
  47.     article->flags |= AF_READ|AF_FAKE|AF_TMPMEM;
  48.     }
  49.     return article;
  50. }
  51.  
  52. void
  53. fix_msgid(msgid)
  54. char *msgid;
  55. {
  56.     register char *cp;
  57.  
  58.     if ((cp = index(msgid, '@')) != Nullch) {
  59.     while (*++cp) {
  60.         if (isupper(*cp)) {
  61.         *cp = tolower(*cp);    /* lower-case domain portion */
  62.         }
  63.     }
  64.     }
  65. }
  66.  
  67. int
  68. msgid_cmp(key, keylen, data)
  69. char *key;
  70. int keylen;
  71. HASHDATUM data;
  72. {
  73.     ARTICLE *article = (data.dat_ptr? (ARTICLE*)data.dat_ptr
  74.                     : article_ptr(data.dat_len));
  75.     /* We already know that the lengths are equal, just compare the strings */
  76.     return bcmp(key, article->msgid, keylen);
  77. }
  78.  
  79. SUBJECT *fake_had_subj; /* the fake-turned-real article had this subject */
  80.  
  81. bool
  82. valid_article(article)
  83. ARTICLE *article;
  84. {
  85.     ARTICLE *ap, *fake_ap;
  86.     char *msgid = article->msgid;
  87.     HASHDATUM data;
  88.  
  89.     if (msgid) {
  90.     fix_msgid(msgid);
  91.     data = hashfetch(msgid_hash, msgid, strlen(msgid));
  92.     if ((fake_ap = (ARTICLE*)data.dat_ptr) == Nullart) {
  93.         if (!data.dat_len) {
  94.         data.dat_len = article_num(article);
  95.         hashstorelast(data);
  96.         fake_had_subj = Nullsubj;
  97.         return TRUE;
  98.         }
  99.         if (data.dat_len == article_num(article)) {
  100.         fake_had_subj = Nullsubj;
  101.         return TRUE;
  102.         }
  103.     }
  104.  
  105.     /* Whenever we replace a fake art with a real one, it's a lot of work
  106.     ** cleaning up the references.  Fortunately, this is not often. */
  107.     if (fake_ap) {
  108.         article->parent = fake_ap->parent;
  109.         article->child1 = fake_ap->child1;
  110.         article->sibling = fake_ap->sibling;
  111.         fake_had_subj = fake_ap->subj;
  112.         if (fake_ap->flags & AF_AUTOFLAGS) {
  113.         article->flags |= fake_ap->flags & AF_AUTOFLAGS;
  114.         localkf_changes = 2;
  115.         }
  116.         if (curr_artp == fake_ap) {
  117.         curr_artp = article;
  118.         curr_art = article_num(article);
  119.         }
  120.         if (recent_artp == fake_ap) {
  121.         recent_artp = article;
  122.         recent_art = article_num(article);
  123.         }
  124.         if ((ap = article->parent) != Nullart) {
  125.         if (ap->child1 == fake_ap)
  126.             ap->child1 = article;
  127.         else {
  128.             ap = ap->child1;
  129.             goto sibling_search;
  130.         }
  131.         } else if (fake_had_subj) {
  132.         register SUBJECT *sp = fake_had_subj;
  133.         if ((ap = sp->thread) == fake_ap) {
  134.             do {
  135.             sp->thread = article;
  136.             sp = sp->thread_link;
  137.             } while (sp != fake_had_subj);
  138.         } else {
  139.           sibling_search:
  140.             while (ap->sibling) {
  141.             if (ap->sibling == fake_ap) {
  142.                 ap->sibling = article;
  143.                 break;
  144.             }
  145.             ap = ap->sibling;
  146.             }
  147.         }
  148.         }
  149.         for (ap = article->child1; ap; ap = ap->sibling)
  150.         ap->parent = article;
  151.         clear_article(fake_ap);
  152.         free((char*)fake_ap);
  153.         data.dat_ptr = Nullch;
  154.         data.dat_len = article_num(article);
  155.         hashstorelast(data);
  156.         return TRUE;
  157.     }
  158.     }
  159.     /* Forget about the duplicate message-id or bogus article. */
  160.     uncache_article(article,TRUE);
  161.     return FALSE;
  162. }
  163.  
  164. /* Take a message-id and see if we already know about it.  If so, return
  165. ** the article, otherwise create a fake one.
  166. */
  167. ARTICLE *
  168. get_article(msgid)
  169. char *msgid;
  170. {
  171.     register ARTICLE *article;
  172.     HASHDATUM data;
  173.  
  174.     fix_msgid(msgid);
  175.  
  176.     data = hashfetch(msgid_hash, msgid, strlen(msgid));
  177.     if (!(article = (ARTICLE *)data.dat_ptr)) {
  178.     if (data.dat_len)
  179.         article = article_ptr(data.dat_len);
  180.     else {
  181.         article = allocate_article(0);
  182.         data.dat_ptr = (char*)article;
  183.         article->msgid = savestr(msgid);
  184.         hashstorelast(data);
  185.     }
  186.     }
  187.     return article;
  188. }
  189.  
  190. /* Take all the data we've accumulated about the article and shove it into
  191. ** the article tree at the best place we can deduce.
  192. */
  193. void
  194. thread_article(article)
  195. ARTICLE *article;
  196. {
  197.     register ARTICLE *ap, *last;
  198.     register char *cp, *end;
  199.     ARTICLE *kill_ap = ((article->flags & AF_AUTOKILL)? article : Nullart);
  200.     int select_this_art = article->flags
  201.     | (article->subj->articles? article->subj->articles->flags : 0);
  202.  
  203.     /* We're definitely not a fake anymore */
  204.     article->flags = (article->flags & ~AF_FAKE) | AF_THREADED;
  205.  
  206.     /* If the article was already part of an existing thread, unlink it
  207.     ** to try to put it in the best possible spot.
  208.     */
  209.     if (fake_had_subj) {
  210.     if (fake_had_subj->thread != article->subj->thread) {
  211.         fake_had_subj->flags &= ~SF_THREAD;
  212.         merge_threads(fake_had_subj, article->subj);
  213.     }
  214.     /* Check for a real or shared-fake parent */
  215.     ap = article->parent;
  216.     while (ap && (ap->flags&AF_FAKE) == AF_FAKE && !ap->child1->sibling)
  217.         ap = ap->parent;
  218.     unlink_child(article);
  219.     if (ap) {            /* do we have decent parents? */
  220.         /* Yes: assume that our references are ok, and just reorder us
  221.         ** with our siblings by date.
  222.         */
  223.         link_child(article);
  224.         /* Freshen the date & subject in any faked parent articles. */
  225.         for (ap = article->parent;
  226.          ap && (ap->flags&AF_FAKE)==AF_FAKE && article->date < ap->date;
  227.          ap = ap->parent)
  228.         {
  229.         ap->date = article->date;
  230.         ap->subj = article->subj;
  231.         unlink_child(ap);
  232.         link_child(ap);
  233.         }
  234.         goto exit;
  235.     }
  236.     /* We'll assume that this article has as good or better references
  237.     ** than the child that faked us initially.  Free the fake reference-
  238.     ** chain and process our references as usual.
  239.     */
  240.     for (ap = article->parent; ap; ap = last) {
  241.         unlink_child(ap);
  242.         last = ap->parent;
  243.         ap->date = 0;
  244.         ap->subj = 0;
  245.         ap->parent = 0;
  246.         /* don't free it until group exit since we probably re-use it */
  247.     }
  248.     article->parent = Nullart;        /* neaten up */
  249.     article->sibling = Nullart;
  250.     }
  251.  
  252.     /* If we have references, process them from the right end one at a time
  253.     ** until we either run into somebody, or we run out of references.
  254.     */
  255.     if (*references) {
  256.     last = article;
  257.     ap = Nullart;
  258.     end = references + strlen(references) - 1;
  259.     while ((cp = rindex(references, '<')) != Nullch) {
  260.         while (end >= cp && ((unsigned char)*end <= ' ' || *end == ',')) {
  261.         end--;
  262.         }
  263.         end[1] = '\0';
  264.         /* Quit parsing references if this one is garbage. */
  265.         if (!(end = valid_message_id(cp, end)))
  266.         break;
  267.         /* Dump all domains that end in '.', such as "..." & "1@DEL." */
  268.         if (end[-1] == '.')
  269.         break;
  270.         ap = get_article(cp);
  271.         *cp = '\0';
  272.         select_this_art |= ap->flags;
  273.         if (ap->flags & AF_AUTOKILL)
  274.         kill_ap = ap;
  275.  
  276.         /* Check for duplicates on the reference line.  Brand-new data has
  277.         ** no date.  Data we just allocated earlier on this line has a
  278.         ** date but no subj.  Special-case the article itself, since it
  279.         ** does have a subj.
  280.         */
  281.         if ((ap->date && !ap->subj) || ap == article) {
  282.         if ((ap = last) == article)
  283.             ap = Nullart;
  284.         continue;
  285.         }
  286.         last->parent = ap;
  287.         link_child(last);
  288.         if (ap->subj)
  289.         break;
  290.  
  291.         ap->date = article->date;
  292.         last = ap;
  293.         end = cp-1;
  294.     }
  295.     if (!ap)
  296.         goto no_references;
  297.  
  298.     /* Check if we ran into anybody that was already linked.  If so, we
  299.     ** just use their thread.
  300.     */
  301.     if (ap->subj) {
  302.         /* See if this article spans the gap between what we thought
  303.         ** were two different threads.
  304.         */
  305.         if (article->subj->thread != ap->subj->thread)
  306.         merge_threads(ap->subj, article->subj);
  307.     } else {
  308.         /* We didn't find anybody we knew, so either create a new thread
  309.         ** or use the article's thread if it was previously faked.
  310.         */
  311.         ap->subj = article->subj;
  312.         link_child(ap);
  313.     }
  314.     /* Set the subj of faked articles we created as references. */
  315.     for (ap = article->parent; ap && !ap->subj; ap = ap->parent)
  316.         ap->subj = article->subj;
  317.  
  318.     /* Make sure we didn't circularly link to a child article(!), by
  319.     ** ensuring that we run off the top before we run into ourself.
  320.     */
  321.     while (ap && ap->parent != article)
  322.         ap = ap->parent;
  323.     if (ap) {
  324.         /* Ugh.  Someone's tweaked reference line with an incorrect
  325.         ** article-order arrived first, and one of our children is
  326.         ** really one of our ancestors. Cut off the bogus child branch
  327.         ** right where we are and link it to the thread.
  328.         */
  329.         unlink_child(ap);
  330.         ap->parent = Nullart;
  331.         link_child(ap);
  332.     }
  333.     } else {
  334.       no_references:
  335.     /* The article has no references.  Either turn it into a new thread
  336.     ** or re-attach the fleshed-out article to its old thread.
  337.     */
  338.     link_child(article);
  339.     }
  340. exit:
  341.     if (!(article->flags & AF_CACHED))
  342.     cache_article(article);
  343.     if (select_this_art & AF_AUTOSELECTALL) {
  344.     if (sel_mode == SM_THREAD)
  345.         select_thread(article->subj->thread, AF_AUTOSELECTALL);
  346.     else
  347.         select_subject(article->subj, AF_AUTOSELECTALL);
  348.     } else if (select_this_art & AF_AUTOSELECT)
  349.     select_subthread(article, AF_AUTOSELECT);
  350.     if (kill_ap)
  351.     kill_subthread(kill_ap, KF_ALL|KF_KILLFILE);
  352. }
  353.  
  354. /* Check if the string we've found looks like a valid message-id reference.
  355. */
  356. static char *
  357. valid_message_id(start, end)
  358. register char *start, *end;
  359. {
  360.     char *mid;
  361.  
  362.     if (start == end)
  363.     return 0;
  364.  
  365.     if (*end != '>') {
  366.     /* Compensate for space cadets who include the header in their
  367.     ** subsitution of all '>'s into another citation character.
  368.     */
  369.     if (*end == '<' || *end == '-' || *end == '!' || *end == '%'
  370.      || *end == ')' || *end == '|' || *end == ':' || *end == '}'
  371.      || *end == '*' || *end == '+' || *end == '#' || *end == ']'
  372.      || *end == '@' || *end == '$') {
  373.         *end = '>';
  374.     }
  375.     } else if (end[-1] == '>') {
  376.     *(end--) = '\0';
  377.     }
  378.     /* Id must be "<...@...>" */
  379.     if (*start != '<' || *end != '>' || (mid = index(start, '@')) == Nullch
  380.      || mid == start+1 || mid+1 == end) {
  381.     return 0;
  382.     }
  383.     return end;
  384. }
  385.  
  386. /* Remove an article from its parent/siblings.  Leave parent pointer intact.
  387. */
  388. static void
  389. unlink_child(child)
  390. register ARTICLE *child;
  391. {
  392.     register ARTICLE *last;
  393.  
  394.     if (!(last = child->parent)) {
  395.     register SUBJECT *sp = child->subj;
  396.     if ((last = sp->thread) == child) {
  397.         do {
  398.         sp->thread = child->sibling;
  399.         sp = sp->thread_link;
  400.         } while (sp != child->subj);
  401.     } else
  402.         goto sibling_search;
  403.     } else {
  404.     if (last->child1 == child)
  405.         last->child1 = child->sibling;
  406.     else {
  407.         last = last->child1;
  408.       sibling_search:
  409.         while (last->sibling != child)
  410.         last = last->sibling;
  411.         last->sibling = child->sibling;
  412.     }
  413.     }
  414. }
  415.  
  416. /* Link an article to its parent article.  If its parent pointer is zero,
  417. ** link it to its thread.  Sorts siblings by date.
  418. */
  419. static void
  420. link_child(child)
  421. register ARTICLE *child;
  422. {
  423.     register ARTICLE *ap;
  424.  
  425.     if (!(ap = child->parent)) {
  426.     register SUBJECT *sp = child->subj;
  427.     ap = sp->thread;
  428.     if (!ap || child->date < ap->date) {
  429.         do {
  430.         sp->thread = child;
  431.         sp = sp->thread_link;
  432.         } while (sp != child->subj);
  433.         child->sibling = ap;
  434.     } else
  435.         goto sibling_search;
  436.     } else {
  437.     ap = ap->child1;
  438.     if (!ap || child->date < ap->date) {
  439.         child->sibling = ap;
  440.         child->parent->child1 = child;
  441.     } else {
  442.       sibling_search:
  443.         while (ap->sibling && ap->sibling->date <= child->date)
  444.         ap = ap->sibling;
  445.         child->sibling = ap->sibling;
  446.         ap->sibling = child;
  447.     }
  448.     }
  449. }
  450.  
  451. /* Merge all of s2's thread into s1's thread.
  452. */
  453. static void
  454. merge_threads(s1, s2)
  455. SUBJECT *s1, *s2;
  456. {
  457.     register SUBJECT *sp;
  458.     register ARTICLE *t1, *t2;
  459.     int visit_flag;
  460.  
  461.     t1 = s1->thread;
  462.     t2 = s2->thread;
  463.     t1->subj->flags &= ~SF_THREAD;
  464.     if (sel_mode == SM_THREAD)
  465.     visit_flag = (t1->subj->flags | (t2? t2->subj->flags : 0)) & SF_VISIT;
  466.     else
  467.     visit_flag = 0;
  468.     /* Change all of t2's thread pointers to a common lead article */
  469.     sp = s2;
  470.     do {
  471.     sp->thread = t1;
  472.     sp->flags &= ~SF_THREAD;
  473.     sp = sp->thread_link;
  474.     } while (sp != s2);
  475.  
  476.     /* Join the two circular lists together */
  477.     sp = s2->thread_link;
  478.     s2->thread_link = s1->thread_link;
  479.     s1->thread_link = sp;
  480.  
  481.     /* Link each article that was attached to t2 to t1. */
  482.     for (t1 = t2; t1; t1 = t2) {
  483.     t2 = t2->sibling;
  484.     link_child(t1);      /* parent is null, thread is newly set */
  485.     }
  486.     s1->thread->subj->flags |= SF_THREAD | visit_flag;
  487. }
  488.