home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / db.c < prev    next >
C/C++ Source or Header  |  1996-08-26  |  44KB  |  1,997 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Database access and update
  5.  */
  6.  
  7. #include "config.h"
  8. #include "db.h"
  9.  
  10. #ifdef NOV
  11. #include "hash.h"
  12. #include "hdbm.h"
  13. #include "newsoverview.h"
  14. #endif /* NOV */
  15.  
  16. /* db.c */
  17.  
  18. #ifndef NOV
  19. static char *mk_archive_file __APROTO((register group_header *gh, register char *cp, int logerr));
  20. #endif
  21. static sort_gh __APROTO((group_header **g1, group_header **g2));
  22.  
  23. #ifdef NOV
  24. static void db_init_group __APROTO((group_header *gh, int num));
  25. static void readtimfile __APROTO((void));
  26. static void readactfile __APROTO((void));
  27. static void db_fixup_cross_postings __APROTO((data_header *dhp,
  28.                           data_dynamic_data *ddp,
  29.                           struct novart *artp));
  30.  
  31. static struct novgroup *ngovp;
  32. static struct novart *allarts;
  33. static char **actlist, **grplist;
  34. static HASHTABLE *timtbl;
  35.  
  36. extern long    atol();
  37. extern void novclose();        /* trash last group's data */
  38. #endif
  39.  
  40. #ifdef    CACHE_PURPOSE
  41. #include <ctype.h>
  42.  
  43. struct purp_list {
  44.     char *group_name;
  45.     char *purpose;
  46. } *purp_list;
  47.  
  48. static void cache_purpose __APROTO((void));
  49. static int purp_cnt;
  50. #endif    /* CACHE_PURPOSE */
  51.  
  52. /*
  53.  * String pool allocation
  54.  */
  55. /* Badly broken.  Fix ASAP */
  56.  
  57. typedef struct stlisthdr {
  58.     struct stlist *next;
  59. } stlisthdr_t;
  60.  
  61. typedef struct stlist {
  62.     stlisthdr_t n;
  63.     char str[1];
  64. } stlist_t;
  65.  
  66. static char *strkeep __APROTO((char *s, int hdr, int poolid));
  67. static char *pmalloc __APROTO((int size, int poolid));
  68. static void pfree __APROTO((int poolid));
  69.  
  70. #define    POOL_GRP 0
  71. #define    POOL_PUR 1
  72. #define    POOL_ACT 2
  73. #define    POOL_TIM 3
  74. #define    POOL_MAX 3
  75. #define    POOL_TMP 4
  76.  
  77. import char
  78. *master_directory, *db_directory, *db_data_directory, *news_directory,
  79.   *news_lib_directory;
  80. import int db_data_subdirs;
  81.  
  82. export master_header master;
  83. #ifdef MALLOC_64K_LIMITATION
  84. export group_header **active_groups = NULL;
  85. #else
  86. export group_header *active_groups = NULL;
  87. #endif
  88. export group_header **sorted_groups = NULL;
  89.  
  90. export int  reread_groups_file = 0;  /* nnmaster -G */
  91.  
  92. export int  check_group_access = 0;
  93.  
  94. export data_header db_hdr;
  95. export data_dynamic_data db_data;
  96.  
  97. export int32 db_read_counter = 0;     /* articles read by db_read_art */
  98. export int32 db_write_counter = 0;    /* articles written by db_write_art */
  99.  
  100. /*
  101.  * Init access to a group
  102.  */
  103.  
  104. export group_header *current_group = NULL;
  105.  
  106. export char group_path_name[FILENAME];
  107. export char *group_file_name = NULL;
  108.  
  109. static char *group_position = NULL;
  110. static article_number current_digest_article = 0;
  111.  
  112. int
  113. init_group(gh)
  114. register group_header *gh;
  115. {
  116.     register char *p, *q;
  117.  
  118.     current_digest_article = 0;
  119.  
  120.     if (gh == NULL) 
  121.       return 0;
  122.     /*    if (gh->master_flag & M_IGNORE_GROUP) return 0; */    /* OBS */
  123.     if (gh == current_group) 
  124.       return 1;
  125.  
  126.     current_group = gh;
  127.  
  128.     if (gh->group_flag & G_FOLDER) {
  129.     group_position = NULL;
  130.     group_file_name = NULL;
  131.     strcpy(group_path_name, gh->archive_file);
  132.     return 1;
  133.     }
  134.  
  135. #ifdef NNTP
  136.     if (use_nntp && nntp_set_group(gh) < 0)
  137.     return 0;
  138. #endif /* NNTP */
  139.  
  140.     if (group_position == NULL)
  141.     if (who_am_i == I_AM_MASTER || who_am_i == I_AM_EXPIRE)
  142.         group_position = group_path_name;
  143.     else {
  144.         strcpy(group_path_name, news_directory);
  145.         group_position = group_path_name + strlen(group_path_name);
  146.         *group_position++ = '/';
  147.     }
  148.  
  149.     for (p = group_position, q = gh->group_name; *q; q++)
  150.     *p++ = (*q == '.') ? '/' : *q;
  151.  
  152.     if (who_am_i == I_AM_MASTER) {
  153.  
  154.     /* The master will chdir to the group's directory to get */
  155.     /* better performance (can use relative path names). */
  156.  
  157.     *p++ = NUL;
  158.  
  159.     if (!use_nntp) {
  160.         if (chdir(news_directory) < 0)
  161.         sys_error(news_directory);
  162.  
  163.         if (chdir(group_path_name) < 0)
  164.         return 0;
  165.     }
  166.     group_file_name = group_path_name;
  167.     return 1;
  168.     }
  169.  
  170.     /* client */
  171.     if (gh->master_flag & M_NO_DIRECTORY) 
  172.         return 0;
  173.  
  174.     if (check_group_access && !use_nntp) {
  175.     *p = NUL;
  176.     if (file_exist(group_path_name, "dxr") == 0) 
  177.         return 0;
  178.     }
  179.  
  180.     *p++ = '/';
  181.     *p = NUL;
  182.     group_file_name = p;
  183.     return 1;
  184. }
  185.  
  186. #ifndef NOV
  187. /*
  188.  *    Open master & group file; read it in if first open.
  189.  *
  190.  *    GROUPS file format is:
  191.  *
  192.  *    One line per group:
  193.  *    <group name> [<space><timestamp>] [<space><options>] <NL>
  194.  *    If <timestamp> is omitted, a timestamp of 0 is used (very old group).
  195.  *    If <group name> is "@", the master entry is ignored.
  196.  *
  197.  *    <options>:
  198.  *    D    Digest all articles in the group
  199.  *    N    Never digest articles
  200.  *    @    Bogus group, ignore completely
  201.  *    !    Don't collect this group
  202.  *
  203.  *    Do not edit the GROUPS file while nnmaster is running.
  204.  *    After editing the groups file (options), run nnmaster -G.
  205.  */
  206.  
  207. static FILE *group_file = NULL;
  208.  
  209. int
  210. open_groups(mode)
  211. int mode;
  212. {
  213.     group_file = open_file(relative(db_directory, "GROUPS"), mode);
  214.  
  215.     if (group_file != NULL && (mode & OPEN_CREATE)) {
  216.     fprintf(group_file, 
  217. "#\n#\tNEVER MODIFY THIS FILE WHILE nnmaster IS RUNNING\n");
  218.     fprintf(group_file, 
  219. "#\n#\tRUN 'nnmaster -G' AFTER MODIFYING THIS FILE\n");
  220.     fprintf(group_file, 
  221. "#\n#\tDO NOT REMOVE OR REORDER ANY LINES IN THIS FILE\n#\n");
  222.     }
  223.     
  224.     return group_file != NULL;
  225. }
  226.  
  227. void
  228. close_groups()
  229. {
  230.     if (group_file != NULL) {
  231.     fclose(group_file);
  232.     group_file = NULL;
  233.     }
  234. }
  235.  
  236. void
  237. db_append_group(gh)
  238. register group_header *gh;
  239. {
  240.     char flags[16], *fp;
  241.  
  242.     if (gh->group_name[0] == NUL) {
  243.     fputc('@', group_file);
  244.     goto out;
  245.     }
  246.  
  247.     fprintf(group_file, "%s", gh->group_name);
  248.     if (gh->creation_time > 0)
  249.     fprintf(group_file, " %ld", (long)(gh->creation_time));
  250.  
  251.     fp = flags;
  252.  
  253.     if (gh->master_flag & M_IGNORE_G)
  254.     *fp++ = '!';
  255.     if (gh->master_flag & M_ALWAYS_DIGEST)
  256.     *fp++ = 'D';
  257.     if (gh->master_flag & M_NEVER_DIGEST)
  258.     *fp++ = 'N';
  259.     if (gh->master_flag & M_INCLUDE_OLD)
  260.     *fp++ = 'O';
  261.     if (gh->master_flag & M_AUTO_RECOLLECT)
  262.     *fp++ = 'R';
  263.     if (gh->archive_file != NULL)
  264.     *fp++ = '>';
  265.  
  266.     if (fp != flags) {
  267.     *fp++ = NUL;
  268.     fprintf(group_file, " %s%s", flags,
  269.         gh->archive_file != NULL ? gh->archive_file : "");
  270.     }
  271.  
  272.  out:
  273.     fputc(NL, group_file);
  274. }
  275.  
  276. void
  277. db_rewrite_groups(save_groups, group_list)
  278. int save_groups;
  279. group_header *group_list;
  280. {
  281.     register group_header *gh;
  282.  
  283.     if (save_groups)
  284.     if (save_old_file(relative(db_directory, "GROUPS"), "~") < 0)
  285.         sys_error("Cannot rename GROUPS file");
  286.     
  287.     open_groups(OPEN_CREATE|MUST_EXIST);
  288.  
  289.     if (group_list != NULL) {
  290.     for (gh = group_list->next_group; gh != NULL; gh = gh->next_group)
  291.         db_append_group(gh);
  292.     } else {
  293.     Loop_Groups_Header(gh) {
  294.         db_append_group(gh);
  295.     }
  296.     }
  297.     close_groups();
  298. }
  299.  
  300. static char *mk_archive_file(gh, cp, logerr)
  301. register group_header *gh;
  302. register char *cp;
  303. int logerr;
  304. {
  305.     char *name;
  306.  
  307.     while (*cp && (*cp == '>' || isspace(*cp))) 
  308.         cp++;
  309.  
  310.     name = cp;
  311.     while (*cp && !isspace(*cp)) 
  312.         cp++;
  313.     if (*cp) 
  314.         *cp++ = NUL;
  315.  
  316.     if (*name) {
  317.     gh->archive_file = copy_str(name);
  318.  
  319.     if (*name == '/')
  320.         gh->master_flag |= M_AUTO_ARCHIVE;
  321.     else {
  322.         gh->master_flag &= ~M_AUTO_ARCHIVE;
  323.         if (who_am_i == I_AM_MASTER)
  324.         if (logerr)
  325.             log_entry('E', "GROUPS %s >%s: Full path required",
  326.                   gh->group_name, name);
  327.         else
  328.             printf("Error in GROUPS: %s >%s: Full path required\n",
  329.                gh->group_name, name);
  330.     }
  331.     }
  332.  
  333.     return cp;
  334. }
  335.  
  336. int
  337. db_parse_group(gh, trust_master)
  338. register group_header *gh;
  339. int trust_master;        /* trust what is in the master file */
  340. {
  341.     char line[256];
  342.     register char *cp, *name;
  343.     int ignore;
  344.  
  345.     do {
  346.     if (fgets(line, 256, group_file) == NULL) return 0;
  347.     for (cp = line; *cp && isspace(*cp); cp++);
  348.     } while (*cp == NUL || *cp == '#');
  349.  
  350.     gh->archive_file = NULL;
  351.  
  352.     name = cp;
  353.  
  354.     if (trust_master) {
  355.     if (gh->group_name_length == 0) {
  356.         gh->group_name = "";
  357.         return 1;
  358.     }
  359.     cp = name + gh->group_name_length;
  360.     if (*cp == NUL || !isspace(*cp))
  361.         sys_error("MASTER/GROUPS conflict: %d/%s", gh->group_num, line);
  362.     } else {
  363.     /* parse GROUPS line */
  364.  
  365.     if (*cp == '@') {
  366.         ignore = 1;
  367.         gh->group_name_length = 0;
  368.         gh->group_name = "";
  369.         goto ignore_group;
  370.     }
  371.  
  372.     if (gh->group_name_length == 0) {
  373.         while (*cp && !isspace(*cp)) cp++;
  374.         gh->group_name_length = cp - name;
  375.     } else {
  376.         cp = name + gh->group_name_length;
  377.         if (*cp == NUL || !isspace(*cp)) {
  378.         sys_error("MASTER/GROUPS conflict: %d/%s", gh->group_num, line);
  379.         }
  380.     }
  381.     }
  382.  
  383.     if (*cp) *cp++ = NUL;
  384.     if (gh->group_name_length > 0)
  385.     gh->group_name = copy_str(name);
  386.     else
  387.     gh->group_name = "";
  388.  
  389.     if (trust_master) {
  390.     if (gh->master_flag & M_AUTO_ARCHIVE) {
  391.         while (*cp && *cp != '>') cp++;
  392.         if (*cp == '>') mk_archive_file(gh, cp, 1);
  393.     }
  394.     return 1;
  395.     }
  396.  
  397.     while (*cp && isspace(*cp)) cp++;
  398.  
  399.     if (*cp && isdigit(*cp)) {
  400.     gh->creation_time = atol(cp);
  401.     while (*cp && isdigit(*cp)) cp++;
  402.     } else
  403.     gh->creation_time = 0;
  404.  
  405.     while (*cp && isspace(*cp)) cp++;
  406.  
  407.     ignore = 0;
  408.     gh->master_flag &= ~(M_ALWAYS_DIGEST | M_NEVER_DIGEST |
  409.             M_AUTO_RECOLLECT | M_AUTO_ARCHIVE);
  410.  
  411.     while (*cp) {
  412.     switch (*cp++) {
  413.      case ' ':
  414.      case '\t':
  415.      case NL:
  416.      case CR:
  417.         continue;
  418.  
  419.      case 'D':    /* Collect this group, digest all articles */
  420.         gh->master_flag |= M_ALWAYS_DIGEST;
  421.         continue;
  422.  
  423.      case 'N':    /* Collect this group, never digest articles */
  424.         gh->master_flag |= M_NEVER_DIGEST;
  425.         continue;
  426.  
  427.      case 'O':    /* Ignore -O option for this group */
  428.         gh->master_flag |= M_INCLUDE_OLD;
  429.         continue;
  430.  
  431.      case 'R':    /* Recollect this group when new articles arrive */
  432.         gh->master_flag |= M_AUTO_RECOLLECT;
  433.         continue;
  434.  
  435.      case '>':    /* Archive all new articles in gh->archive_file */
  436.         cp = mk_archive_file(gh, cp, 0);
  437.         continue;
  438.  
  439.      case '@':    /* Bogus GROUP -- ignore completely */
  440.         ignore = 1;
  441.         gh->group_name_length = 0;
  442.         break;
  443.  
  444.      case '!':    /* Do not collect this group */
  445.      case 'X':
  446.         ignore = 1;
  447.         break;
  448.  
  449.      case '#':    /* comment */
  450.         *cp = NUL;
  451.         break;
  452.  
  453.      default:
  454.         printf("Bad GROUPS flag for %s: `%c'\n", gh->group_name, *--cp);
  455.         break;
  456.     }
  457.     break;
  458.     }
  459.  
  460.  ignore_group:
  461.  
  462.     /* G_DONE indicates to master that the group must be cleaned */
  463.  
  464.     if (ignore) {
  465.     if ((gh->master_flag & M_IGNORE_GROUP) == 0) {
  466.         gh->master_flag |= M_MUST_CLEAN;
  467.         log_entry('X', "Group %s ignored", gh->group_name);
  468.     }
  469.     gh->master_flag |= M_IGNORE_G;
  470.     } else {    /* was group ignored in GROUPS, but not active before? */
  471.     if ((gh->master_flag & M_IGNORE_GROUP) == M_IGNORE_G) {
  472.         gh->master_flag &= ~M_NO_DIRECTORY;
  473.         log_entry('X', "Group %s activated", gh->group_name);
  474.     }
  475.     gh->master_flag &= ~M_IGNORE_G;
  476.     }
  477.     
  478.     return 1;
  479. }
  480.  
  481. static FILE *master_file = NULL;
  482. static int db_sequential = 0;
  483.  
  484. #ifdef APOLLO_DOMAIN_OS
  485. /* make copy of master file to CLIENT, but only once every 100 passes or */
  486. /* if forced */
  487. static make_master_copy(force_copy)
  488. int force_copy;
  489. {
  490.   char client_path[FILENAME];
  491.   static int pass_count = 0;
  492.   int n;
  493.     
  494.   if (!force_copy) {    /* if not forced copy, only do copy every %100 passes */
  495.       pass_count++;
  496.       if (pass_count < 100) {
  497.           return;
  498.       }
  499.   }
  500.   pass_count = 0;
  501.     
  502.   strcpy(client_path,relative(db_directory, "CLIENT"));
  503.   unlink(client_path);
  504.   if ((n = copy_file(relative(db_directory, "MASTER"), client_path, 0)) < 0)
  505.       log_entry('R', "Copy of MASTER to CLIENT failed (err=%d)", n);
  506. }
  507. #endif
  508. #endif /* NOV */
  509.  
  510. #ifdef NOV
  511. /*
  512.  *    Init the groups data from active file.
  513.  */
  514. void
  515. open_master(mode)
  516. int    mode;
  517. {
  518.     register group_header *gh;
  519.  
  520.     freeobj(sorted_groups);
  521.     freeobj(active_groups);
  522.     active_groups = NULL;
  523.     sorted_groups = NULL;
  524.  
  525.     readactfile();        /* Read in the active file - count groups */
  526.     readtimfile();        /* Read the newsgroup creation time file */
  527.  
  528.     db_expand_master();        /* uses count from readact() call! */
  529.   
  530.     Loop_Groups_Header(gh) {
  531.         /* db_read_group opens a file per call; use db_init_group instead */
  532.         db_init_group(gh, l_g_index);
  533.     }
  534.  
  535.     /*
  536.      * Free actlist and timtbl space.
  537.      * Don't free grplist, because active_groups[] has pointers thereto.
  538.      */
  539.     pfree(POOL_ACT);
  540.     free(actlist);
  541.  
  542.     pfree(POOL_TIM);
  543.     free(timtbl);
  544.  
  545.     sort_groups();
  546.  
  547. #ifdef    CACHE_PURPOSE
  548.     cache_purpose();    /* cache sorted newsgroups and descriptions */
  549. #endif
  550. }
  551.  
  552. #else /* NOV */
  553.  
  554. /*
  555.  *    Open master & group files; read then in if first open.
  556.  */
  557.  
  558. void
  559. open_master(mode)
  560. int mode;
  561. {
  562.     register group_header *gh;
  563.     int trust_master;
  564.  
  565.     close_master();
  566.  
  567. #ifdef APOLLO_DOMAIN_OS
  568.     if (who_am_i != I_AM_MASTER && who_am_i != I_AM_ADMIN)
  569.     master_file = open_file(relative(db_directory, "CLIENT"), mode|MUST_EXIST);
  570.     else
  571. #endif
  572.     master_file = open_file(relative(db_directory, "MASTER"), mode|MUST_EXIST);
  573.  
  574.     db_sequential = 0;
  575.     if (mode == OPEN_CREATE) db_sequential = 1;
  576.  
  577.     if (mode != OPEN_READ) return;
  578.  
  579.     db_read_master();
  580.  
  581.     if (who_am_i != I_AM_MASTER && master.db_lock[0])
  582.     nn_exitmsg(88, "DATABASE LOCKED.\n%s\n", master.db_lock);
  583.  
  584.     freeobj(sorted_groups);
  585. #ifdef MALLOC_64K_LIMITATION
  586.     if (active_groups)
  587.     Loop_Groups_Header(gh) freeobj(gh);
  588. #endif
  589.     freeobj(active_groups);
  590.     active_groups = NULL;
  591.     sorted_groups = NULL;
  592.  
  593.     db_expand_master();
  594.  
  595.     open_groups(OPEN_READ|MUST_EXIST);
  596.  
  597.     trust_master = (who_am_i != I_AM_MASTER || !reread_groups_file);
  598.  
  599.     db_sequential = 1;
  600. #ifdef MALLOC_64K_LIMITATION
  601.     Loop_Groups_Number(l_g_index) {
  602.     gh = newobj(group_header, 1);
  603.     active_groups[l_g_index] = gh;
  604. #else
  605.     Loop_Groups_Header(gh) {
  606. #endif
  607.     gh->group_num = l_g_index;
  608.     db_read_group(gh);
  609.     db_parse_group(gh, trust_master);
  610.     }
  611.     db_sequential = 0;
  612.  
  613.     close_groups();
  614.  
  615.     sort_groups();
  616.  
  617. #ifdef    CACHE_PURPOSE
  618.     cache_purpose();    /* cache sorted newsgroups and descriptions */
  619. #endif
  620. }
  621. #endif /* NOV */
  622.  
  623. void
  624. db_expand_master()
  625. {
  626.     master.free_groups = 20;
  627.  
  628. #ifndef NOV
  629. #ifdef MALLOC_64K_LIMITATION
  630.     active_groups = resizeobj(active_groups, group_header *,
  631.                   master.number_of_groups + master.free_groups);
  632. #else
  633.     active_groups = resizeobj(active_groups, group_header,
  634.                   master.number_of_groups + master.free_groups);
  635.     clearobj(active_groups + master.number_of_groups, group_header,
  636.          master.free_groups);
  637. #endif /* MALLOC_64K_LIMITATION */
  638. #else
  639.     active_groups = resizeobj(active_groups, group_header,
  640.                   master.number_of_groups + master.free_groups);
  641.     clearobj(active_groups + master.number_of_groups, group_header,
  642.          master.free_groups);
  643. #endif /* NOV */
  644.     sorted_groups = resizeobj(sorted_groups, group_header * , master.number_of_groups + master.free_groups);
  645. }
  646.  
  647. #if 0
  648. #ifdef NOV
  649. static void
  650. freeup(key, data, hook)
  651. char *key, *data, *hook;
  652. {
  653.     hashdelete((HASHTABLE *)data, key);
  654.     free(key);
  655.     free(data);
  656. }
  657. #endif /* NOV */
  658. #endif /* 0 */
  659.  
  660. void
  661. close_master()
  662. {
  663. #ifdef NOV
  664. #if 0
  665.     if (ngovp) {
  666.     novclose(ngovp);
  667.     ngovp = NULL;
  668.     }
  669. #endif
  670.     
  671. #else /* NOV */
  672.     if (master_file != NULL) {
  673.     fclose(master_file);
  674.     master_file = NULL;
  675.  
  676. #ifdef APOLLO_DOMAIN_OS
  677.     if (who_am_i == I_AM_MASTER) make_master_copy(1);
  678. #endif
  679.     }
  680. #endif /* NOV */
  681. }
  682.  
  683. int
  684. update_group(gh)
  685. group_header *gh;
  686. {
  687. #ifndef NOV
  688.     group_number numg;
  689.  
  690.     numg = master.number_of_groups;
  691.  
  692.     db_read_master();
  693.     master.number_of_groups = numg;
  694.     if (master.db_lock[0]) return -3;
  695. #endif /* NOV */
  696.  
  697. #ifndef NOV
  698.     db_read_group(gh);
  699. #else
  700.     db_read_group(gh, gh->first_a_article, gh->last_a_article);    /* XXX */
  701. #endif
  702.  
  703.     if (gh->master_flag & M_IGNORE_GROUP) return 0;
  704.     if (gh->master_flag & M_BLOCKED) return -1;
  705.  
  706.     return 1;
  707. }
  708.  
  709.  
  710. static int
  711. sort_gh(g1, g2)
  712. group_header **g1, **g2;
  713. {
  714.     return strcmp((*g1)->group_name, (*g2)->group_name);
  715. }
  716.  
  717.  
  718. void
  719. sort_groups()
  720. {
  721.     register group_header *gh;
  722.  
  723.     Loop_Groups_Header(gh)
  724.     sorted_groups[l_g_index] = gh;
  725.  
  726.     quicksort(sorted_groups, master.number_of_groups, group_header *, sort_gh);
  727.  
  728.     s_g_first = 0;
  729.     Loop_Groups_Sorted(gh)
  730.     if (gh->group_name[0] != NUL) {
  731.         s_g_first = l_g_index;
  732.         break;
  733.     }
  734. }
  735.  
  736.  
  737. group_header *lookup_no_alias(name)
  738. char *name;
  739. {
  740.     register i, j, k, t;
  741.  
  742.     i = s_g_first;
  743.     j = master.number_of_groups - 1;
  744.  
  745.     while (i <= j) {
  746.     k = (i + j) / 2;
  747.  
  748.     if ( (t=strcmp(name, sorted_groups[k]->group_name)) > 0)
  749.         i = k+1;
  750.     else
  751.     if (t < 0)
  752.         j = k-1;
  753.     else
  754.         return sorted_groups[k];
  755.     }
  756.  
  757.     return NULL;
  758. }
  759.  
  760. group_header *lookup(name)
  761. char *name;
  762. {
  763.     register group_header *gh;
  764.     group_header *gh_na;
  765.     register int32 n, x;
  766.  
  767.     gh = lookup_no_alias(name);
  768.     if (gh == NULL || (gh->master_flag & M_ALIASED) == 0) return gh;
  769.  
  770.     gh_na = gh;
  771.     x = 16;
  772.     do {
  773.     if (--x == 0) {
  774.         log_entry('R', "Possible alias loop: %s", name);
  775.         return gh_na;
  776.     }
  777.     n = (int32)gh->data_write_offset;
  778.     /* if alias info is unreliable, return original group
  779.        which will be ignored anyway */
  780.     if (n < 0 || n >= master.number_of_groups) {
  781.         log_entry('R', "Bad aliasing of %s -> %d", gh->group_name, n);
  782.         return gh_na;
  783.     }
  784.     gh = ACTIVE_GROUP(n);
  785.     } while (gh->master_flag & M_ALIASED);
  786.  
  787.     return gh;
  788. }
  789.  
  790. int
  791. art_collected(gh, art_num)
  792. group_header *gh;
  793. article_number art_num;
  794. {
  795.     return gh->first_db_article <= art_num && gh->last_db_article >= art_num;
  796. }
  797.  
  798. #ifndef NOV
  799.  
  800. char *db_data_path(namebuf, gh, d_or_x)
  801. char *namebuf;
  802. group_header *gh;
  803. char d_or_x;
  804. {
  805.     register char *cp, *np;
  806.  
  807.     if (db_data_directory != NULL) {
  808. #ifdef DB_LONG_NAMES
  809.     sprintf(namebuf, "%s/%s.%c", db_data_directory, gh->group_name, d_or_x);
  810. #else
  811.     if (db_data_subdirs)
  812.         sprintf(namebuf, "%s/%d/%d.%c", db_data_directory, 
  813.             gh->group_num/100, gh->group_num, d_or_x);
  814.     else
  815.     sprintf(namebuf, "%s/%d.%c", db_data_directory, gh->group_num, d_or_x);
  816. #endif
  817.     } else {
  818.     np = namebuf;
  819.     /* master chdir to the group's directory */
  820.     if (who_am_i != I_AM_MASTER) {
  821.         for (cp = news_directory; (*np = *cp++); np++);
  822.         *np++ = '/';
  823.         for (cp = gh->group_name; *cp; cp++)
  824.         *np++ = *cp == '.' ? '/' : *cp;
  825.         *np++ = '/';
  826.     }
  827.  
  828.     *np++ = '.';
  829.     *np++ = 'n';
  830.     *np++ = 'n';
  831.     *np++ = d_or_x;
  832.     *np++ = NUL;
  833.     }
  834.  
  835.     return namebuf;
  836. }
  837.  
  838. /* STUB */
  839. FILE *open_data_file(gh, d_or_x, mode)
  840. group_header *gh;
  841. char d_or_x;
  842. int mode;
  843. {
  844.     FILE *f;
  845.     char data_file[FILENAME];
  846.  
  847.     db_data_path(data_file, gh, d_or_x);
  848.  
  849.     if (mode == -1) {
  850.     if (unlink(data_file) < 0 && errno != ENOTDIR && errno != ENOENT)
  851.         log_entry('E', "Cannot unlink %s (errno=%d)", data_file, errno);
  852.     f = NULL;
  853.     } else {
  854.      again:
  855.     f = open_file(data_file, (mode & ~MUST_EXIST));
  856.     if (f != NULL) return f;
  857. #ifndef DB_LONG_NAMES
  858.     if (db_data_subdirs && (mode&0xf) == OPEN_CREATE && errno == ENOENT) {
  859.         char *s;
  860.         s = strrchr(data_file, '/');
  861.         *s = NUL;
  862.         if (!file_exist(data_file, "dx")) {
  863.         if (mkdir(data_file, 0755) < 0)
  864.             sys_error("Cannot create directory %s", data_file);
  865.         log_entry('C', "Created directory %s", data_file);
  866.         *s = '/';
  867.         goto again;
  868.         }
  869.         *s = '/';
  870.         errno = ENOENT;
  871.     }
  872. #endif
  873.     if (mode & MUST_EXIST)
  874.         sys_error("%s (%d): cannot open '%c' file (mode=%x, errno=%d)",
  875.               gh->group_name, (int)(gh->group_num), d_or_x,
  876.               mode, errno);
  877.     }
  878.     return f;
  879. }
  880.  
  881. #ifdef NETWORK_DATABASE
  882.  
  883. #define MASTER_FIELDS    5    /* + DB_LOCK_MESSAGE bytes */
  884. #define    GROUP_FIELDS    9
  885. #define    ARTICLE_FIELDS    10
  886.  
  887.  
  888. typedef int32 net_long;
  889.  
  890.  
  891. #ifdef NETWORK_BYTE_ORDER
  892.  
  893. #define net_to_host(buf, n)
  894. #define host_to_net(buf, n)
  895.  
  896. #else
  897.  
  898. static net_to_host(buf, lgt)
  899. register net_long *buf;
  900. int lgt;
  901. {
  902.     while (--lgt >= 0) {
  903.     *buf = ntohl(*buf);
  904.     buf++;
  905.     }
  906. }
  907.  
  908. static host_to_net(buf, lgt)
  909. register net_long *buf;
  910. int lgt;
  911. {
  912.     while (--lgt >= 0) {
  913.     *buf = htonl(*buf);
  914.     buf++;
  915.     }
  916. }
  917.  
  918. #endif /* not NETWORK_BYTE_ORDER */
  919. #endif /* NETWORK_DATABASE */
  920.  
  921. #define NWDB_MAGIC 0x00190000    /* NN#n <-> NW#n */
  922.  
  923.  
  924. void
  925. db_read_master()
  926. {
  927. #ifdef NETWORK_DATABASE
  928.     net_long buf[MASTER_FIELDS];
  929.  
  930.     rewind(master_file);
  931.     if (fread((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  932.     != MASTER_FIELDS) goto err;
  933.     if (fread(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  934.     != DB_LOCK_MESSAGE) goto err;
  935.  
  936.     net_to_host(buf, MASTER_FIELDS);
  937.  
  938.     master.db_magic = buf[0] ^ NWDB_MAGIC;
  939.     master.last_scan = buf[1];
  940.     master.last_size = buf[2];
  941.     master.number_of_groups = buf[3];
  942.     master.db_created = buf[4];
  943. #else
  944.     rewind(master_file);
  945.     if (fread((char *)&master, sizeof(master_header), 1, master_file) != 1)
  946.     goto err;
  947. #endif
  948.  
  949.     if (master.db_magic != NNDB_MAGIC)
  950.     sys_error("Database magic number mismatch");
  951.     return;
  952.  
  953.  err:
  954.     sys_error("Incomplete MASTER file");
  955. }
  956.  
  957. #endif    /* NOV */
  958.  
  959.  
  960. #ifdef NOV
  961.  
  962.  
  963. static void
  964. readactfile()
  965. {
  966.     char actline[512];
  967.     int count = 0;
  968.     int    i;
  969.     FILE *actfp;
  970.     stlist_t *sthead, *stp;
  971.  
  972.     if (actlist != NULL)
  973.     return;
  974.  
  975. #ifdef NNTP 
  976.     if (use_nntp) {
  977.     actfp = nntp_fopen_list("LIST");
  978.     } else
  979. #endif
  980.     actfp = fopen(relative(news_lib_directory, "active"), "r");
  981.  
  982.     if (actfp == NULL) {
  983.     nn_exitmsg(1, "could not fetch active file\n");
  984.     }
  985.  
  986.     /*
  987.      * Snarf all of active up in first pass.  This gives us a count
  988.      * of the groups we can use for internal tables.
  989.      */
  990.     sthead = NULL;
  991. #ifdef NNTP
  992.     while ( use_nntp  ?  nntp_fgets(actline, sizeof actline)
  993.               :  fgets(actline, sizeof actline, actfp) )
  994. #else
  995.     while ( fgets(actline, sizeof actline, actfp) )
  996. #endif
  997.     {
  998.     stlist_t *stnew = (stlist_t *)strkeep(actline,sizeof(stlisthdr_t),POOL_ACT);
  999.     if (stnew == NULL) {
  1000.         nn_exitmsg(1, "out of mem for active file (at line %d)\n", count+1);
  1001.     }
  1002.     if (sthead != NULL) {
  1003.         stp->n.next = stnew;
  1004.         stp = stnew;
  1005.     }
  1006.     else {
  1007.         sthead = stnew;
  1008.         stp = sthead;
  1009.     }
  1010.     count++;
  1011.     }
  1012.     stp->n.next = NULL;
  1013.  
  1014.     if (!use_nntp)
  1015.     (void) fclose(actfp);
  1016.  
  1017.     actlist = (char **)calloc(count + 1, sizeof(char *));
  1018.     grplist = (char **)calloc(count + 1, sizeof(char *));
  1019.     if (grplist == NULL) {
  1020.     nn_exitmsg(1, "can't create active or group list (%d entries)\n",
  1021.            count+1);
  1022.     }
  1023.  
  1024.     /*
  1025.      * Second pass (in core):
  1026.      * Put active lines and group names into string arrays.
  1027.      */
  1028.     for (i = 0, stp = sthead; stp && i < count; stp = stp->n.next, i++) {
  1029.     char *p = strchr(stp->str, ' ');
  1030.  
  1031.     if (p == NULL)
  1032.         actlist[i] = NULL;
  1033.     else {
  1034.         *p++ = NUL;
  1035.         actlist[i] = p;
  1036.     }
  1037.     grplist[i] = strkeep(stp->str, 0, POOL_GRP);
  1038.     }
  1039.     actlist[count] = NULL;
  1040.     grplist[count] = NULL;
  1041.  
  1042.     /* init the master struct */
  1043.     clearobj(&master, sizeof(master), 1);
  1044.     master.number_of_groups = count;
  1045.  
  1046. }
  1047.  
  1048. #endif /* NOV */
  1049.  
  1050.  
  1051.  
  1052. #ifndef NOV
  1053.  
  1054. void
  1055. db_write_master()
  1056. {
  1057. #ifdef NETWORK_DATABASE
  1058.     net_long buf[MASTER_FIELDS];
  1059.  
  1060.     buf[0] = master.db_magic ^ NWDB_MAGIC;
  1061.     buf[1] = master.last_scan;
  1062.     buf[2] = master.last_size;
  1063.     buf[3] = master.number_of_groups;
  1064.     buf[4] = master.db_created;
  1065.  
  1066.     host_to_net(buf, MASTER_FIELDS);
  1067.     rewind(master_file);
  1068.     if (fwrite((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  1069.     != MASTER_FIELDS) goto err;
  1070.     if (fwrite(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  1071.     != DB_LOCK_MESSAGE) goto err;
  1072. #else
  1073.     rewind(master_file);
  1074.     if (fwrite((char *)&master, sizeof(master_header), 1, master_file) != 1)
  1075.     goto err;
  1076. #endif
  1077.  
  1078.     fflush(master_file);
  1079. #ifdef APOLLO_DOMAIN_OS
  1080.     if (who_am_i == I_AM_MASTER) make_master_copy(0);
  1081. #endif
  1082.     return;
  1083.  
  1084.  err:
  1085.     sys_error("Write to MASTER failed");
  1086. }
  1087.  
  1088.  
  1089. void
  1090. db_read_group(gh)
  1091. register group_header *gh;
  1092. {
  1093. #ifdef NETWORK_DATABASE
  1094.     net_long buf[GROUP_FIELDS];
  1095.  
  1096.     if (!db_sequential)
  1097.     fseek(master_file,
  1098.           (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  1099.           GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  1100.  
  1101.     if (fread((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  1102.     goto err;
  1103.  
  1104.     net_to_host(buf, GROUP_FIELDS);
  1105.  
  1106.     gh->first_db_article = buf[0];
  1107.     gh->last_db_article = buf[1];
  1108.     gh->index_write_offset = buf[2];
  1109.     gh->data_write_offset = buf[3];
  1110.     gh->group_name_length = buf[4];
  1111.     gh->master_flag = buf[5];
  1112.     gh->first_a_article = buf[6];
  1113.     gh->last_a_article = buf[7];
  1114.     gh->creation_time = buf[8];
  1115. #else
  1116.     if (!db_sequential)
  1117.     fseek(master_file, 
  1118.           (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  1119.  
  1120.     if (fread((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  1121.     goto err;
  1122. #endif
  1123.  
  1124.     return;
  1125.  
  1126.  err:
  1127.     sys_error("Read GROUPS failed");
  1128. }
  1129.  
  1130.  
  1131. void
  1132. db_write_group(gh)
  1133. register group_header *gh;
  1134. {
  1135. #ifdef NETWORK_DATABASE
  1136.     net_long buf[GROUP_FIELDS];
  1137.  
  1138.     if (!db_sequential)
  1139.     fseek(master_file,
  1140.           (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  1141.           GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  1142.  
  1143.     buf[0] = gh->first_db_article;
  1144.     buf[1] = gh->last_db_article;
  1145.     buf[2] = gh->index_write_offset;
  1146.     buf[3] = gh->data_write_offset;
  1147.     buf[4] = gh->group_name_length;
  1148.     buf[5] = gh->master_flag;
  1149.     buf[6] = gh->first_a_article;
  1150.     buf[7] = gh->last_a_article;
  1151.     buf[8] = gh->creation_time;
  1152.  
  1153.     host_to_net(buf, GROUP_FIELDS);
  1154.     if (fwrite((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  1155.     goto err;
  1156. #else
  1157.     if (!db_sequential)
  1158.     fseek(master_file, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  1159.  
  1160.  
  1161.     if (fwrite((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  1162.     goto err;
  1163. #endif
  1164.  
  1165.     fflush(master_file);
  1166.  
  1167. #ifdef APOLLO_DOMAIN_OS
  1168.     if (who_am_i == I_AM_MASTER) make_master_copy(0);
  1169. #endif
  1170.     return;
  1171.  
  1172.  err:
  1173.     sys_error("Write GROUPS failed");
  1174. }
  1175. #endif
  1176.  
  1177.  
  1178. #ifdef NOV
  1179. static void
  1180. readtimfile()
  1181. {
  1182.     char timline[512];
  1183.     FILE *timfp;
  1184.     int hsize;
  1185.  
  1186.     if (timtbl != NULL)
  1187.     return;
  1188.     hsize = master.number_of_groups | 0x1ff;
  1189.     timtbl = hashcreate(hsize, (unsigned (*)())NULL);
  1190.     if (timtbl == NULL) {
  1191.     nn_exitmsg(1, "can't create time hash (%d entries)\n", hsize);
  1192.     }
  1193.  
  1194. #ifdef NNTP
  1195.     if (use_nntp)
  1196.     timfp = nntp_fopen_list("LIST active.times");
  1197.     else
  1198. #endif
  1199.     timfp = fopen(relative(news_lib_directory, "active.times"), "r");
  1200.  
  1201.     if (timfp == NULL)
  1202.     return;        /* no great shakes if its missing */
  1203.  
  1204.     /* alt.fan.marla-thrift 736668095 netnews@ccc.amdahl.com */
  1205.  
  1206. #ifdef NNTP
  1207.     while ( use_nntp  ?  nntp_fgets(timline, sizeof timline)
  1208.               :  fgets(timline, sizeof timline, timfp) )
  1209. #else
  1210.     while ( fgets(timline, sizeof timline, timfp) )
  1211. #endif
  1212.     {
  1213.     char *line = strkeep(timline, 0, POOL_TIM);
  1214.     char *p = strchr(line, ' ');
  1215.  
  1216.     if (p == NULL)
  1217.         continue;
  1218.     *p++ = NUL;
  1219.     if (!hashstore(timtbl, line, p)) {
  1220.         nn_exitmsg(1, "nn: time hashstore failed\n");
  1221.     }
  1222.     }
  1223.  
  1224.     if (!use_nntp)
  1225.     (void) fclose(timfp);
  1226.  
  1227. }
  1228. #endif /* NOV */
  1229.  
  1230. #ifdef    CACHE_PURPOSE
  1231. static int
  1232. purpcmp(p0, p1)
  1233. const void *p0, *p1;
  1234. {
  1235.     return strcmp( ((struct purp_list *)p0)->group_name,
  1236.     ((struct purp_list *)p1)->group_name );
  1237. }
  1238.  
  1239. /*
  1240.  * Open purpose file and cache sorted list of group purposes
  1241.  */
  1242. static void
  1243. cache_purpose()
  1244. {
  1245.     char buf[512];
  1246.     register char *p;
  1247.     FILE *fp;
  1248.     int i, ngrp;
  1249.     stlist_t *sthead, *stp, *stnew;
  1250.     struct purp_list *plp;
  1251.  
  1252.     if ((fp = open_purpose_file()) == NULL
  1253.     || (ngrp = master.number_of_groups) == 0
  1254.     || purp_list != NULL)
  1255.     {
  1256.     return;
  1257.     }
  1258.  
  1259.     ngrp = 0;
  1260.     sthead = NULL;
  1261.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  1262.     if ((p = strchr(buf, NL)) != NULL) {
  1263.         *p = NUL;
  1264.     }
  1265.     stnew = (stlist_t *)strkeep(buf, sizeof(stlisthdr_t), POOL_PUR);
  1266.     if (stnew == NULL) {
  1267.         /* tough cookies.  we'll just do without. */
  1268.         pfree(POOL_PUR);
  1269.         return;
  1270.     }
  1271.     if (sthead != NULL) {
  1272.         stp->n.next = stnew;
  1273.         stp = stnew;
  1274.     }
  1275.     else {
  1276.         sthead = stnew;
  1277.         stp = sthead;
  1278.     }
  1279.     stnew->n.next = NULL;
  1280.     ngrp++;
  1281.     }
  1282.  
  1283.     purp_list = (struct purp_list *)calloc(ngrp + 1, sizeof(struct purp_list));
  1284.     if (purp_list == NULL) {
  1285.     pfree(POOL_PUR);
  1286.     return;
  1287.     }
  1288.  
  1289.     for (i = 0, plp = purp_list, stp = sthead; stp; stp = stp->n.next) {
  1290.     p = stp->str;
  1291.     while (!isspace(*p)) {    /* skip newsgroup name */
  1292.         if (*++p == NUL)
  1293.         goto next;
  1294.     }
  1295.     *p++ = NUL;
  1296.  
  1297.     while (isspace(*p)) {    /* skip to group description */
  1298.         if (*++p == NUL)
  1299.         goto next;
  1300.     }
  1301.     plp->group_name = stp->str;
  1302.     plp->purpose = p;
  1303.     plp++;
  1304.     i++;
  1305. next:
  1306.     continue;
  1307.     }
  1308.     plp->group_name = NULL;
  1309.     plp->purpose = NULL;
  1310.     purp_cnt = i;
  1311.  
  1312.     qsort(purp_list, purp_cnt, sizeof(struct purp_list), purpcmp);
  1313.  
  1314. }
  1315.  
  1316. char *
  1317. purp_lookup(group)
  1318. char *group;
  1319. {
  1320.     register i, j, k, t;
  1321.  
  1322.     i = 0;
  1323.     j = purp_cnt - 1;
  1324.  
  1325.     while (i <= j) {
  1326.     k = (i + j) / 2;
  1327.  
  1328.     if ( (t=strcmp(group, purp_list[k].group_name)) > 0)
  1329.         i = k+1;
  1330.     else
  1331.     if (t < 0)
  1332.         j = k-1;
  1333.     else
  1334.         return purp_list[k].purpose;
  1335.     }
  1336.  
  1337.     return "";
  1338. }
  1339. #endif    /* CACHE_PURPOSE */
  1340.  
  1341.  
  1342. #ifndef NOV
  1343. off_t db_read_art(f)
  1344. FILE *f;
  1345. {
  1346.     off_t bytes;
  1347.  
  1348. #ifdef NETWORK_DATABASE
  1349.     net_long buf[ARTICLE_FIELDS];
  1350.  
  1351.     if (fread((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  1352.     return 0;
  1353.     bytes = sizeof(net_long) * ARTICLE_FIELDS;
  1354.  
  1355.     net_to_host(buf, ARTICLE_FIELDS);
  1356.  
  1357.     db_hdr.dh_number = buf[0];
  1358.     db_hdr.dh_date = buf[1];
  1359.     db_hdr.dh_hpos = buf[2];
  1360.     db_hdr.dh_lpos = buf[3];
  1361.     db_hdr.dh_fpos = buf[4];
  1362.     db_hdr.dh_lines = buf[5];
  1363.     db_hdr.dh_replies = buf[6];
  1364.     db_hdr.dh_cross_postings = buf[7];
  1365.     db_hdr.dh_subject_length = buf[8];
  1366.     db_hdr.dh_sender_length = buf[9];
  1367. #else
  1368.     if (fread((char *)&db_hdr, sizeof(data_header), 1, f) != 1) return 0;
  1369.     bytes = sizeof(data_header);
  1370. #endif
  1371.  
  1372.     if (db_hdr.dh_number < 0) {
  1373.     current_digest_article = db_hdr.dh_number = -db_hdr.dh_number;
  1374.     db_data.dh_type = DH_DIGEST_HEADER;
  1375.     } else
  1376.     if (db_hdr.dh_number == 0) {
  1377.     db_hdr.dh_number = current_digest_article;
  1378.     db_data.dh_type = DH_SUB_DIGEST;
  1379.     } else {
  1380.     current_digest_article = 0;
  1381.     db_data.dh_type = DH_NORMAL;
  1382.     }
  1383.  
  1384.     if (db_hdr.dh_cross_postings) {
  1385.         if (fread((char *)db_data.dh_cross, sizeof(cross_post_number),
  1386.           (int)db_hdr.dh_cross_postings, f)
  1387.         != (int)db_hdr.dh_cross_postings) return -1;
  1388.     bytes += sizeof(cross_post_number) * (int)db_hdr.dh_cross_postings;
  1389.     }
  1390.  
  1391.     if (db_hdr.dh_sender_length) {
  1392.     if (fread(db_data.dh_sender, sizeof(char),
  1393.           (int)db_hdr.dh_sender_length, f)
  1394.         != db_hdr.dh_sender_length) return -1;
  1395.     bytes += sizeof(char) * (int)db_hdr.dh_sender_length;
  1396.     }
  1397.     db_data.dh_sender[db_hdr.dh_sender_length] = NUL;
  1398.  
  1399.     if (db_hdr.dh_subject_length) {
  1400.     if (fread(db_data.dh_subject, sizeof(char),
  1401.           (int)db_hdr.dh_subject_length, f)
  1402.         !=  db_hdr.dh_subject_length) return -1;
  1403.     bytes += sizeof(char) * (int)db_hdr.dh_subject_length;
  1404.     }
  1405.     db_data.dh_subject[db_hdr.dh_subject_length] = NUL;
  1406.  
  1407.     db_read_counter++;
  1408.  
  1409.     return bytes;
  1410. }
  1411. #endif /* NOV */
  1412.  
  1413.  
  1414. #ifdef NOV
  1415. /*
  1416.  * initialise *gh; this is much cheaper than calling db_read_group.
  1417.  */
  1418. static void
  1419. db_init_group(gh, num)
  1420. register group_header *gh;
  1421. int    num;
  1422. {
  1423.     register char    *p;
  1424.  
  1425.     /* tidy up the struct */
  1426.     clearobj(gh, sizeof(struct group_header), 1);
  1427.  
  1428.     gh->group_num = num;
  1429.     if (gh->group_name == NULL) {
  1430.         gh->group_name = grplist[num];
  1431.         if (gh->group_name == NULL) {  
  1432.         nn_exitmsg(1, "can't map group %d to name\n", num);
  1433.         }
  1434.         gh->group_name_length = strlen(gh->group_name);
  1435.     }
  1436.     gh->master_flag = M_VALID;
  1437.     /* control.newgrp, etc are control groups */
  1438.     if (strncmp(gh->group_name, "control", 7) == 0)
  1439.         gh->master_flag |= M_CONTROL;
  1440.     /* these next two are subtle and we need to lie below */
  1441.     /* gh->first_db_article = 0;*/        /* lowest # in ov. data */
  1442.     /* gh->last_db_article = 0; */        /* highest # in ov. data */
  1443.     gh->first_a_article = 1;         /* lowest # in active */
  1444.     /* gh->last_a_article = 0; */        /* highest number in active */
  1445.     /* gh->index_write_offset = 0; */    /* dunno */
  1446.     /* gh->data_write_offset = 0; */    /* dunno */
  1447.  
  1448.     /* set the creation time */
  1449.     gh->creation_time = 1;                  /* group creation date (~epoch) */
  1450.     p = hashfetch(timtbl, gh->group_name);
  1451.     if (p != NULL) {
  1452.         gh->creation_time = atol(p);
  1453.     }
  1454.  
  1455.     p = actlist[num];
  1456.     if (p != NULL) {
  1457.         gh->last_a_article = atol(++p);
  1458.         p = strchr(p, ' ');
  1459.         if (p == NULL)
  1460.         return;
  1461.         gh->first_a_article = atol(++p);
  1462.     }
  1463.     gh->first_db_article = gh->first_a_article; /* lowest # in ov. data */
  1464.     gh->last_db_article = gh->last_a_article; /* highest # in ov. data */
  1465. }
  1466.  
  1467.  
  1468. /*
  1469.  * slurp up the overview data for this group into *gh.
  1470.  * this costs a file open and so should not be done frivolously.
  1471.  */
  1472. void
  1473. db_read_group(gh, first, last)
  1474. register group_header *gh;
  1475. article_number first, last;
  1476. {
  1477.     register struct novart *artp, *lastartp;
  1478.  
  1479.     /* db_init_group(gh, group_num?? );  already done early at init time */
  1480.  
  1481.     if (ngovp != NULL)
  1482.         novclose(ngovp);        /* trash last group's data */
  1483.  
  1484. #ifdef NNTP
  1485.     if (use_nntp) {
  1486.         ngovp = nntp_get_overview(gh, first, last);
  1487.     } else
  1488. #endif
  1489.         ngovp = novopen(gh->group_name);
  1490.  
  1491.     if (ngovp == NULL) {
  1492.         printf("no overview data for group `%s'\n", gh->group_name);
  1493.         return;
  1494.     }
  1495.     allarts = novall(ngovp, first, last);
  1496.     if (allarts == NULL) {
  1497.         /*
  1498.            printf("overview data inaccessible for group `%s'\n",
  1499.                gh->group_name);
  1500.          */
  1501.         return;
  1502.     }
  1503.     if (!use_nntp || first == gh->first_a_article)
  1504.         gh->first_db_article = atol(allarts->a_num); /* lowest # */
  1505.  
  1506.     if (!use_nntp || last == gh->last_a_article) {
  1507.  
  1508.         lastartp = allarts;
  1509.         for (artp = allarts; artp != NULL; artp = artp->a_nxtnum)
  1510.             if (artp->a_num[0] != '0' || artp->a_num[1] != 0)
  1511.                 lastartp = artp;
  1512.  
  1513.         gh->last_db_article  = atol(lastartp->a_num); /* highest # */
  1514.     }
  1515.  
  1516.     /* Handle Digest flag */
  1517.     if (gh->first_db_article < 0)
  1518.         gh->first_db_article = -gh->first_db_article;
  1519.     if (gh->last_db_article < 0)
  1520.         gh->last_db_article = -gh->last_db_article;
  1521. }
  1522.  
  1523.  
  1524. /*
  1525.  * fill in db_hdr and db_data from the overview data for the next
  1526.  * article in this group.  does weirdo nn encodings of header fields.
  1527.  */
  1528. off_t
  1529. db_read_art(f)
  1530. FILE *f;
  1531. {
  1532.     register data_header *dhp = &db_hdr;
  1533.     register data_dynamic_data *ddp = &db_data;
  1534.     register struct novart *artp;
  1535.     int    recnt = 0;
  1536.  
  1537.     if (ngovp == NULL || ngovp->g_first == NULL)
  1538.         return 0;
  1539.     if (ngovp->g_first == NULL)        /* XXX */
  1540.         return 0;
  1541.     artp = novnext(ngovp);
  1542.     if (artp == NULL)
  1543.         return 0;            /* group exhausted */
  1544.  
  1545.     dhp->dh_number = atol(artp->a_num);
  1546.     /* printf("article #%ld\n", dhp->dh_number); */    /* DEBUG */
  1547.     dhp->dh_date = pack_date(artp->a_date);    /* "encoded Date: filed" */
  1548.     dhp->dh_hpos = 0;            /* 1st hdr byte */
  1549.     dhp->dh_lpos = 1L << 30;        /* last article byte */
  1550.     dhp->dh_fpos = 0;            /* 1st article text byte */
  1551.     dhp->dh_lines = -1;            /* -1 == "unknown" */
  1552.     if (isascii(artp->a_lines[0]) && isdigit(artp->a_lines[0]))
  1553.         dhp->dh_lines = atoi(artp->a_lines);
  1554.     dhp->dh_replies = 0;            /* # of References: */
  1555.     if (artp->a_refs != NULL) {
  1556.         register char    *p;
  1557.  
  1558.         for (p = artp->a_refs; *p != '\0'; p++)
  1559.             if (*p == '<')
  1560.                 dhp->dh_replies++;
  1561.     }
  1562.  
  1563.     db_fixup_cross_postings(dhp, ddp, artp);
  1564.  
  1565.     if (dhp->dh_number < 0) {
  1566.         current_digest_article = dhp->dh_number = -dhp->dh_number;
  1567.         ddp->dh_type = DH_DIGEST_HEADER;
  1568.     } else if (dhp->dh_number == 0) {
  1569. #ifdef DO_NOV_DIGEST
  1570.         char *cp;
  1571.         if (artp->a_bytes && (cp = strchr(artp->a_bytes, ':'))) {
  1572.             dhp->dh_hpos = atol(++cp);
  1573.             if ((cp = strchr(cp, ':')) != NULL) {
  1574.                 dhp->dh_fpos = atol(++cp);
  1575.                 if ((cp = strchr(cp, ':')) != NULL) {
  1576.                     dhp->dh_lpos = atol(++cp);
  1577.                 }
  1578.             }
  1579.         }
  1580. #endif
  1581.         dhp->dh_number = current_digest_article;
  1582.         ddp->dh_type = DH_SUB_DIGEST;
  1583.     } else {
  1584.         current_digest_article = 0;
  1585.         ddp->dh_type = DH_NORMAL;
  1586.     }
  1587.  
  1588.     dhp->dh_sender_length =  pack_name(ddp->dh_sender, artp->a_from, NAME_LENGTH);
  1589.     dhp->dh_subject_length = pack_subject(ddp->dh_subject, artp->a_subj, &recnt, DBUF_SIZE);
  1590.  
  1591.     if (recnt)        /* 5/3/93 wolfgang@wsrcc.com */
  1592.         dhp->dh_replies |= 0x80;
  1593.  
  1594.     db_read_counter++;
  1595.     return 1;
  1596. }
  1597.  
  1598. static void
  1599. db_fixup_cross_postings(dhp, ddp, artp)
  1600. data_header *dhp;
  1601. data_dynamic_data *ddp;
  1602. struct novart *artp;
  1603. {
  1604.     char *curg, *tmp;
  1605.     int numgrps = 0;
  1606.  
  1607.     dhp->dh_cross_postings = 0; /* assume none as default until we can show 
  1608.                    otherwise */
  1609.  
  1610.     /* If no "other" header lines are in NOV database, we're out of luck,
  1611.        can only assume no crosspostings, so return. */
  1612.     if ((artp->a_others) == NULL) return;
  1613.  
  1614.     /* Scan until we find a Xref: header line. */
  1615.     for (curg = artp->a_others; ; ++curg) {
  1616.     if (strncmp("Xref: ", curg, 6) == 0 ||
  1617.         strncmp("xref: ", curg, 6) == 0)
  1618.     {
  1619.         break;
  1620.     }
  1621.     curg = strchr(curg, '\t'); /* Not this header, skip to the next */
  1622.     if (curg == NULL) return;
  1623.     }
  1624.  
  1625.     curg += 6;               /* Skip over "Xref: " */
  1626.  
  1627.     while (*curg == ' ') ++curg;   /* Skip to the hostname field after Xref: */
  1628.  
  1629.     /* Skip over the hostname to the space following hostname */
  1630.     if ( (curg = strchr(curg, ' ')) == NULL ) {
  1631.     return;               /* header is malformed, punt. */
  1632.     }
  1633.     /*
  1634.      * Start reading the entries one at a time.  Each entry is of the
  1635.      * form "newsgroup:number", and entries are separated by spaces.
  1636.      * Algorithm loosely based on the orignal one in collect.c for
  1637.      * setting up the crosspost information.
  1638.      */
  1639.     while (*curg == ' ' && numgrps < DBUF_SIZE) {
  1640.     group_header *gh;
  1641.  
  1642.     while (*curg == ' ') ++curg;    /* Skip spaces to the next entry */
  1643.  
  1644.     /* Zap colon at end of current entry. */
  1645.     for (tmp = curg ; ; ++tmp) {
  1646.         if (*tmp == ':' || *tmp == '\0' || *tmp == '\t')
  1647.         break;
  1648.     }
  1649.     if (*tmp != ':') break;        /* malformed entry, punt. */
  1650.     *tmp = '\0';
  1651.  
  1652.     /* Find gh struct for the group. */
  1653.     if ( (gh = lookup(curg)) != NULL) {
  1654.         /* and add group number to the crosspost list. */
  1655.         ddp->dh_cross[numgrps++] = gh->group_num;
  1656.     }
  1657.     curg = tmp + 1;
  1658.     while (isdigit(*curg)) ++curg; /* Skip over the article number */
  1659.     }
  1660.     if (numgrps > 1) {
  1661.     /* Note: if # of groups is only 1, we leave dh_cross_postings 
  1662.        at its original value of zero. */
  1663.     dhp->dh_cross_postings = numgrps;
  1664.     }
  1665.     return;
  1666. }
  1667. #endif /* NOV */
  1668.  
  1669.  
  1670. #ifndef NOV
  1671. int
  1672. db_write_art(f)
  1673. FILE *f;
  1674. {
  1675. #ifdef NETWORK_DATABASE
  1676.     net_long buf[ARTICLE_FIELDS];
  1677. #endif
  1678.     article_number art_num = db_hdr.dh_number;
  1679.  
  1680.     switch (db_data.dh_type) {
  1681.      case DH_NORMAL:
  1682.     break;
  1683.      case DH_SUB_DIGEST:
  1684.     db_hdr.dh_number = 0;
  1685.     break;
  1686.      case DH_DIGEST_HEADER:
  1687.     db_hdr.dh_number = -art_num;
  1688.     break;
  1689.     }
  1690.  
  1691. #ifdef NETWORK_DATABASE
  1692.     buf[0] = db_hdr.dh_number;
  1693.     buf[1] = db_hdr.dh_date;
  1694.     buf[2] = db_hdr.dh_hpos;
  1695.     buf[3] = db_hdr.dh_lpos;
  1696.     buf[4] = db_hdr.dh_fpos;
  1697.     buf[5] = db_hdr.dh_lines;
  1698.     buf[6] = db_hdr.dh_replies;
  1699.     buf[7] = db_hdr.dh_cross_postings;
  1700.     buf[8] = db_hdr.dh_subject_length;
  1701.     buf[9] = db_hdr.dh_sender_length;
  1702.  
  1703.     host_to_net(buf, ARTICLE_FIELDS);
  1704.  
  1705.     if (fwrite((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  1706.     return -1;
  1707. #else
  1708.  
  1709.     if (fwrite((char *)&db_hdr, sizeof(data_header), 1, f) != 1)
  1710.     return -1;
  1711.  
  1712. #endif
  1713.     if (db_hdr.dh_cross_postings)
  1714.         if (fwrite((char *)db_data.dh_cross, sizeof(cross_post_number),
  1715.           (int)db_hdr.dh_cross_postings, f)
  1716.         != (int)db_hdr.dh_cross_postings) return -1;
  1717.  
  1718.     if (db_hdr.dh_sender_length)
  1719.     if (fwrite(db_data.dh_sender, sizeof(char),
  1720.           (int)db_hdr.dh_sender_length, f)
  1721.         != db_hdr.dh_sender_length) return -1;
  1722.  
  1723.     if (db_hdr.dh_subject_length)
  1724.     if (fwrite(db_data.dh_subject, sizeof(char),
  1725.           (int)db_hdr.dh_subject_length, f)
  1726.         != db_hdr.dh_subject_length) return -1;
  1727.  
  1728.     db_hdr.dh_number = art_num;
  1729.  
  1730.     db_write_counter++;
  1731.  
  1732.     return 1;
  1733. }
  1734.  
  1735.  
  1736. off_t get_index_offset(gh, art_num)
  1737. group_header *gh;
  1738. article_number art_num;
  1739. {
  1740. #ifdef NETWORK_DATABASE
  1741.     return (off_t)((art_num - gh->first_db_article) * sizeof(net_long));
  1742. #else
  1743.     return (off_t)((art_num - gh->first_db_article) * sizeof(off_t));
  1744. #endif
  1745. }
  1746.  
  1747. off_t get_data_offset(gh, art_num)
  1748. group_header *gh;
  1749. article_number art_num;
  1750. {
  1751.     FILE *index;
  1752.     off_t data_offset;
  1753.  
  1754.     if (gh->first_db_article == art_num) return (off_t)0;
  1755.  
  1756.     index = open_data_file(gh, 'x', OPEN_READ);
  1757.     if (index == NULL) return (off_t)(-1);
  1758.  
  1759.     fseek(index, get_index_offset(gh, art_num), 0);
  1760.     if (!db_read_offset(index, &data_offset))
  1761.     data_offset = (off_t)(-1);
  1762.  
  1763.     fclose(index);
  1764.  
  1765.     return data_offset;
  1766. }
  1767.  
  1768.  
  1769. int
  1770. db_read_offset(f, offset)
  1771. FILE *f;
  1772. off_t *offset;
  1773. {
  1774. #ifdef NETWORK_DATABASE
  1775.     net_long temp;
  1776.  
  1777.     if (fread((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  1778.  
  1779. #ifndef NETWORK_BYTE_ORDER
  1780.     temp = ntohl(temp);
  1781. #endif
  1782.     *offset = temp;
  1783. #else
  1784.  
  1785.     if (fread((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  1786. #endif
  1787.     return 1;
  1788. }
  1789.  
  1790. int
  1791. db_write_offset(f, offset)
  1792. FILE *f;
  1793. off_t *offset;
  1794. {
  1795. #ifdef NETWORK_DATABASE
  1796.     net_long temp;
  1797.  
  1798.     temp = *offset;
  1799.  
  1800. #ifndef NETWORK_BYTE_ORDER
  1801.     temp = htonl(temp);
  1802. #endif
  1803.     if (fwrite((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  1804.  
  1805. #else
  1806.  
  1807.     if (fwrite((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  1808. #endif
  1809.     return 1;
  1810. }
  1811.  
  1812. #endif /* NOV */
  1813.  
  1814.  
  1815. #ifdef NOV
  1816.  
  1817. /* These are strictly temporary.  They will go away. */
  1818. char *
  1819. db_data_path(namebuf, gh, d_or_x)
  1820. char *namebuf;
  1821. group_header *gh;
  1822. int d_or_x;
  1823. {
  1824.   nn_exitmsg(50, "STUB ROUTINE CALLED: db_data_path\n");
  1825.   return NULL;
  1826. }
  1827.  
  1828. int
  1829. db_read_offset(f, offset)
  1830. FILE *f;
  1831. off_t *offset;
  1832. {
  1833.   nn_exitmsg(50, "STUB ROUTINE CALLED: db_read_offset\n");
  1834.   return -1;
  1835. }
  1836.  
  1837. void
  1838. db_write_group(gh)
  1839. group_header *gh;
  1840. {
  1841.   nn_exitmsg(50, "STUB ROUTINE CALLED: db_write_group\n");
  1842.   return;
  1843. }
  1844.  
  1845. FILE *
  1846. open_data_file(gh, d_or_x, mode)
  1847. group_header *gh;
  1848. int d_or_x;
  1849. int mode;
  1850. {
  1851.   nn_exitmsg(50, "STUB ROUTINE CALLED: open_data_dile\n");
  1852.   return NULL;
  1853. }
  1854.  
  1855. off_t
  1856. get_index_offset(gh, art_num)
  1857. group_header *gh;
  1858. article_number art_num;
  1859. {
  1860.   nn_exitmsg(50, "STUB ROUTINE CALLED: get_index_offset\n");
  1861.   return -1;
  1862. }
  1863. #endif /* NOV */
  1864.  
  1865. #if defined(NOV) || defined(CACHE_PURPOSE)
  1866. /*
  1867.  * pmalloc()/pfree():
  1868.  *    A scheme to avoid malloc()/free() overhead; handles memory in
  1869.  *    STRCHUNK increments (deliberately same as STR_THUNK_SIZE).
  1870.  *    Unlike mark_str()/alloc_str()/release_str(), pfree()'d memory
  1871.  *    returns to the malloc() pool, which is arguably more social.
  1872.  *    More important, the alloc_str() family assumes only one active
  1873.  *    use at a time; interleaving uses or a misplaced release_str() has
  1874.  *    the potential to leak memory or corrupt the current_str_t pool.
  1875.  *
  1876.  *    Perhaps should be globally available; move to global.c?
  1877.  */
  1878. #define    STRCHUNK    ((1<<14) - 32)    /* leave room for malloc header */
  1879.  
  1880. typedef struct stpool {
  1881.     stlist_t *sthead;
  1882.     char *pool;
  1883.     int pfree;
  1884.     int pslop;
  1885. } stpool_t;
  1886.  
  1887. static stpool_t stpool[POOL_MAX+1];
  1888.  
  1889. static char *
  1890. pmalloc(size, poolid)
  1891.     int size, poolid;
  1892. {
  1893.     register stpool_t *pp;
  1894.     register stlist_t *stnew;
  1895.     register char *ret;
  1896.  
  1897.     if (poolid < 0 || poolid > POOL_MAX)
  1898.     return NULL;
  1899.  
  1900.     pp = &stpool[poolid];
  1901.     if (size <= pp->pfree) {
  1902.     /* Usually short; fits into current chunk */
  1903.     ret = pp->pool;
  1904.     }
  1905.     else if (size <= STRCHUNK) {
  1906.     /* Sometimes chunk is exhausted; chain new one to pool */
  1907.     stnew = (stlist_t *)malloc(sizeof(stlisthdr_t) + STRCHUNK);
  1908.     if (stnew == NULL)
  1909.         return NULL;
  1910.     pp->pslop += pp->pfree;
  1911.     stnew->n.next = pp->sthead;
  1912.     pp->sthead = stnew;
  1913.     pp->pfree = STRCHUNK;
  1914.     ret = stnew->str;
  1915.     }
  1916.     else {
  1917.     /*
  1918.      * Last resort: allocate oversize chunk and chain to pool
  1919.      * behind current chunk.
  1920.      */
  1921.     stnew = (stlist_t *)malloc(sizeof(stlisthdr_t) + size);
  1922.     if (stnew == NULL)
  1923.         return NULL;
  1924.     if (pp->sthead != NULL) {
  1925.         stnew->n.next = pp->sthead->n.next;
  1926.         pp->sthead->n.next = stnew;
  1927.     }
  1928.     else {
  1929.         stnew->n.next = NULL;
  1930.         pp->sthead = stnew;
  1931.     }
  1932.     return stnew->str;
  1933.     /*NOTREACHED*/
  1934.     }
  1935.  
  1936.     pp->pool = ret + size;
  1937.     pp->pfree -= size;
  1938.     return ret;
  1939. }
  1940.  
  1941. static void
  1942. pfree(poolid)
  1943.     int poolid;
  1944. {
  1945.     register stpool_t *pp;
  1946.     register stlist_t *stp, *stnext;
  1947.  
  1948.     if (poolid < 0 || poolid > POOL_MAX)
  1949.     return;
  1950.  
  1951.     pp = &stpool[poolid];
  1952.     for (stp = pp->sthead; stp; stp = stnext) {
  1953.     stnext = stp->n.next;
  1954.     free(stp);
  1955.     }
  1956.     pp->sthead = NULL;
  1957.     pp->pool = NULL;
  1958.     pp->pfree = 0;
  1959.     pp->pslop = 0;
  1960. }
  1961.  
  1962.  
  1963. /*
  1964.  * strkeep()
  1965.  *    Save a string, allowing space for a header.
  1966.  */
  1967. static char *
  1968. strkeep(s, hdr, poolid)
  1969. char *s;
  1970. int hdr;
  1971. int poolid;
  1972. {
  1973.     register int size;
  1974.     register char *ret;
  1975.     register stpool_t *pp;
  1976.     register stlist_t *stnew;
  1977.  
  1978.     if (poolid == POOL_TMP) {
  1979.     size = hdr + strlen(s);
  1980.     ret = alloc_str(size);
  1981.     }
  1982.     else {
  1983.     size = (hdr + strlen(s) + sizeof(long)) & ~(sizeof(long) - 1);
  1984.     ret = pmalloc(size, poolid);
  1985.     }
  1986.  
  1987.     if (ret)
  1988. #ifdef NO_MEMMOVE
  1989.     bcopy(s, ret+hdr, size-hdr);
  1990. #else
  1991.     memmove(ret+hdr, s, size-hdr);
  1992. #endif /* NO_MEMMOVE */
  1993.  
  1994.     return ret;
  1995. }
  1996. #endif    /* NOV || CACHE_PURPOSE */
  1997.