home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / libnov / lib.c next >
C/C++ Source or Header  |  1993-06-30  |  7KB  |  297 lines

  1. /*
  2.  * library to access news history adjunct data
  3.  * See the file COPYRIGHT for the copyright notice.
  4.  */
  5.  
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <fgetmfs.h>
  9. #include <fgetfln.h>
  10. #include <hash.h>
  11. #include <hdbm.h>
  12. #include "newsoverview.h"
  13.  
  14. #define NEWSARTS "/usr/spool/news"    /* default news spool */
  15.  
  16. #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  17.  
  18. /* imports */
  19. extern char *malloc(), *strsave(), *str3save();
  20. extern char *progname;
  21.  
  22. static char *newsarts = NEWSARTS;    /* news spool */
  23.  
  24. novartdir(dir)
  25. char *dir;
  26. {
  27.     newsarts = (dir == NULL? NEWSARTS: dir);
  28. }
  29.  
  30. static struct novgroup *            /* malloced */
  31. novnew()
  32. {
  33.     register struct novgroup *gp = (struct novgroup *)malloc(sizeof *gp);
  34.  
  35.     if (gp != NULL) {
  36.         gp->g_first = gp->g_curr = NULL;
  37.         gp->g_msgids = gp->g_roots = NULL;
  38.         gp->g_dir = NULL;
  39.         gp->g_stream = NULL;
  40.     }
  41.     return gp;
  42. }
  43.  
  44. struct novgroup *                /* malloced cookie */
  45. novopen(grp)                    /* change to group grp */
  46. char *grp;
  47. {
  48.     register struct novgroup *gp = novnew();
  49.     register char *sgrp;
  50.  
  51.     if (gp == NULL)
  52.         return NULL;
  53.     sgrp = strsave(grp);
  54.     if (sgrp == NULL) {
  55.         free((char *)gp);
  56.         return NULL;
  57.     }
  58.     mkfilenm(sgrp);
  59.     gp->g_dir = str3save(newsarts, "/", sgrp);
  60.     free(sgrp);
  61.     return gp;
  62. }
  63.  
  64. struct novgroup *
  65. novstream(fp)
  66. register FILE *fp;
  67. {
  68.     register struct novgroup *gp = novnew();
  69.  
  70.     if (gp != NULL)
  71.         gp->g_stream = fp;
  72.     return gp;
  73. }
  74.  
  75. struct novart *
  76. novall(gp)
  77. register struct novgroup *gp;
  78. {
  79.     if (gp->g_first == NULL)    /* new group? */
  80.         (void) prsoverview(gp);
  81.     return gp->g_first;
  82. }
  83.  
  84. struct novart *
  85. novnext(gp)
  86. register struct novgroup *gp;            /* cookie from novopen */
  87. {
  88.     register struct novart *thisart;
  89.  
  90.     if (gp->g_first == NULL)    /* new group? */
  91.         (void) prsoverview(gp);
  92.     thisart = gp->g_curr;
  93.     if (thisart != NULL)
  94.         gp->g_curr = thisart->a_nxtnum;
  95.     return thisart;
  96. }
  97.  
  98. static
  99. freeart(art)
  100. register struct novart *art;
  101. {
  102.     if (art->a_refs != NULL)
  103.         free(art->a_refs);
  104.     if (art->a_parent != NULL)
  105.         free(art->a_parent);
  106.     if (art->a_num != NULL)
  107.         free(art->a_num);    /* the original input line, chopped */
  108.     free((char *)art);
  109. }
  110.  
  111. #define MAXFIELDS 9        /* last field is "other" fields */
  112. #define DEFREFS 20
  113.  
  114. #define PRSFAIL 0        /* disaster (out of memory, etc.) */
  115. #define PRSOKAY 1
  116. #define PRSBAD  2        /* bad syntax */
  117.  
  118. static int
  119. prsovline(line, gp, art, prevart)
  120. register char *line;        /* malloced; will be chopped up */
  121. register struct novgroup *gp;
  122. register struct novart *art, *prevart;
  123. {
  124.     register int nf, nrefs, len;
  125.     char *fields[MAXFIELDS], *refs[DEFREFS];
  126.     char **refsp = refs;
  127.     static struct novart zart;
  128.  
  129.     *art = zart;        /* make freeart safe if we bail out early */
  130.     len = strlen(line);
  131.     if (len > 0 && line[len-1] == '\n')
  132.         line[len-1] = '\0';    /* make field count straightforward */
  133.     nf = split(line, fields, MAXFIELDS, "\t");
  134.     if (nf < MAXFIELDS - 1)    /* only "others" fields are optional */
  135.         return PRSBAD;    /* skip this line */
  136.     while (nf < MAXFIELDS)
  137.         fields[nf++] = "";    /* fake missing fields */
  138.  
  139.     /*
  140.      * duplicate message-ids would confuse the threading code and anyway
  141.      * should not happen (now that relaynews suppresses multiple links
  142.      * within a group for the same article), so ignore any entries for
  143.      * duplicate message-ids.
  144.      */
  145.     if (hashfetch(gp->g_msgids, fields[4]) != NULL)
  146.         return PRSBAD;
  147.  
  148.     art->a_parent = NULL;
  149.     art->a_refs = strsave(fields[5]); /* fields[5] will be split below */
  150.     if (art->a_refs == NULL)
  151.         return PRSFAIL;
  152.     if (art->a_refs[0] != '\0') {    /* at least one ref? */
  153.         nrefs = awksplit(fields[5], &refsp, DEFREFS, "");
  154.         if (refsp == NULL)
  155.             return PRSFAIL;
  156.         if (nrefs > 0) {    /* last ref is parent */
  157.             if (refsp[nrefs - 1] == NULL)
  158.                 return PRSFAIL;
  159.             art->a_parent = strsave(refsp[nrefs - 1]);
  160.             if (art->a_parent == NULL)
  161.                 return PRSFAIL;
  162.             if (refsp != refs)
  163.                 free((char *)refsp);
  164.         }
  165.     }
  166.     art->a_num = fields[0];        /* line */
  167.     art->a_subj = fields[1];
  168.     art->a_from = fields[2];
  169.     art->a_date = fields[3];
  170.     art->a_msgid = fields[4];
  171.     /* see above for fields[5] */
  172.     art->a_bytes = fields[6];
  173.     art->a_lines = fields[7];
  174.     art->a_others = fields[8];
  175.     art->a_nxtnum = NULL;
  176.  
  177.     if (!hashstore(gp->g_msgids, art->a_msgid, (char *)art))
  178.         return PRSFAIL;
  179.     if (gp->g_first == NULL)
  180.         gp->g_first = art;
  181.     if (prevart != NULL)
  182.         prevart->a_nxtnum = art;
  183.     return PRSOKAY;
  184. }
  185.  
  186. static int
  187. prsoverview(gp)
  188. register struct novgroup *gp;            /* cookie from novopen */
  189. {
  190.     register struct novart *art, *prevart = NULL;
  191.     register int prssts;
  192.     char *line;
  193.  
  194.     gp->g_curr = gp->g_first = NULL;
  195.     if (gp->g_dir == NULL && gp->g_stream == NULL)
  196.         return 0;
  197.     if (gp->g_stream == NULL) {
  198.         line = str3save(gp->g_dir, "/", ".overview");
  199.         if (line == NULL)
  200.             return 0;
  201.         gp->g_stream = fopen(line, "r");
  202.         free(line);
  203.         if (gp->g_stream == NULL)
  204.             return 0;
  205.     }
  206.  
  207.     /* parse input and store in gp->g_msgids for later traversal */
  208.     gp->g_msgids = hashcreate(200, (unsigned (*)())NULL);
  209.     if (gp->g_msgids == NULL) {
  210.         if (gp->g_dir != NULL)        /* we opened the stream? */
  211.             (void) fclose(gp->g_stream);
  212.         return 0;
  213.     }
  214.  
  215.     while ((line = fgetms(gp->g_stream)) != NULL) {
  216.         art = (struct novart *)malloc(sizeof *art);
  217.         if (art == NULL ||
  218.             (prssts = prsovline(line, gp, art, prevart)) == PRSFAIL) {
  219.             if (gp->g_dir != NULL)    /* we opened the stream? */
  220.                 (void) fclose(gp->g_stream);
  221.             if (art != NULL)
  222.                 freeart(art);
  223.             return 0;
  224.         }
  225.         if (prssts == PRSOKAY)
  226.             prevart = art;
  227.         else
  228.             freeart(art);
  229.     }
  230.     if (gp->g_dir != NULL)        /* we opened the stream? */
  231.         (void) fclose(gp->g_stream);
  232.     gp->g_curr = gp->g_first;
  233.     return 1;
  234. }
  235.  
  236. /*
  237.  * if this article has no parent, enter it in the roots hash table.
  238.  * if it has a parent, make this article the parent's first child,
  239.  * even it means making the existing first child our first sibling.
  240.  */
  241. /* ARGSUSED */
  242. static
  243. numvisit(key, data, hook)
  244. char *key, *data, *hook;
  245. {
  246.     register struct novart *art = (struct novart *)data, *parent = NULL;
  247.     register char *msgid;
  248.     register struct novgroup *gp = (struct novgroup *)hook;
  249.  
  250.     if (gp->g_roots == NULL) {
  251.         gp->g_roots = hashcreate(500, (unsigned (*)())NULL);
  252.         if (gp->g_roots == NULL)    /* better not happen */
  253.             return;
  254.     }
  255.  
  256.     msgid = art->a_msgid;
  257.     if (art->a_parent != NULL)
  258.         parent = (struct novart *)hashfetch(gp->g_msgids, art->a_parent);
  259.     if (parent != NULL) {
  260.         if (parent->a_child1 != NULL) {
  261.             if (art->a_sibling != NULL)
  262.                 return;    /* sibling in use; better not happen */
  263.             art->a_sibling = parent->a_child1;
  264.         }
  265.         parent->a_child1 = msgid;
  266.     } else {                /* no parent - must be a root */
  267.         art->a_parent = NULL;
  268.         if (!hashstore(gp->g_roots, msgid, (char *)art))
  269.             return;        /* better not happen */
  270.     }
  271. }
  272.  
  273. novthread(gp)
  274. register struct novgroup *gp;
  275. {
  276.     if (gp->g_first == NULL)    /* new group? */
  277.         (void) prsoverview(gp);
  278.     /* build trees */
  279.     if (gp->g_first != NULL)
  280.         hashwalk(gp->g_msgids, numvisit, (char *)gp);
  281. }
  282.  
  283. novclose(gp)
  284. register struct novgroup *gp;
  285. {
  286.     register struct novart *art, *next;
  287.  
  288.     hashdestroy(gp->g_msgids);
  289.     hashdestroy(gp->g_roots);
  290.     if (gp->g_dir != NULL)
  291.         free(gp->g_dir);
  292.     for (art = gp->g_first; art != NULL; art = next) {
  293.         next = art->a_nxtnum;
  294.         freeart(art);
  295.     }
  296. }
  297.