Skip to content
Snippets Groups Projects
dbck.c 28.6 KiB
Newer Older
Linus Tolke's avatar
Linus Tolke committed
/*
 * $Id: dbck.c,v 0.23 1994/06/18 21:05:43 ceder Exp $
 * Copyright (C) 1991, 1992, 1993, 1994  Lysator Academic Computer Association.
Linus Tolke's avatar
Linus Tolke committed
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 1, or (at your option) 
 * any later version.
 * 
 * LysKOM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
 * Please mail bug reports to bug-lyskom@lysator.liu.se. 
 */
Per Cederqvist's avatar
Per Cederqvist committed
/*
 * dbck.c - A simple database checker and corrector.
 *
 * Author: Per Cederqvist.
 */

static char *rcsid = "$Id: dbck.c,v 0.23 1994/06/18 21:05:43 ceder Exp $";
#include "rcs.h"
USE(rcsid);
Per Cederqvist's avatar
Per Cederqvist committed
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
#include <stdio.h>
Per Cederqvist's avatar
Per Cederqvist committed
#ifdef HAVE_STDARG_H
#  include <stdarg.h>
#endif
#include <time.h>
#include <sys/types.h>
Per Cederqvist's avatar
Per Cederqvist committed

Per Cederqvist's avatar
Per Cederqvist committed
#include "misc-types.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "kom-types.h"
#include "tmp-limits.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "lyskomd.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "log.h"
#include "server/smalloc.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "misc-parser.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "cache.h"
#include "config.h"
#include "debug.h"
Per Cederqvist's avatar
Per Cederqvist committed
#include "dbck-cache.h"
#include "param.h"
#include "server-config.h"
#include "manipulate.h"

Info kom_info;			/* Not used, but needed by server-config.o. */
struct kom_par param;
Per Cederqvist's avatar
Per Cederqvist committed

/* This is set to TRUE if init_cache finds out that the last part of the
   database is missing. */
Bool truncated_texts = FALSE;

Per Cederqvist's avatar
Per Cederqvist committed
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. */

#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 sflag=0;			/* Statistic flag. */
/* The following variable corresponds to the -c flag, and is
   present here due to a bug in lyskomd 1.6.1. */
int unset_change_name_is_error=0;

Per Cederqvist's avatar
Per Cederqvist committed
int modifications = 0;

typedef struct {
    int	created_confs;
} Person_scratchpad;

static const Person_scratchpad   EMPTY_PERSON_SCRATCHPAD = { 0 };

static Person_scratchpad *person_scratchpad[MAX_CONF];

#ifndef NDEBUG
Per Cederqvist's avatar
Per Cederqvist committed
int	buglevel = 0;
#endif

Per Cederqvist's avatar
Per Cederqvist committed
#if defined(HAVE_VFPRINTF) && defined(HAVE_STDARG_H)
Per Cederqvist's avatar
Per Cederqvist committed
extern void
log (const char * format, ...)
{
    va_list AP;

    va_start(AP, format);

    vfprintf(stdout, format, AP);

    va_end(AP);
}
log (format, a, b, c, d, e, f, g)
     const char * format;
     int a, b, c, d, e, f, g;
{
    fprintf(stdout, format, a, b, c, d, e, f, g);
}
#endif
Per Cederqvist's avatar
Per Cederqvist committed

Per Cederqvist's avatar
Per Cederqvist committed
#if defined(HAVE_VFPRINTF) && defined(HAVE_STDARG_H)
Per Cederqvist's avatar
Per Cederqvist committed
extern void
restart_kom (const char * format, ...)
{
    va_list AP;

    va_start(AP, format);

    vfprintf(stdout, format, AP);

    va_end(AP);
    exit(1);
}
Per Cederqvist's avatar
Per Cederqvist committed
#else
restart_kom (format, a, b, c, d, e, f, g)
     const char * format;
     int a, b, c, d, e, f, g;
{
    fprintf(stdout, format, a, b, c, d, e, f, g);
    exit(1);
}
#endif
Per Cederqvist's avatar
Per Cederqvist committed

void 
register_jubel(Pers_no pno,
	       Text_no divis,
	       Text_no tno)
{
    /* Not needed in dbck.c, but called from conf-file.c. */
}


Per Cederqvist's avatar
Per Cederqvist committed
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;
}

static Member *
Per Cederqvist's avatar
Per Cederqvist committed
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", (unsigned long)tno);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
		    (unsigned long)tno, (unsigned long)group.recipient,
		    (unsigned long)group.local_no,
		    (unsigned long)c->texts.first_local_no);
Per Cederqvist's avatar
Per Cederqvist committed
		error++;
	    }
	    else if ( c->texts.first_local_no + c->texts.no_of_texts - 1
		     < group.local_no )
	    {
Per Cederqvist's avatar
Per Cederqvist committed
		log("Text %lu: Recipient %lu<%lu> loc_no > %lu\n",
		    (unsigned long)tno, (unsigned long)group.recipient,
		    (unsigned long)group.local_no,
		    (unsigned long)(c->texts.first_local_no
Per Cederqvist's avatar
Per Cederqvist committed
			     + c->texts.no_of_texts - 1));
		error++;
	    }
	    else if ( c->texts.texts[group.local_no
				     - c->texts.first_local_no] != tno )
	    {
Per Cederqvist's avatar
Per Cederqvist committed
		log("Text %lu: Recipient %lu<%lu>: %s to %lu.\n",
		    (unsigned long)tno, (unsigned long)group.recipient,
		    (unsigned long)group.local_no,
Per Cederqvist's avatar
Per Cederqvist committed
		    "that local number is mapped",
		    (unsigned long)c->texts.texts[group.local_no
Per Cederqvist's avatar
Per Cederqvist committed
					   - 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",
Per Cederqvist's avatar
Per Cederqvist committed
		if (rflag || confirm("Repair by deleting misc_item? "))
		{
		    delete_misc(tstat, previous);
		    mark_text_as_changed(tno);
		    modifications++;
Per Cederqvist's avatar
Per Cederqvist committed
		    log("Repaired: Conf 0 is no longer a cc_recipient.\n");
Per Cederqvist's avatar
Per Cederqvist committed
		    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",
		    (unsigned long)tno, (unsigned long)group.cc_recipient,
		    (unsigned long)group.local_no,
		    (unsigned long)c->texts.first_local_no);
Per Cederqvist's avatar
Per Cederqvist committed
		error++;
	    }
	    else if ( c->texts.first_local_no + c->texts.no_of_texts - 1
		     < group.local_no )
	    {
Per Cederqvist's avatar
Per Cederqvist committed
		log("Text %lu: CC_Recipient %lu<%lu> loc_no > %lu\n",
		    (unsigned long)tno, (unsigned long)group.cc_recipient,
		    (unsigned long)group.local_no,
		    (unsigned long)(c->texts.first_local_no
Per Cederqvist's avatar
Per Cederqvist committed
			     + c->texts.no_of_texts - 1));
		error++;
	    }
	    else if ( c->texts.texts[group.local_no
				     - c->texts.first_local_no] != tno )
	    {
Per Cederqvist's avatar
Per Cederqvist committed
		log("Text %lu: CC_Recipient %lu<%lu>: %s to %lu.\n",
		    (unsigned long)tno, (unsigned long)group.cc_recipient,
		    (unsigned long)group.local_no,
Per Cederqvist's avatar
Per Cederqvist committed
		    "that local number is mapped",
		    (unsigned long)c->texts.texts[group.local_no
Per Cederqvist's avatar
Per Cederqvist committed
					   - 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",
		    (unsigned long)tno, (unsigned long)group.comment_to);
Per Cederqvist's avatar
Per Cederqvist committed

		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",
		    (unsigned long)tno, (unsigned long)group.comment_to);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
		    (unsigned long)tno, (unsigned long)group.commented_in);
Per Cederqvist's avatar
Per Cederqvist committed

		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",
		    (unsigned long)tno, (unsigned long)group.commented_in);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
		    (unsigned long)tno, (unsigned long)group.footnote_to);
Per Cederqvist's avatar
Per Cederqvist committed

		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",
		    (unsigned long)tno, (unsigned long)group.footnote_to);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
		    (unsigned long)tno, (unsigned long)group.footnoted_in);
Per Cederqvist's avatar
Per Cederqvist committed

		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",
		    (unsigned long)tno, (unsigned long)group.footnoted_in);
Per Cederqvist's avatar
Per Cederqvist committed
		error++;
	    }

	    break;

	default:
	    log("check_misc_infos(): parse_next_misc returned type %lu\n",
Per Cederqvist's avatar
Per Cederqvist committed
	    break;
	}
    }

    if ( group.type == m_error )
    {
	log("Text %lu has a bad misc_info_list.\n", (unsigned long)tno);
Per Cederqvist's avatar
Per Cederqvist committed
	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;
    unsigned long	bytes=0;
    unsigned long	max_bytes=0;
Per Cederqvist's avatar
Per Cederqvist committed
    Text_no	max_text=0;

    while ( (ct = traverse_text(ct)) != 0 )
Per Cederqvist's avatar
Per Cederqvist committed
    {
	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
	{
Per Cederqvist's avatar
Per Cederqvist committed
	    log("Total of %lu texts (total %lu bytes, avg. %lu bytes/text).\n",
		(unsigned long)number_of_texts,
		(unsigned long)bytes,
		(unsigned long)(bytes/number_of_texts));
Per Cederqvist's avatar
Per Cederqvist committed
	    log("Longest text is %lu (%lu bytes).\n",
		(unsigned long)max_text, (unsigned long)max_bytes);
Per Cederqvist's avatar
Per Cederqvist committed
	}
    }
    
    return errors;
}

static Bool
adjust_text_list(Text_list *text_list)
{
    unsigned long zeroes;
    unsigned long i;
Per Cederqvist's avatar
Per Cederqvist committed

    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)
{
Per Cederqvist's avatar
Per Cederqvist committed
    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",
		    (unsigned long)pno, (unsigned long)created->texts[i],
		    (unsigned long)t->author);
Per Cederqvist's avatar
Per Cederqvist committed
		error++;
	    }

	    if ( t == NULL )
	    {
		log("Person %lu is author of text %lu, which doesn't exist.\n",
		    (unsigned long)pno, (unsigned long)created->texts[i]);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
Per Cederqvist's avatar
Per Cederqvist committed
	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",
	    (unsigned long)pno, (unsigned long)mship->conf_no);
Per Cederqvist's avatar
Per Cederqvist committed
	error++;
    }
    else
    {
	/* Check read texts */
	if ( mship->last_text_read >
	    conf->texts.first_local_no + conf->texts.no_of_texts - 1)
	{
Per Cederqvist's avatar
Per Cederqvist committed
	    log("Person %lu %s %lu in conf %lu, which only has %lu texts.\n",
Per Cederqvist's avatar
Per Cederqvist committed
		"has read text",
		(unsigned long)mship->last_text_read,
		(unsigned long)mship->conf_no,
		(unsigned long)(conf->texts.first_local_no
Per Cederqvist's avatar
Per Cederqvist committed
			 + conf->texts.no_of_texts - 1));
	    error++;
	}

	for ( last = i = 0; i < mship->no_of_read; i++)
	{
	    if ( mship->read_texts[i] <= last )
	    {
Per Cederqvist's avatar
Per Cederqvist committed
		log("Person %lu's membership in %lu %s %lu<%lu> <= %lu.\n",
		    (unsigned long)pno, (unsigned long)mship->conf_no,
Per Cederqvist's avatar
Per Cederqvist committed
		    "is corrupt: read text number",
		    (unsigned long)mship->read_texts[i], (unsigned long)i, (unsigned long)last);
Per Cederqvist's avatar
Per Cederqvist committed
		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",
		(unsigned long)pno, (unsigned long)mship->conf_no);
Per Cederqvist's avatar
Per Cederqvist committed
	    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)) != 0 )
Per Cederqvist's avatar
Per Cederqvist committed
    {
	number_of_persons++;
	pstat = cached_get_person_stat (cp);
	cstat = cached_get_conf_stat (cp);
	
	if ( pstat == NULL )
	{
	    log("Person %lu nonexistent.\n", (unsigned long)cp);
Per Cederqvist's avatar
Per Cederqvist committed
	    errors++;
	}
	else if (cstat == NULL)
	{
	    log("Person %lu has no conference.\n", (unsigned long)cp);
Per Cederqvist's avatar
Per Cederqvist committed
	    errors++;
	}
	else if (!cstat->type.letter_box)
	{
	    log("Person %lu's conference is not a letter_box.\n",
Per Cederqvist's avatar
Per Cederqvist committed
	    errors++;
	}
	else
	{
	    errors += (check_created_texts(cp, &pstat->created_texts)
		       + check_membership_list(cp, &pstat->conferences));
	}

	if (unset_change_name_is_error == 1
	    && pstat->privileges.change_name == 0)
	{
	    log("Person %lu has no change_name capability.\n",
		(unsigned long)cp);
	    if (rflag || confirm("Grant him the capability"))
	    {
		pstat->privileges.change_name = 1;
		mark_person_as_changed(cp);
		modifications++;
	    }
	    else
		errors++;
	}
Per Cederqvist's avatar
Per Cederqvist committed
    }

    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)
{
Per Cederqvist's avatar
Per Cederqvist committed
    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",
		    (unsigned long)tlist->texts[i],
		    (unsigned long)i + tlist->first_local_no,
		    (unsigned long)cc);
Per Cederqvist's avatar
Per Cederqvist committed

		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",
Per Cederqvist's avatar
Per Cederqvist committed
		}
		else
		    error++;
	    }
	    else
	    {
		if ( !is_recipient(cc, t) )
		{
Per Cederqvist's avatar
Per Cederqvist committed
		    log("Text %lu<%lu> in conference %lu %s.\n",
			(unsigned long)tlist->texts[i],
			(unsigned long)i + tlist->first_local_no,
			(unsigned long)cc,
Per Cederqvist's avatar
Per Cederqvist committed
			"doesn't have the conference as recipient");
Per Cederqvist's avatar
Per Cederqvist committed

		    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",
Per Cederqvist's avatar
Per Cederqvist committed
		    }
		    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",
Per Cederqvist's avatar
Per Cederqvist committed
	if ( rflag || confirm ("Repair by adjusting text_list"))