Skip to content
Snippets Groups Projects
Select Git revision
  • a7459809a4158c43f52e51958ed4420771570c50
  • master default
  • wip-add-ed25519
  • disable-sha1
  • lsh-2.0.4
  • experimental-20050201
  • lsh-1.4.2
  • lsh-1.2
  • lsh_2.1_release_20130626
  • converted-master-branch-to-git
  • nettle_2.4_release_20110903
  • nettle_2.3_release_20110902
  • nettle_2.2_release_20110711
  • nettle_2.1_release_20100725
  • camellia_32bit_20100720
  • nettle_2.0_release_20090608
  • converted-lsh-2.0.4-branch-to-git
  • lsh_2.0.4_release_20070905
  • lsh_2.9_exp_release_20070404
  • nettle_1.15_release_20061128
  • after_experimental_merge_20060516
  • branch_before_experimental_merge_20060516
  • converted-experimental-branch-to-git
  • head_before_experimental_merge_20060516
  • lsh_2.0.3_release_20060509
  • lsh_2.0.2_release_20060127
  • nettle_1.14_release_20051205
  • nettle_1.13_release_20051006
28 results

getopt.h

Blame
  • dbck.c 24.38 KiB
    /*
     * dbck.c - A simple database checker and corrector.
     *
     * Author: Per Cederqvist.
     */
    
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <kom-types.h>
    #include <config.h>
    #include "cache.h"
    #include "log.h"
    #include "lyskomd.h"
    #include "misc-parser.h"
    #include "smalloc.h"
    #include <debug.h>
    #include "dbck-cache.h"
    
    char datafilename[1024];   /* Full pathname to the database file */
    char backupfilename[1024]; /* Full pathname to the backup file */
    char textfilename[1024];
    char textbackupfilename[1024]; /* unshrinked text-file. */
    static const char *dbase_dir = NULL;     /* Directory where database resides */
    
    #define TEXTBACKUPFILE_NAME "db/backup-texts"
    
    int vflag=0;			/* Verbose - list statistics also. */
    int iflag=0;			/* Interactive - prompt user and repair. */
    int rflag=0;			/* Repair simple error without confirmation. */
    int gflag=0;			/* Garbage collect text-file. */
    	
    int modifications = 0;
    
    typedef struct {
        int	created_confs;
    } Person_scratchpad;
    
    static const Person_scratchpad   EMPTY_PERSON_SCRATCHPAD = { 0 };
    
    #include "limits.h"
    
    static Person_scratchpad *person_scratchpad[MAX_CONF];
    
    #ifdef DEBUG
    int	buglevel = 0;
    #endif
    
    extern void
    log (const char * format, ...)
    {
        va_list AP;
    
        va_start(AP, format);
    
        vfprintf(stdout, format, AP);
    
        va_end(AP);
    }
    
    extern void
    restart_kom (const char * format, ...)
    {
        va_list AP;
    
        va_start(AP, format);
    
        vfprintf(stdout, format, AP);
    
        va_end(AP);
        exit(1);
    }
    
    static Person_scratchpad *
    alloc_person_scratchpad(void)
    {
        Person_scratchpad *p;
    
        p = smalloc(sizeof(Person_scratchpad));
        *p = EMPTY_PERSON_SCRATCHPAD;
        return p;
    }
    
    
    static Bool
    is_comment_to(Text_no    comment,
    	      Text_stat *parent)
    {
        int i;
    
        for ( i = 0; i < parent->no_of_misc; i++ )
        {
    	switch( parent->misc_items[ i ].type )
    	{
    	case comm_in:
    	    if ( parent->misc_items[ i ].datum.commented_in == comment )
    		return TRUE;
    	    break;
    	default:
    	    break;
    	}
        }
    
        return FALSE;
    }
    
    static Bool
    is_commented_in(Text_no    parent,
    		Text_stat *child)
    {
        int i;
    
        for ( i = 0; i < child->no_of_misc; i++ )
        {
    	switch( child->misc_items[ i ].type )
    	{
    	case comm_to:
    	    if ( child->misc_items[ i ].datum.comment_to == parent )
    		return TRUE;
    	    break;
    	default:
    	    break;
    	}
        }
    
        return FALSE;
    }
    	
    static Bool
    is_footnote_to(Text_no    footnote,
    	       Text_stat *parent)
    {
        int i;
    
        for ( i = 0; i < parent->no_of_misc; i++ )
        {
    	switch( parent->misc_items[ i ].type )
    	{
    	case footn_in:
    	    if ( parent->misc_items[ i ].datum.footnoted_in == footnote )
    		return TRUE;
    	    break;
    	default:
    	    break;
    	}
        }
    
        return FALSE;
    }
    
    static Bool
    is_footnoted_in(Text_no    parent,
    		Text_stat *child)
    {
        int i;
    
        for ( i = 0; i < child->no_of_misc; i++ )
        {
    	switch( child->misc_items[ i ].type )
    	{
    	case footn_to:
    	    if ( child->misc_items[ i ].datum.footnote_to == parent )
    		return TRUE;
    	    break;
    	default:
    	    break;
    	}
        }
    
        return FALSE;
    }
    
    Member *
    locate_member(Pers_no      pers_no,
    	      Conference * conf_c)
    {
        Member * member;
        int      i;
    
        for(member = conf_c->members.members, i = conf_c->members.no_of_members;
    	i > 0; i--, member++)
        {
    	if ( member->member == pers_no )
    	{
    	    return member;
    	}
        }
    
        return NULL;
    }
    
    /*
     * Delete a misc_info.
     * If it is a recpt, cc_recpt, comm_to or footn_to delete any
     * loc_no, rec_time, sent_by or sent_at that might follow it.
     *
     * Note that the Misc_info is not reallocated.
     */
    
    static void
    delete_misc (Text_stat *tstat,
    	     Misc_info *misc)	/* Pointer to first misc_item to delete. */
    {
        int del = 1;		/* Number of items to delete. */
        				/* Always delete at least one item. */
        Bool ready;
        
        /* Check range of misc */
    
        if (misc < tstat->misc_items
    	|| misc >= tstat->misc_items + tstat->no_of_misc )
        {
    	restart_kom("delete_misc() - misc out of range");
        }
    
        ready = FALSE;
        
        while (ready == FALSE
    	   && misc + del < tstat->misc_items + tstat->no_of_misc )
        {
    	switch ( misc[ del ].type )
    	{
    	case loc_no:
    	case rec_time:
    	case sent_by:
    	case sent_at:
    	    del++;
    	    break;
    
    	case recpt:
    	case cc_recpt:
    	case footn_to:
    	case footn_in:
    	case comm_to:
    	case comm_in:
    	    ready = TRUE;
    	    break;
    
    #ifndef COMPILE_CHECKS
    	default:
    	    restart_kom("delete_misc() - illegal misc found.\n");
    #endif
    	}
        }
    
        tstat->no_of_misc -= del;
    
        /* Move items beyond the deleted ones. */
    
        while ( misc < tstat->misc_items + tstat->no_of_misc )
        {
    	misc[ 0 ] = misc[ del ];
    	misc++;
        }
    }
    
    static int
    confirm(char *question)
    {
        if ( iflag )
        {
    	fputs(question, stdout);
    	fputs(" (y/n) ", stdout);
    	while(1)
    	    switch(getchar())
    	    {
    	    case 'y':
    	    case 'Y':
    		return 1;
    	    case 'n':
    	    case 'N':
    	    case EOF:
    		return 0;
    	    default:
    		break;
    	    }
        }
        else
    	return 0;
    }
    	
        
    static long
    check_misc_infos(Text_no    tno,
    		 Text_stat *tstat)
    {
        const Misc_info   * misc = tstat->misc_items;
        Misc_info   * previous;
        Misc_info_group group;
        Conference *c;
        Text_stat *t;
    
        long error=0;
    
        while (previous = (Misc_info *)misc,
    	   group = parse_next_misc(&misc,
    				   tstat->misc_items + tstat->no_of_misc),
    	   group.type != m_end_of_list && group.type != m_error )
        {
    	switch ( group.type )
    	{
    	case m_recpt:
    	    c = cached_get_conf_stat (group.recipient);
    	    if ( c == NULL && group.recipient == 0 )
    	    {
    		log ("Conference 0 is recipient to text %lu.\n", (u_long)tno);
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Conference 0 is no longer a recipient.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    		
    		break;
    	    }
    
    	    if ( c == NULL )
    		break;
    	    
    	    /* Check loc_no */
    	    if ( group.local_no < c->texts.first_local_no )
    	    {
    		log("Text %lu: Recipient %lu<%lu> loc_no is less than %lu\n",
    		    (u_long)tno, (u_long)group.recipient,
    		    (u_long)group.local_no,
    		    (u_long)c->texts.first_local_no);
    		error++;
    	    }
    	    else if ( c->texts.first_local_no + c->texts.no_of_texts - 1
    		     < group.local_no )
    	    {
    		log("Text %lu: Recipient %lu<%lu> loc_no"
    		    " is greater than %lu\n",
    		    (u_long)tno, (u_long)group.recipient,
    		    (u_long)group.local_no,
    		    (u_long)(c->texts.first_local_no
    			     + c->texts.no_of_texts - 1));
    		error++;
    	    }
    	    else if ( c->texts.texts[group.local_no
    				     - c->texts.first_local_no] != tno )
    	    {
    		log("Text %lu: Recipient %lu<%lu>: that local number "
    		    "is mapped to %lu.\n",
    		    (u_long)tno, (u_long)group.recipient,
    		    (u_long)group.local_no,
    		    (u_long)c->texts.texts[group.local_no
    					   - c->texts.first_local_no]);
    		error++;
    	    }
    
    	    break;
    
    	case m_cc_recpt:
    	    c = cached_get_conf_stat (group.cc_recipient);
    	    if ( c == NULL && group.cc_recipient == 0 )
    	    {
    		log ("Conference 0 is cc_recipient to text %lu.\n",
    		     (u_long)tno);
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Conference 0 is no longer "
    			"a cc_recipient.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    		
    		break;
    	    }
    
    	    if ( c == NULL )
    		break;
    	    
    	    /* Check loc_no */
    	    if ( group.local_no < c->texts.first_local_no )
    	    {
    		log("Text %lu: CC_Recipient %lu<%lu> is less than %lu\n",
    		    (u_long)tno, (u_long)group.cc_recipient,
    		    (u_long)group.local_no,
    		    (u_long)c->texts.first_local_no);
    		error++;
    	    }
    	    else if ( c->texts.first_local_no + c->texts.no_of_texts - 1
    		     < group.local_no )
    	    {
    		log("Text %lu: CC_Recipient %lu<%lu> loc_no is "
    		    "greater than %lu\n",
    		    (u_long)tno, (u_long)group.cc_recipient,
    		    (u_long)group.local_no,
    		    (u_long)(c->texts.first_local_no
    			     + c->texts.no_of_texts - 1));
    		error++;
    	    }
    	    else if ( c->texts.texts[group.local_no
    				     - c->texts.first_local_no] != tno )
    	    {
    		log("Text %lu: CC_Recipient %lu<%lu>: that local "
    		    "number is mapped to %lu.\n",
    		    (u_long)tno, (u_long)group.cc_recipient,
    		    (u_long)group.local_no,
    		    (u_long)c->texts.texts[group.local_no
    					   - c->texts.first_local_no]);
    		error++;
    	    }
    
    	    break;
    
    	case m_comm_to:
    	    t = cached_get_text_stat(group.comment_to);
    
    	    if ( t == NULL )
    	    {
    		log("Text %lu is a comment to %lu, which doesn't exist.\n",
    		    (u_long)tno, (u_long)group.comment_to);
    
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Comment-link deleted.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    		    
    		error++;
    	    }
    	    else if (!is_comment_to(tno, t))
    	    {
    		log("Text %lu is a comment to %lu, but not the reverse.\n",
    		    (u_long)tno, (u_long)group.comment_to);
    		error++;
    	    }
    
    	    break;
    
    	case m_comm_in:
    	    t = cached_get_text_stat(group.commented_in);
    
    	    if ( t == NULL )
    	    {
    		log("Text %lu is commented in %lu, which doesn't exist.\n",
    		    (u_long)tno, (u_long)group.commented_in);
    
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Comment-link deleted.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    	    }
    	    else if (!is_commented_in(tno, t))
    	    {
    		log("Text %lu is a comment to %lu, but not the reverse.\n",
    		    (u_long)tno, (u_long)group.commented_in);
    		error++;
    	    }
    
    	    break;
    
    	case m_footn_to:
    	    t = cached_get_text_stat(group.footnote_to);
    
    	    if ( t == NULL )
    	    {
    		log("Text %lu is a footnote to %lu, which doesn't exist.\n",
    		    (u_long)tno, (u_long)group.footnote_to);
    
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Footnote-link deleted.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    	    }
    	    else if (!is_footnote_to(tno, t))
    	    {
    		log("Text %lu is a footnote to %lu, but not the reverse.\n",
    		    (u_long)tno, (u_long)group.footnote_to);
    		error++;
    	    }
    
    	    break;
    	    
    	case m_footn_in:
    	    t = cached_get_text_stat(group.footnoted_in);
    
    	    if ( t == NULL )
    	    {
    		log("Text %lu is footnoted in %lu, which doesn't exist.\n",
    		    (u_long)tno, (u_long)group.footnoted_in);
    
    		if (rflag || confirm("Repair by deleting misc_item? "))
    		{
    		    delete_misc(tstat, previous);
    		    mark_text_as_changed(tno);
    		    modifications++;
    		    log("Repaired: Footnote-link deleted.\n");
    		    misc = previous;
    		}
    		else
    		    error++;
    	    }
    	    else if (!is_footnoted_in(tno, t))
    	    {
    		log("Text %lu is a footnot to %lu, but not the reverse.\n",
    		    (u_long)tno, (u_long)group.footnoted_in);
    		error++;
    	    }
    
    	    break;
    
    	default:
    	    log("check_misc_infos(): parse_next_misc returned type %lu\n",
    		(u_long)group.type);
    	    break;
    	}
        }
    
        if ( group.type == m_error )
        {
    	log("Text %lu has a bad misc_info_list.\n", (u_long)tno);
    	error++;
        }
    
        return error;
    }
    
    		    
    		     
    		 
    static long
    check_texts(void)
    {
        Text_no    ct = 0;
        Text_stat *ctp=NULL;
        long	errors = 0;
        Text_no	number_of_texts = 0;
        u_long	bytes=0;
        u_long	max_bytes=0;
        Text_no	max_text=0;
    
        while ( ct = traverse_text(ct) )
        {
    	number_of_texts++;
    	
    	ctp = cached_get_text_stat( ct );
    	if ( ctp == NULL )
    	{
    	    log("Text %lu nonexistent.\n", ct);
    	    errors++;
    	}
    	else
    	{
    	    bytes += ctp->no_of_chars;
    	    if ( ctp->no_of_chars > max_bytes )
    	    {
    		max_bytes = ctp->no_of_chars;
    		max_text = ct;
    	    }
    	    
    	    /* no_of_marks is not yet checked. */
    	    errors += check_misc_infos(ct, ctp);
    	}
        }
    
        if (vflag)
        {
    	if ( number_of_texts == 0 )
    	    log("WARNING: No texts found.\n");
    	else
    	{
    	    log("Total of %lu texts (total %lu bytes, "
    		"average %lu bytes/text).\n",
    		(u_long)number_of_texts,
    		(u_long)bytes,
    		(u_long)(bytes/number_of_texts));
    	    log("Longest text is %lu (%lu bytes).\n",
    		(u_long)max_text, (u_long)max_bytes);
    	}
        }
        
        return errors;
    }
    
    static Bool
    adjust_text_list(Text_list *text_list)
    {
        u_long zeroes;
        u_long i;
    
        for (zeroes = 0;
    	 zeroes < text_list->no_of_texts && text_list->texts[ zeroes ] == 0;
    	 zeroes++)
    	;
    
        if ( zeroes > 0 )
        {
    	text_list->no_of_texts -= zeroes;
    	text_list->first_local_no += zeroes;
    
    	for ( i = 0; i < text_list->no_of_texts; i++)
    	    text_list->texts[ i ] = text_list->texts[ i + zeroes ];
    
    	text_list->texts = srealloc(text_list->texts,
    				    (text_list->no_of_texts
    				     * sizeof(Text_no)));
        }
    
        return zeroes > 0;
    }
        
    static int
    check_created_texts(Pers_no pno,
    		    Text_list *created)
    {
        u_long i;
        Text_stat *t;
        int error=0;
    
        for ( i=0; i < created->no_of_texts; i++ )
        {
    	if (created->texts[i] != 0)
    	{
    	    t = cached_get_text_stat(created->texts[i]);
    	    if ( t != NULL && t->author != pno)
    	    {
    		log("Person %lu is author of text %lu whose author is %lu.\n",
    		    (u_long)pno, (u_long)created->texts[i],
    		    (u_long)t->author);
    		error++;
    	    }
    
    	    if ( t == NULL )
    	    {
    		log("Person %lu is author of text %lu, which doesn't exist.\n",
    		    (u_long)pno, (u_long)created->texts[i]);
    		if ( rflag ||
    		    confirm("Repair by setting to text_no to 0 in local map"))
    		{
    		    created->texts[i] = 0;
    		    mark_person_as_changed(pno);
    		    modifications++;
    		    log("Repaired: created_texts corrected.\n");
    		}
    		else
    		    error++;
    	    }
    	}
        }
    
        if ( created->no_of_texts > 0 && created->texts[0] == 0 )
        {
    	log("Person %lu has a bad created_texts array. Starts with a 0.\n",
    	    (u_long)pno);
    	if ( rflag || confirm ("Repair by adjusting created_texts"))
    	{
    	    adjust_text_list(created);
    	    mark_person_as_changed(pno);
    	    modifications++;
    	    log("Repaired: created_texts adjusted.\n");
    	}
    	else
    	    error++;
        }
        
        return error;
    }
    
    static int
    check_membership(Pers_no pno,
    		 const Membership *mship)
    {
        int error=0;
        Conference *conf;
        int i;
        Local_text_no last=0;
        
        conf = cached_get_conf_stat(mship->conf_no);
        if ( conf == NULL )
        {
    	log("Person %lu is a member in the non-existing conference %lu.\n",
    	    (u_long)pno, (u_long)mship->conf_no);
    	error++;
        }
        else
        {
    	/* Check read texts */
    	if ( mship->last_text_read >
    	    conf->texts.first_local_no + conf->texts.no_of_texts - 1)
    	{
    	    log("Person %lu has read text %lu in conf %lu, "
    		"which only has %lu texts.\n",
    		(u_long)pno,
    		(u_long)mship->last_text_read,
    		(u_long)mship->conf_no,
    		(u_long)(conf->texts.first_local_no
    			 + conf->texts.no_of_texts - 1));
    	    error++;
    	}
    
    	for ( last = i = 0; i < mship->no_of_read; i++)
    	{
    	    if ( mship->read_texts[i] <= last )
    	    {
    		log("Person %lu's membership in %lu is corrupt:"
    		    " read text number %lu<%lu> <= %lu.\n",
    		    (u_long)pno, (u_long)mship->conf_no,
    		    (u_long)mship->read_texts[i], (u_long)i, (u_long)last);
    		error++;
    	    }
    
    	    last = mship->read_texts[i];
    	}
    
    	/* Check that he is a member */
    	if ( locate_member(pno, conf) == NULL )
    	{
    	    log("Person %lu is a member in %lu in which he isn't a member.\n",
    		(u_long)pno, (u_long)mship->conf_no);
    	    error++;
    	}
        }
    
        return error;
    }
    
    	    
    
    static int
    check_membership_list(Pers_no pno,
    		      const Membership_list *mlist)
    {
        int errors=0;
        int i;
        
        for (i = 0; i < mlist->no_of_confs; i++)
    	errors += check_membership(pno, &mlist->confs[i]);
    
        return errors;
    }
    
    
    static int
    check_persons(void)
    {
        Pers_no     cp = 0;
        Person     *pstat=NULL;
        Conference *cstat=NULL;
        long	errors = 0;
        Pers_no	number_of_persons=0;
    
        while ( cp = traverse_person(cp) )
        {
    	number_of_persons++;
    	pstat = cached_get_person_stat (cp);
    	cstat = cached_get_conf_stat (cp);
    	
    	if ( pstat == NULL )
    	{
    	    log("Person %lu nonexistent.\n", (u_long)cp);
    	    errors++;
    	}
    	else if (cstat == NULL)
    	{
    	    log("Person %lu has no conference.\n", (u_long)cp);
    	    errors++;
    	}
    	else if (!cstat->type.letter_box)
    	{
    	    log("Person %lu's conference is not a letter_box.\n",
    		(u_long)cp);
    	    errors++;
    	}
    	else
    	{
    	    errors += (check_created_texts(cp, &pstat->created_texts)
    		       + check_membership_list(cp, &pstat->conferences));
    	}
        }
    
        if (vflag)
    	log("Total of %lu persons.\n", number_of_persons);
        
        return errors;
    }
    
    static Bool
    is_recipient(Conf_no	 conf_no,
    	     Text_stat * t_stat)
    {
        int i;
        
        for ( i = 0; i < t_stat->no_of_misc; i++ )
        {
    	switch( t_stat->misc_items[ i ].type )
    	{
    	case recpt:
    	    if ( t_stat->misc_items[ i ].datum.recipient == conf_no )
    	    {
    		return TRUE;
    	    }
    	    break;
    	    
    	case cc_recpt:
    	    if ( t_stat->misc_items[ i ].datum.cc_recipient == conf_no )
    	    {
    		return TRUE;
    	    }
    	    break;
    	    
    	case rec_time:
    	case comm_to:
    	case comm_in:
    	case footn_to:
    	case footn_in:
    	case sent_by:
    	case sent_at:
    	case loc_no:
    	    break;
    	    
    #ifndef COMPILE_CHECKS
    	default:
    	    restart_kom("is_recipient(): illegal misc_item\n");
    #endif
    	}
        }
    
        return FALSE;
    }
    
    static int
    check_texts_in_conf(Conf_no cc,
    		    Text_list *tlist)
    {
        u_long i;
        Text_stat *t;
        int error=0;
    
        for ( i=0; i < tlist->no_of_texts; i++ )
        {
    	if (tlist->texts[i] != 0)
    	{
    	    t = cached_get_text_stat(tlist->texts[i]);
    	    if ( t == NULL )
    	    {
    		log("Text %lu<%lu> in conference %lu is non-existent.\n",
    		    (u_long)tlist->texts[i],
    		    (u_long)i + tlist->first_local_no,
    		    (u_long)cc);
    
    		if (rflag
    		    || confirm("Repair by setting Text_no to 0 in the map?") )
    		{
    		    tlist->texts[i]=0;
    		    mark_conference_as_changed(cc);
    		    modifications++;
    		    log("Repaired: %lu is no longer a recipient.\n",
    			(u_long)cc);
    		}
    		else
    		    error++;
    	    }
    	    else
    	    {
    		if ( !is_recipient(cc, t) )
    		{
    		    log("Text %lu<%lu> in conference %lu doesn't "
    			"have the conference as recipient.\n",
    			(u_long)tlist->texts[i],
    			(u_long)i + tlist->first_local_no,
    			(u_long)cc);
    
    		    if (confirm("Repair by setting Text_no to 0 in the map?") )
    		    {
    			tlist->texts[i]=0;
    			mark_conference_as_changed(cc);
    			modifications++;
    			log("Repaired: %lu is no longer a recipient.\n",
    			    (u_long)cc);
    		    }
    		    else
    			error++;
    		}
    	    }
    	}
        }
    
        if ( tlist->no_of_texts > 0 && tlist->texts[0] == 0 )
        {
    	log("Conference %lu has a bad Text_list. Starts with a 0.\n",
    	    (u_long)cc);
    	if ( rflag || confirm ("Repair by adjusting text_list"))
    	{
    	    adjust_text_list(tlist);
    	    mark_conference_as_changed(cc);
    	    modifications++;
    	    log("Repaired: text_list adjusted.\n");
    	}
    	else
    	    error++;
        }
        
        return error;
    }
    
    Membership *
    locate_membership(Conf_no     conf_no,
    		  Person    * pers_p)
    {
        Membership * confp;
        int    i;
    
        for(confp = pers_p->conferences.confs, i = pers_p->conferences.no_of_confs;
    	i > 0; i--, confp++)
        {
    	if ( confp->conf_no == conf_no )
    	{
    	    return confp;
    	}
        }
    
        return NULL;
    }
    
    static int
    check_member(Conf_no cc,
    	     Member *memb)
    {
        Person *pp;
        int error=0;
    
        pp = cached_get_person_stat(memb->member);
        if ( pp == NULL )
        {
    	log("Person %lu, who is supposed to be a member in conf %lu, "
    	    "is nonexistent.\n",
    	    (u_long)memb->member, (u_long)cc);
    	error++;
        }
        else
        {
    	if ( locate_membership(cc, pp) == NULL )
    	{
    	    log("Person %lu is not a member in conf %lu.\n",
    		(u_long)memb->member,
    		(u_long)cc);
    	    error++;
    	}
        }
    
        return error;
    }
    
    static int
    check_member_list(Conf_no cc,
    		  const Member_list *mlist)
    {
        int errors=0;
        int i;
        
        for (i = 0; i < mlist->no_of_members; i++)
    	errors += check_member(cc, &mlist->members[i]);
    
        return errors;
    }
    
    
    static int
    check_confs(void)
    {
        Conf_no     cc = 0;
        Person     *pstat=NULL;
        Conference *cstat=NULL;
        long	errors = 0;
        Conf_no	number_of_confs = 0;
    
        while ( (cc = traverse_conference(cc)) != 0 )
        {
    	number_of_confs++;
    	cstat = cached_get_conf_stat (cc);
    	
    	if ( cstat == NULL )
    	{
    	    log("Conference %lu nonexistent.\n", (u_long)cc);
    	    errors++;
    	}
    	else
    	{
    	    if (cstat->type.letter_box)
    	    {
    		pstat = cached_get_person_stat(cc);
    		if (pstat == NULL)
    		{
    		    log("Mailbox %lu has no person.\n", (u_long)cc);
    		    errors++;
    		}
    	    }
    	    else		/* not letter_box */
    	    {
    		/* Remember that the creator might no longer exist. */
    		if ( person_scratchpad[ cstat->creator ] != NULL )
    		    ++person_scratchpad[ cstat->creator ]->created_confs;
    	    }
    
    	    errors += (check_texts_in_conf(cc, &cstat->texts)
    		       + check_member_list(cc, &cstat->members));
    	}
        }
    
        if ( vflag )
    	log("Total of %lu conferences.\n", (u_long)number_of_confs);
    
        return errors;
    }
    
    static void
    init_person_scratch(void)
    {
        Pers_no pno = 0;
        
        while( (pno = traverse_person(pno)) != 0 )
        {
    	person_scratchpad[pno] = alloc_person_scratchpad();
        }
    }
    
    static long
    post_check_persons(void)
    {
        long errors = 0;
        
        Pers_no pers_no = 0;
        Person *pstat;
    
        while ( (pers_no = traverse_person(pers_no)) != 0 )
        {
    	if ( (pstat = cached_get_person_stat(pers_no)) == NULL )
    	{
    	    log("INTERNAL DBCK ERROR: post_check_persons(): can't "
    		"cached_get_person_stat(%d).\n", pers_no);
    	}
    
    	if ( person_scratchpad[pers_no]->created_confs
    	    != pstat->created_confs )
    	{
    	    log("Person %d has created %d conferences, not %d (as said in "
    		"his person-stat).\n",
    		pers_no, person_scratchpad[pers_no]->created_confs,
    		pstat->created_confs);
    	    if ( rflag || confirm("Repair by altering person-stat? ") )
    	    {
    		pstat->created_confs
    		    = person_scratchpad[pers_no]->created_confs;
    		mark_person_as_changed(pers_no);
    		log("Person-stat corrected.\n");
    	    }
    	    else
    		errors++;
    	}
        }
    
        return errors;
    }
    
    		
    /*
     * Returns 0 if the database seems to be correct.
     */
    static long
    check_data_base(void)
    {
        long errors;
    
        init_person_scratch();
        errors = check_texts() + check_persons() + check_confs();
        return errors + post_check_persons();
    }
    
    static void
    init_data_base(void)
    {
        if (dbase_dir == NULL)
          dbase_dir = DEFAULT_DBASE_DIR;
        
        sprintf(datafilename,   "%s/%s", dbase_dir, DATAFILE_NAME);
        sprintf(backupfilename, "%s/%s", dbase_dir, BACKUPFILE_NAME);
        sprintf(textfilename, "%s/%s", dbase_dir, TEXTFILE_NAME);
        sprintf(textbackupfilename, "%s/%s", dbase_dir, TEXTBACKUPFILE_NAME);
    
        if ( vflag )
        {
    	log("Database = %s\n", datafilename);
    	log("Backup   = %s\n", backupfilename);
    	log("Text     = %s\n", textfilename);
    	log("Textback = %s\n", textbackupfilename);
        }
        
        if ( init_cache() == FAILURE )
    	restart_kom("Can't find database.\n");
    }
    
    void
    garb_text_file(void)
    {
        Text_no tno = 0;
        String text;
    
        log("Renaming %s to %s", textfilename, textbackupfilename);
        rename(textfilename, textbackupfilename);
        log("Writing texts to (new) %s", textfilename);
        fflush(stdout);
        fflush(stderr);
        cache_open_new_text_file();
    
        while ( (tno = traverse_text(tno)) != 0 )
        {
    	text = cached_get_text(tno);
    	cached_flush_text(tno, text);
    	free_tmp();
        }
        log("Writing datafile with new indexes.\n");
        fflush(stdout);
        fflush(stderr);
        cache_sync();
        log("Ready.");    
    }
    
    	
    	
    
    
    int
    main (int    argc,
          char **argv)
    {
        int i;
        int errors;
        BUGDECL;
    
        for (i = 1; i < argc && argv[i][0] == '-'; i++)
        {
    	switch (argv[i][1])
    	{
    #ifdef DEBUG
    	case 'd':
    	    buglevel++;
    	    break;
    #endif
    
    	case 'D':		/* Database directory */
    	    dbase_dir = argv[i]+2;
    	    break;
    
    	case 'i':		/* Running interactively. */
    	    iflag++;		/* Will ask user and try to repair. */
    	    break;
    
    	case 'r':		/* Repair simple errors wihtout asking. */
    	    rflag++;
    	    break;
    
    	case 'v':		/* Verbose: report more than errors. */
    	    vflag++;
    	    break;
    
    	case 'g':		/* Garbage collect: compress text-file. */
    	    gflag++;
    	    break;
    
    	default:
    	    restart_kom("usage: dbck [options]\n");
    	}
        }
        
        s_set_storage_management(smalloc, srealloc, sfree);
    
        init_data_base();
        errors = check_data_base();
        if ( iflag )
    	log("Total of %d error%s remains.\n", errors, errors == 1 ? "" : "s");
        else if ( vflag && errors > 0 )
    	log("%d error%s found.\n", errors, errors == 1 ? "" : "s");
    
        if ( modifications > 0 )
        {
    	log("%d modification%s made. Syncing...\n",
    	    modifications, modifications == 1 ? "" : "s");
    	fflush(stdout);
    	fflush(stderr);
    	cache_sync();
    	log("ready.\n");
        }
    
        if ( modifications == 0 && errors == 0 && gflag )
        {
    	log("No errors found. Compressing textfile.\n");
    	fflush(stdout);
    	fflush(stderr);
    	garb_text_file();
    	log("ready.\n");
        }
        
        return errors != 0;
    }