home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part13 / db.c next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  20.3 KB  |  955 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. import char
  11.     *master_directory, *db_directory, *db_data_directory, *news_directory;
  12.  
  13. export master_header master;
  14. export group_header *active_groups = NULL;
  15. export group_header **sorted_groups = NULL;
  16.  
  17. export int  reread_groups_file = 0;  /* nnmaster -G */
  18.  
  19. export data_header db_hdr;
  20. export data_dynamic_data db_data;
  21.  
  22. export int32 db_read_counter = 0;     /* articles read by db_read_art */
  23. export int32 db_write_counter = 0;    /* articles written by db_write_art */
  24.  
  25. /*
  26.  * Init access to a group
  27.  */
  28.  
  29. export group_header *current_group = NULL;
  30.  
  31. export char group_path_name[FILENAME];
  32. export char *group_file_name = NULL;
  33.  
  34. static char *group_position = NULL;
  35. static article_number current_digest_article = 0;
  36.  
  37. init_group(gh)
  38. register group_header *gh;
  39. {
  40.     register char *p, *q;
  41.  
  42.     current_digest_article = 0;
  43.  
  44.     if (gh == NULL) return 0;
  45.     if (gh->master_flag & M_IGNORE_GROUP) return 0;
  46.     if (gh == current_group) return 1;
  47.  
  48.     current_group = gh;
  49.  
  50.     if (gh->group_flag & G_FOLDER) {
  51.     group_position = NULL;
  52.     group_file_name = NULL;
  53.     strcpy(group_path_name, gh->group_name);
  54.     return 1;
  55.     }
  56.  
  57. #ifdef NNTP
  58.     if (use_nntp && nntp_set_group(gh) < 0)
  59.     return 0;
  60. #endif /* NNTP */
  61.  
  62.     if (group_position == NULL)
  63.     if (who_am_i == I_AM_MASTER || who_am_i == I_AM_EXPIRE)
  64.         group_position = group_path_name;
  65.     else {
  66.         strcpy(group_path_name, news_directory);
  67.         group_position = group_path_name + strlen(group_path_name);
  68.         *group_position++ = '/';
  69.     }
  70.  
  71.     for (p = group_position, q = gh->group_name; *q; q++)
  72.     *p++ = (*q == '.') ? '/' : *q;
  73.  
  74.     if (who_am_i == I_AM_MASTER) {
  75.  
  76.     /* The master will chdir to the group's directory to get */
  77.     /* better performance (can use relative path names). */
  78.  
  79.     *p++ = NUL;
  80.  
  81.     if (!use_nntp) {
  82.         if (chdir(news_directory) < 0)
  83.         sys_error(news_directory);
  84.  
  85.         if (chdir(group_path_name) < 0)
  86.         return 0;
  87.     }
  88.     group_file_name = group_path_name;
  89.     return 1;
  90.     }
  91.  
  92.     /* client */
  93.     if (gh->master_flag & M_NO_DIRECTORY) return 0;
  94.  
  95.     *p++ = '/';
  96.     *p = NUL;
  97.     group_file_name = p;
  98.     return 1;
  99. }
  100.  
  101. /*
  102.  *    Open master & group file; read it in if first open.
  103.  *
  104.  *    GROUPS file format is:
  105.  *
  106.  *    One line per group:
  107.  *    <group name> [<space><timestamp>] [<space><options>] <NL>
  108.  *    If <timestamp> is omitted, a timestamp of 0 is used (very old group).
  109.  *    If <group name> is "@", the master entry is ignored.
  110.  *
  111.  *    <options>:
  112.  *    D    Digest all articles in the group
  113.  *    N    Never digest articles
  114.  *    @    Bogus group, ignore completely
  115.  *    !    Don't collect this group
  116.  *
  117.  *    Do not edit the GROUPS file while nnmaster is running.
  118.  *    After editing the groups file (options), run nnmaster -G.
  119.  */
  120.  
  121. static FILE *group_file = NULL;
  122.  
  123. open_groups(mode)
  124. int mode;
  125. {
  126.     group_file = open_file(relative(db_directory, "GROUPS"), mode);
  127.  
  128.     if (group_file != NULL && (mode & OPEN_CREATE)) {
  129.     fprintf(group_file, 
  130. "#\n#\tRUN 'nnmaster -G' AFTER MODIFYING THIS FILE\n");
  131.     fprintf(group_file, 
  132. "#\n#\tDO NOT REMOVE OR REORDER ANY LINES IN THIS FILE\n#\n");
  133.     }
  134.     
  135.     return group_file != NULL;
  136. }
  137.  
  138. close_groups()
  139. {
  140.     if (group_file != NULL) {
  141.     fclose(group_file);
  142.     group_file = NULL;
  143.     }
  144. }
  145.  
  146. db_append_group(gh)
  147. register group_header *gh;
  148. {
  149.     char flags[16], *fp;
  150.     int was_open = (group_file != NULL);
  151.  
  152.     if (!was_open) open_groups(OPEN_UPDATE|MUST_EXIST);
  153.  
  154.     fseek(group_file, (off_t)0, 2);
  155.     if (gh->group_name[0] == NUL) {
  156.     fputc('@', group_file);
  157.     goto out;
  158.     }
  159.  
  160.     fprintf(group_file, "%s", gh->group_name);
  161.     if (gh->creation_time > 0)
  162.     fprintf(group_file, " %ld", (long)(gh->creation_time));
  163.  
  164.     fp = flags;
  165.  
  166.     if (gh->master_flag & M_IGNORE_G)
  167.     *fp++ = '!';
  168.     if (gh->master_flag & M_ALWAYS_DIGEST)
  169.     *fp++ = 'D';
  170.     if (gh->master_flag & M_NEVER_DIGEST)
  171.     *fp++ = 'N';
  172.     if (gh->master_flag & M_INCLUDE_OLD)
  173.     *fp++ = 'O';
  174.     if (gh->master_flag & M_AUTO_RECOLLECT)
  175.     *fp++ = 'R';
  176.     if (gh->archive_file != NULL)
  177.     *fp++ = '>';
  178.  
  179.     if (fp != flags) {
  180.     *fp++ = NUL;
  181.     fprintf(group_file, " %s%s", flags,
  182.         gh->archive_file != NULL ? gh->archive_file : "");
  183.     }
  184.  
  185.  out:
  186.     fputc(NL, group_file);
  187.     fflush(group_file);
  188.  
  189.     if (!was_open) close_groups();
  190. }
  191.  
  192. static char *mk_archive_file(gh, cp, logerr)
  193. register group_header *gh;
  194. register char *cp;
  195. int logerr;
  196. {
  197.     char *name;
  198.  
  199.     while (*cp && (*cp == '>' || isspace(*cp))) cp++;
  200.  
  201.     name = cp;
  202.     while (*cp && !isspace(*cp)) cp++;
  203.     if (*cp) *cp++ = NUL;
  204.     if (*name) {
  205.     gh->archive_file = copy_str(name);
  206.  
  207.     if (*name == '/')
  208.         gh->master_flag |= M_AUTO_ARCHIVE;
  209.     else {
  210.         gh->master_flag &= ~M_AUTO_ARCHIVE;
  211.         if (who_am_i == I_AM_MASTER)
  212.         if (logerr)
  213.             log_entry('E', "GROUPS %s >%s: Full path required",
  214.                   gh->group_name, name);
  215.         else
  216.             printf("Error in GROUPS: %s >%s: Full path required\n",
  217.                   gh->group_name, name);
  218.     }
  219.     }
  220.  
  221.     return cp;
  222. }
  223.  
  224. db_parse_group(gh, trust_master)
  225. register group_header *gh;
  226. int trust_master;        /* trust what is in the master file */
  227. {
  228.     char line[256];
  229.     register char *cp, *name;
  230.     int ignore;
  231.  
  232.     do {
  233.     if (fgets(line, 256, group_file) == NULL) return 0;
  234.     for (cp = line; *cp && isspace(*cp); cp++);
  235.     } while (*cp == NUL || *cp == '#');
  236.  
  237.     gh->archive_file = NULL;
  238.  
  239.     name = cp;
  240.  
  241.     if (trust_master) {
  242.     cp = name + gh->group_name_length;
  243.     } else {
  244.     /* parse GROUPS line */
  245.  
  246.     if (*cp == '@') {
  247.         ignore = 1;
  248.         gh->group_name_length = 0;
  249.         gh->group_name = "";
  250.         goto ignore_group;
  251.     }
  252.  
  253.     if (gh->group_name_length == 0) {
  254.         while (*cp && !isspace(*cp)) cp++;
  255.         gh->group_name_length = cp - name;
  256.     } else {
  257.         cp = name + gh->group_name_length;
  258.         if (*cp == NUL || !isspace(*cp)) {
  259.         sys_error("MASTER/GROUPS conflict: %d/%s", gh->group_num, line);
  260.         }
  261.     }
  262.     }
  263.  
  264.     if (*cp) *cp++ = NUL;
  265.     if (gh->group_name_length > 0)
  266.     gh->group_name = copy_str(name);
  267.     else
  268.     gh->group_name = "";
  269.  
  270.     if (trust_master) {
  271.     if (gh->master_flag & M_AUTO_ARCHIVE) {
  272.         while (*cp && *cp != '>') cp++;
  273.         if (*cp == '>') cp = mk_archive_file(gh, cp, 1);
  274.     }
  275.     return 1;
  276.     }
  277.  
  278.     while (*cp && isspace(*cp)) cp++;
  279.  
  280.     if (*cp && isdigit(*cp)) {
  281.     gh->creation_time = atol(cp);
  282.     while (*cp && isdigit(*cp)) cp++;
  283.     } else
  284.     gh->creation_time = 0;
  285.  
  286.     while (*cp && isspace(*cp)) cp++;
  287.  
  288.     ignore = 0;
  289.     gh->master_flag &= ~(M_ALWAYS_DIGEST | M_NEVER_DIGEST |
  290.             M_AUTO_RECOLLECT | M_AUTO_ARCHIVE);
  291.  
  292.     while (*cp) {
  293.     switch (*cp++) {
  294.      case ' ':
  295.      case '\t':
  296.      case NL:
  297.      case CR:
  298.         continue;
  299.  
  300.      case 'D':    /* Collect this group, digest all articles */
  301.         gh->master_flag |= M_ALWAYS_DIGEST;
  302.         continue;
  303.  
  304.      case 'N':    /* Collect this group, never digest articles */
  305.         gh->master_flag |= M_NEVER_DIGEST;
  306.         continue;
  307.  
  308.      case 'O':    /* Ignore -O option for this group */
  309.         gh->master_flag |= M_INCLUDE_OLD;
  310.         continue;
  311.  
  312.      case 'R':    /* Recollect this group when new articles arrive */
  313.         gh->master_flag |= M_AUTO_RECOLLECT;
  314.         continue;
  315.  
  316.      case '>':    /* Archive all new articles in gh->archive_file */
  317.         cp = mk_archive_file(gh, cp, 0);
  318.         continue;
  319.  
  320.      case '@':    /* Bogus GROUP -- ignore completely */
  321.         ignore = 1;
  322.         gh->group_name_length = 0;
  323.         break;
  324.  
  325.      case '!':    /* Do not collect this group */
  326.      case 'X':
  327.         ignore = 1;
  328.         break;
  329.  
  330.      case '#':    /* comment */
  331.         *cp = NUL;
  332.         break;
  333.  
  334.      default:
  335.         printf("Bad GROUPS flag for %s: `%c'\n", gh->group_name, *--cp);
  336.         break;
  337.     }
  338.     break;
  339.     }
  340.  
  341.  ignore_group:
  342.  
  343.     /* G_DONE indicates to master that the group must be cleaned */
  344.  
  345.     if (ignore) {
  346.     if ((gh->master_flag & M_IGNORE_GROUP) == 0) {
  347.         gh->master_flag |= M_MUST_CLEAN;
  348.         log_entry('X', "Group %s ignored", gh->group_name);
  349.     }
  350.     gh->master_flag |= M_IGNORE_G;
  351.     } else {    /* was group ignored in GROUPS, but not active before? */
  352.     if ((gh->master_flag & M_IGNORE_GROUP) == M_IGNORE_G) {
  353.         gh->master_flag &= ~M_NO_DIRECTORY;
  354.         log_entry('X', "Group %s activated", gh->group_name);
  355.     }
  356.     gh->master_flag &= ~M_IGNORE_G;
  357.     }
  358.     
  359.     return 1;
  360. }
  361.  
  362. /*
  363.  *    Open master & group files; read then in if first open.
  364.  */
  365.  
  366. static FILE *master_file = NULL;
  367. static int db_sequential = 0;
  368.  
  369. open_master(mode)
  370. int mode;
  371. {
  372.     register group_header *gh;
  373.     int trust_master;
  374.  
  375.     close_master();
  376.  
  377.     master_file = open_file(relative(db_directory, "MASTER"), mode|MUST_EXIST);
  378.  
  379.     db_sequential = 0;
  380.     if (mode == OPEN_CREATE) db_sequential = 1;
  381.  
  382.     if (mode != OPEN_READ) return;
  383.  
  384.     db_read_master();
  385.  
  386.     if (who_am_i != I_AM_MASTER && master.db_lock[0]) {
  387.     printf("DATABASE LOCKED.\n%s\n", master.db_lock);
  388.     nn_exit(88);
  389.     }
  390.  
  391.     freeobj(sorted_groups);
  392.     freeobj(active_groups);
  393.     active_groups = NULL;
  394.     sorted_groups = NULL;
  395.  
  396.     db_expand_master();
  397.  
  398.     open_groups(OPEN_READ|MUST_EXIST);
  399.  
  400.     trust_master = (who_am_i != I_AM_MASTER || !reread_groups_file);
  401.  
  402.     db_sequential = 1;
  403.     Loop_Groups_Header(gh) {
  404.     gh->group_num = l_g_index;
  405.     db_read_group(gh);
  406.     db_parse_group(gh, trust_master);
  407.     }
  408.     db_sequential = 0;
  409.  
  410.     close_groups();
  411.  
  412.     sort_groups();
  413. }
  414.  
  415. db_expand_master()
  416. {
  417.     master.free_groups = 20;
  418.  
  419.     active_groups = resizeobj(active_groups, group_header,
  420.                   master.number_of_groups + master.free_groups);
  421.     sorted_groups = resizeobj(sorted_groups, group_header *,
  422.                   master.number_of_groups + master.free_groups);
  423. }
  424.  
  425. close_master()
  426. {
  427.     if (master_file != NULL) {
  428.     fclose(master_file);
  429.     master_file = NULL;
  430.     }
  431. }
  432.  
  433. update_group(gh)
  434. group_header *gh;
  435. {
  436.     group_number numg;
  437.  
  438.     numg = master.number_of_groups;
  439.     db_read_master();
  440.     master.number_of_groups = numg;
  441.     if (master.db_lock[0]) return -3;
  442.  
  443.     db_read_group(gh);
  444.  
  445.     if (gh->master_flag & M_IGNORE_GROUP) return 0;
  446.     if (gh->master_flag & M_BLOCKED) return -1;
  447.  
  448.     return 1;
  449. }
  450.  
  451.  
  452. static sort_gh(g1, g2)
  453. group_header **g1, **g2;
  454. {
  455.     return strcmp((*g1)->group_name, (*g2)->group_name);
  456. }
  457.  
  458.  
  459. sort_groups()
  460. {
  461.     register group_header *gh;
  462.  
  463.     Loop_Groups_Header(gh)
  464.     sorted_groups[l_g_index] = gh;
  465.  
  466.     quicksort(sorted_groups, master.number_of_groups, group_header *, sort_gh);
  467.  
  468.     s_g_first = 0;
  469.     Loop_Groups_Sorted(gh)
  470.     if (gh->group_name[0] != NUL) {
  471.         s_g_first = l_g_index;
  472.         break;
  473.     }
  474. }
  475.  
  476.  
  477. group_header *lookup_no_alias(name)
  478. char *name;
  479. {
  480.     register i, j, k, t;
  481.  
  482.     i = s_g_first; j = master.number_of_groups - 1;
  483.  
  484.     while (i <= j) {
  485.     k = (i + j) / 2;
  486.  
  487.     if ( (t=strcmp(name, sorted_groups[k]->group_name)) > 0)
  488.         i = k+1;
  489.     else
  490.     if (t < 0)
  491.         j = k-1;
  492.     else
  493.         return sorted_groups[k];
  494.     }
  495.  
  496.     return NULL;
  497. }
  498.  
  499. group_header *lookup(name)
  500. char *name;
  501. {
  502.     register group_header *gh;
  503.  
  504.     gh = lookup_no_alias(name);
  505.     if (gh == NULL || (gh->master_flag & M_ALIASED) == 0) return gh;
  506.  
  507.     do
  508.     gh = &active_groups[gh->data_write_offset];
  509.     while (gh->master_flag & M_ALIASED);
  510.  
  511.     return gh;
  512. }
  513.  
  514. art_collected(gh, art_num)
  515. group_header *gh;
  516. article_number art_num;
  517. {
  518.     return gh->first_db_article <= art_num && gh->last_db_article >= art_num;
  519. }
  520.  
  521. char *db_data_path(namebuf, gh, d_or_x)
  522. char *namebuf;
  523. group_header *gh;
  524. char d_or_x;
  525. {
  526.     register char *cp, *np;
  527.  
  528.     if (db_data_directory != NULL) {
  529. #ifdef DB_LONG_NAMES
  530.     sprintf(namebuf, "%s/%s.%c", db_data_directory, gh->group_name, d_or_x);
  531. #else
  532.     sprintf(namebuf, "%s/%d.%c", db_data_directory, gh->group_num, d_or_x);
  533. #endif
  534.     } else {
  535.     np = namebuf;
  536.     /* master chdir to the group's directory */
  537.     if (who_am_i != I_AM_MASTER) {
  538.         for (cp = news_directory; *np = *cp++; np++);
  539.         *np++ = '/';
  540.         for (cp = gh->group_name; *cp; cp++)
  541.         *np++ = *cp == '.' ? '/' : *cp;
  542.         *np++ = '/';
  543.     }
  544.  
  545.     *np++ = '.';
  546.     *np++ = 'n';
  547.     *np++ = 'n';
  548.     *np++ = d_or_x;
  549.     *np++ = NUL;
  550.     }
  551.  
  552.     return namebuf;
  553. }
  554.  
  555.  
  556. FILE *open_data_file(gh, d_or_x, mode)
  557. group_header *gh;
  558. char d_or_x;
  559. int mode;
  560. {
  561.     char data_file[FILENAME];
  562.  
  563.     db_data_path(data_file, gh, d_or_x);
  564.  
  565.     if (mode == -1) {
  566.     unlink(data_file);
  567.     return (FILE *)NULL;
  568.     } else
  569.     return open_file(data_file, mode);
  570. }
  571.  
  572.  
  573. #ifdef NETWORK_DATABASE
  574.  
  575. #define MASTER_FIELDS    5    /* + DB_LOCK_MESSAGE bytes */
  576. #define    GROUP_FIELDS    9
  577. #define    ARTICLE_FIELDS    10
  578.  
  579.  
  580. typedef int32 net_long;
  581.  
  582.  
  583. #ifdef NETWORK_BYTE_ORDER
  584.  
  585. #define net_to_host(buf, n)
  586. #define host_to_net(buf, n)
  587.  
  588. #else
  589.  
  590. static net_to_host(buf, lgt)
  591. register net_long *buf;
  592. int lgt;
  593. {
  594.     while (--lgt >= 0) {
  595.     *buf = ntohl(*buf);
  596.     buf++;
  597.     }
  598. }
  599.  
  600. static host_to_net(buf, lgt)
  601. register net_long *buf;
  602. int lgt;
  603. {
  604.     while (--lgt >= 0) {
  605.     *buf = htonl(*buf);
  606.     buf++;
  607.     }
  608. }
  609. #endif /* not NETWORK_BYTE_ORDER */
  610. #endif /* NETWORK_DATABASE */
  611.  
  612. #define NWDB_MAGIC 0x00190000    /* NN#n <-> NW#n */
  613.  
  614. db_read_master()
  615. {
  616. #ifdef NETWORK_DATABASE
  617.     net_long buf[MASTER_FIELDS];
  618.  
  619.     rewind(master_file);
  620.     if (fread((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  621.     != MASTER_FIELDS) goto err;
  622.     if (fread(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  623.     != DB_LOCK_MESSAGE) goto err;
  624.  
  625.     net_to_host(buf, MASTER_FIELDS);
  626.  
  627.     master.db_magic = buf[0] ^ NWDB_MAGIC;
  628.     master.last_scan = buf[1];
  629.     master.last_size = buf[2];
  630.     master.number_of_groups = buf[3];
  631.     master.db_created = buf[4];
  632.  
  633. #else
  634.     rewind(master_file);
  635.     if (fread((char *)&master, sizeof(master_header), 1, master_file) != 1)
  636.     goto err;
  637. #endif
  638.  
  639.     if (master.db_magic != NNDB_MAGIC)
  640.     sys_error("Database magic number mismatch");
  641.     return;
  642.  
  643.  err:
  644.     sys_error("Incomplete MASTER file");
  645. }
  646.  
  647.  
  648. db_write_master()
  649. {
  650. #ifdef NETWORK_DATABASE
  651.     net_long buf[MASTER_FIELDS];
  652.  
  653.     buf[0] = master.db_magic ^ NWDB_MAGIC;
  654.     buf[1] = master.last_scan;
  655.     buf[2] = master.last_size;
  656.     buf[3] = master.number_of_groups;
  657.     buf[4] = master.db_created;
  658.  
  659.     host_to_net(buf, MASTER_FIELDS);
  660.     rewind(master_file);
  661.     if (fwrite((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  662.     != MASTER_FIELDS) goto err;
  663.     if (fwrite(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  664.     != DB_LOCK_MESSAGE) goto err;
  665. #else
  666.     rewind(master_file);
  667.     if (fwrite((char *)&master, sizeof(master_header), 1, master_file) != 1)
  668.     goto err;
  669. #endif
  670.  
  671.     fflush(master_file);
  672.     return;
  673.  
  674.  err:
  675.     sys_error("Write to MASTER failed");
  676. }
  677.  
  678. db_read_group(gh)
  679. register group_header *gh;
  680. {
  681. #ifdef NETWORK_DATABASE
  682.     net_long buf[GROUP_FIELDS];
  683.  
  684.     if (!db_sequential)
  685.     fseek(master_file,
  686.           (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  687.           GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  688.  
  689.     if (fread((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  690.     goto err;
  691.  
  692.     net_to_host(buf, GROUP_FIELDS);
  693.  
  694.     gh->first_db_article = buf[0];
  695.     gh->last_db_article = buf[1];
  696.     gh->index_write_offset = buf[2];
  697.     gh->data_write_offset = buf[3];
  698.     gh->group_name_length = buf[4];
  699.     gh->master_flag = buf[5];
  700.     gh->first_a_article = buf[6];
  701.     gh->last_a_article = buf[7];
  702.     gh->creation_time = buf[8];
  703. #else
  704.  
  705.     if (!db_sequential)
  706.     fseek(master_file, 
  707.           (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  708.  
  709.     if (fread((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  710.     goto err;
  711.  
  712. #endif
  713.     return;
  714.  
  715.  err:
  716.     sys_error("Read GROUPS failed");
  717. }
  718.  
  719.  
  720. db_write_group(gh)
  721. register group_header *gh;
  722. {
  723. #ifdef NETWORK_DATABASE
  724.     net_long buf[GROUP_FIELDS];
  725.  
  726.     if (!db_sequential)
  727.     fseek(master_file,
  728.           (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  729.           GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  730.  
  731.     buf[0] = gh->first_db_article;
  732.     buf[1] = gh->last_db_article;
  733.     buf[2] = gh->index_write_offset;
  734.     buf[3] = gh->data_write_offset;
  735.     buf[4] = gh->group_name_length;
  736.     buf[5] = gh->master_flag;
  737.     buf[6] = gh->first_a_article;
  738.     buf[7] = gh->last_a_article;
  739.     buf[8] = gh->creation_time;
  740.  
  741.     host_to_net(buf, GROUP_FIELDS);
  742.     if (fwrite((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  743.     goto err;
  744. #else
  745.     if (!db_sequential)
  746.     fseek(master_file, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  747.  
  748.     if (fwrite((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  749.     goto err;
  750. #endif
  751.     fflush(master_file);
  752.     return;
  753.  
  754.  err:
  755.     sys_error("Write GROUPS failed");
  756. }
  757.  
  758. db_read_art(f)
  759. FILE *f;
  760. {
  761. #ifdef NETWORK_DATABASE
  762.     net_long buf[ARTICLE_FIELDS];
  763.  
  764.     if (fread((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  765.     return 0;
  766.  
  767.     net_to_host(buf, ARTICLE_FIELDS);
  768.  
  769.     db_hdr.dh_number = buf[0];
  770.     db_hdr.dh_date = buf[1];
  771.     db_hdr.dh_hpos = buf[2];
  772.     db_hdr.dh_lpos = buf[3];
  773.     db_hdr.dh_fpos = buf[4];
  774.     db_hdr.dh_lines = buf[5];
  775.     db_hdr.dh_replies = buf[6];
  776.     db_hdr.dh_cross_postings = buf[7];
  777.     db_hdr.dh_subject_length = buf[8];
  778.     db_hdr.dh_sender_length = buf[9];
  779. #else
  780.     if (fread((char *)&db_hdr, sizeof(data_header), 1, f) != 1) return 0;
  781. #endif
  782.  
  783.     if (db_hdr.dh_number < 0) {
  784.     current_digest_article = db_hdr.dh_number = -db_hdr.dh_number;
  785.     db_data.dh_type = DH_DIGEST_HEADER;
  786.     } else
  787.     if (db_hdr.dh_number == 0) {
  788.     db_hdr.dh_number = current_digest_article;
  789.     db_data.dh_type = DH_SUB_DIGEST;
  790.     } else {
  791.     current_digest_article = 0;
  792.     db_data.dh_type = DH_NORMAL;
  793.     }
  794.  
  795.     if (db_hdr.dh_cross_postings)
  796.         if (fread((char *)db_data.dh_cross, sizeof(cross_post_number),
  797.           (int)db_hdr.dh_cross_postings, f)
  798.         != (int)db_hdr.dh_cross_postings) return -1;
  799.  
  800.     if (db_hdr.dh_sender_length)
  801.     if (fread(db_data.dh_sender, sizeof(char),
  802.           (int)db_hdr.dh_sender_length, f)
  803.         != db_hdr.dh_sender_length) return -1;
  804.     db_data.dh_sender[db_hdr.dh_sender_length] = NUL;
  805.  
  806.     if (db_hdr.dh_subject_length)
  807.     if (fread(db_data.dh_subject, sizeof(char),
  808.           (int)db_hdr.dh_subject_length, f)
  809.         !=  db_hdr.dh_subject_length) return -1;
  810.     db_data.dh_subject[db_hdr.dh_subject_length] = NUL;
  811.  
  812.     db_read_counter++;
  813.  
  814.     return 1;
  815. }
  816.  
  817. db_write_art(f)
  818. FILE *f;
  819. {
  820. #ifdef NETWORK_DATABASE
  821.     net_long buf[ARTICLE_FIELDS];
  822. #endif
  823.     article_number art_num = db_hdr.dh_number;
  824.  
  825.     switch (db_data.dh_type) {
  826.      case DH_NORMAL:
  827.     break;
  828.      case DH_SUB_DIGEST:
  829.     db_hdr.dh_number = 0;
  830.     break;
  831.      case DH_DIGEST_HEADER:
  832.     db_hdr.dh_number = -art_num;
  833.     break;
  834.     }
  835.  
  836. #ifdef NETWORK_DATABASE
  837.     buf[0] = db_hdr.dh_number;
  838.     buf[1] = db_hdr.dh_date;
  839.     buf[2] = db_hdr.dh_hpos;
  840.     buf[3] = db_hdr.dh_lpos;
  841.     buf[4] = db_hdr.dh_fpos;
  842.     buf[5] = db_hdr.dh_lines;
  843.     buf[6] = db_hdr.dh_replies;
  844.     buf[7] = db_hdr.dh_cross_postings;
  845.     buf[8] = db_hdr.dh_subject_length;
  846.     buf[9] = db_hdr.dh_sender_length;
  847.  
  848.     host_to_net(buf, ARTICLE_FIELDS);
  849.  
  850.     if (fwrite((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  851.     return -1;
  852. #else
  853.  
  854.     if (fwrite((char *)&db_hdr, sizeof(data_header), 1, f) != 1)
  855.     return -1;
  856.  
  857. #endif
  858.     if (db_hdr.dh_cross_postings)
  859.         if (fwrite((char *)db_data.dh_cross, sizeof(cross_post_number),
  860.           (int)db_hdr.dh_cross_postings, f)
  861.         != (int)db_hdr.dh_cross_postings) return -1;
  862.  
  863.     if (db_hdr.dh_sender_length)
  864.     if (fwrite(db_data.dh_sender, sizeof(char),
  865.           (int)db_hdr.dh_sender_length, f)
  866.         != db_hdr.dh_sender_length) return -1;
  867.  
  868.     if (db_hdr.dh_subject_length)
  869.     if (fwrite(db_data.dh_subject, sizeof(char),
  870.           (int)db_hdr.dh_subject_length, f)
  871.         != db_hdr.dh_subject_length) return -1;
  872.  
  873.     db_hdr.dh_number = art_num;
  874.  
  875.     db_write_counter++;
  876.  
  877.     return 1;
  878. }
  879.  
  880.  
  881. off_t get_index_offset(gh, art_num)
  882. group_header *gh;
  883. article_number art_num;
  884. {
  885. #ifdef NETWORK_DATABASE
  886.     return (off_t)((art_num - gh->first_db_article) * sizeof(net_long));
  887. #else
  888.     return (off_t)((art_num - gh->first_db_article) * sizeof(off_t));
  889. #endif
  890. }
  891.  
  892. off_t get_data_offset(gh, art_num)
  893. group_header *gh;
  894. article_number art_num;
  895. {
  896.     FILE *index;
  897.     off_t data_offset;
  898.  
  899.     if (gh->first_db_article == art_num) return (off_t)0;
  900.  
  901.     index = open_data_file(gh, 'x', OPEN_READ);
  902.     if (index == NULL) return (off_t)(-1);
  903.  
  904.     fseek(index, get_index_offset(gh, art_num), 0);
  905.     if (!db_read_offset(index, &data_offset))
  906.     return (off_t)(-1);
  907.  
  908.     fclose(index);
  909.  
  910.     return data_offset;
  911. }
  912.  
  913.  
  914. db_read_offset(f, offset)
  915. FILE *f;
  916. off_t *offset;
  917. {
  918. #ifdef NETWORK_DATABASE
  919.     net_long temp;
  920.  
  921.     if (fread((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  922.  
  923. #ifndef NETWORK_BYTE_ORDER
  924.     temp = ntohl(temp);
  925. #endif
  926.     *offset = temp;
  927. #else
  928.  
  929.     if (fread((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  930. #endif
  931.     return 1;
  932. }
  933.  
  934. db_write_offset(f, offset)
  935. FILE *f;
  936. off_t *offset;
  937. {
  938. #ifdef NETWORK_DATABASE
  939.     net_long temp;
  940.  
  941.     temp = *offset;
  942.  
  943. #ifndef NETWORK_BYTE_ORDER
  944.     temp = htonl(temp);
  945. #endif
  946.     if (fwrite((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  947.  
  948. #else
  949.  
  950.     if (fwrite((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  951. #endif
  952.     return 1;
  953. }
  954.  
  955.