Skip to content
Snippets Groups Projects
Select Git revision
  • 59a5c5c2080bd0093715c07e7252850856fa4d52
  • master default protected
  • 9.0
  • 8.0
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • nt-tools
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2016
  • v8.0.2014
  • v8.0.2012
  • v8.0.2008
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
  • v8.0.1982
  • v8.0.1980
41 results

_Image_PSD.pmod

Blame
    • Martin Nilsson's avatar
      640db9b0
      image -> Image · 640db9b0
      Martin Nilsson authored
      Rev: lib/modules/GTKSupport.pmod/pDrawingArea.pike:1.8
      Rev: lib/modules/Graphics.pmod/Graph.pmod/testsuite.in:1.4
      Rev: lib/modules/_Image_PSD.pmod:1.24
      640db9b0
      History
      image -> Image
      Martin Nilsson authored
      Rev: lib/modules/GTKSupport.pmod/pDrawingArea.pike:1.8
      Rev: lib/modules/Graphics.pmod/Graph.pmod/testsuite.in:1.4
      Rev: lib/modules/_Image_PSD.pmod:1.24
    simple-cache.c 44.68 KiB
    /*
     * $Id: simple-cache.c,v 0.35 1993/10/13 00:20:14 ceder Exp $
     * Copyright (C) 1991  Lysator Academic Computer Association.
     *
     * 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. 
     */
    /*
     * This module contains some simple simulations of the routines in
     * cache.c.
     *
     * Extracted from ram-cache.c and rewritten by ceder.
     *
     * New database format with texts in their own file by Inge Wallin.
     *
     * New save algorithm by ceder.
     */
    
    static char *rcsid = "$Id: simple-cache.c,v 0.35 1993/10/13 00:20:14 ceder Exp $";
    #include "rcs.h"
    USE(rcsid);
    
    #include <errno.h>
    #include <stdio.h>
    #ifndef SEEK_END
    #  include <fcntl.h>
    #endif
    #ifndef SEEK_END
    #  include <unistd.h>
    #endif
    #ifndef SEEK_END
    #  define SEEK_SET 0
    #  define SEEK_END 2
    #endif
    #include <time.h>
    #ifndef HAVE_DIFFTIME
    #  include <sys/types.h>
    #  include "tmp-difftime.h"
    #endif
    #include <setjmp.h>
    
    #include "exp.h"
    #include "misc-types.h"
    #include "kom-types.h"
    #include "tmp-limits.h"
    #include "cache-node.h"
    #include "cache.h"
    #include "parser.h"
    #include "ram-parse.h"
    #include "ram-output.h"
    #include "server/smalloc.h"
    #include "kom-memory.h"
    #include "lyskomd.h"
    #include "debug.h"
    #include "kom-errno.h"
    #include "log.h"
    #include "com.h"
    #include "connections.h"
    #include "send-async.h"
    
    /*
     * Possible improvements: +++
     *  When there are consecutive items in file A that shall be copied
     *  to file B, copy them in one transfer (up to a certain limit).
     *
     *  In pre_sync: compute size of, and allocate disk space for file B.
     */
    
    /*
     * All functions that can fail sets kom_errno to a suitable value
     * if they fail.
     */
    
    static Small_conf	* small_conf_arr[ MAX_CONF ];
    static Cache_node_mcb	* pers_mcb;
    static Cache_node_mcb 	* conf_mcb;
    static int		  next_free_num = 1;
    
    static Cache_node_mcb 	* text_mcb;
    static int		  next_text_num = 1;
    
    /* Defined in ramkomd.c */
    extern char datafilename[BUFSIZ];
    extern char backupfilename[BUFSIZ];
    extern char textfilename[BUFSIZ];
    
    /*
     * The elements in the following lists with same index refers to the same
     * conference.
     */
    static int    no_of_match_info;
    EXPORT Matching_info *match_table = NULL;
    EXPORT Conf_no       *conf_table  = NULL; /* Used in conference.c. */
    
    
    static FILE	*text_file= NULL;
    static FILE	*file_a = NULL;	/* Current file. */
    static FILE	*file_b = NULL;	/* File under construction. */
    
    /*
     * Four state variables for the background save.
     */
    static enum {
        sync_idle,
        sync_save_conf,
        sync_save_pers,
        sync_save_text,
        sync_error,
        sync_wait,
        sync_ready
    } sync_state;
    
    /* The state machine sync_state works like this:
     *
     *  Old state              action           new state
     *  none               sync_part called     sync_idle
     *                     for the first time
     *  sync_idle	       SYNC_INTERVAL        sync_save_conf
     *  sync_save_conf     all confs saved      sync_save_pers
     *  sync_save_pers     all persons saved    sync_save_text
     *  sync_save_text     all texts saved      sync_ready
     *  sync_ready         sync_part called     sync_idle
     *  any                error occurs         sync_error
     *  sync_error         sync_part called     sync_wait
     *  sync_wait          SYNC_RETRY_INTERVAL  sync_save_conf
     */
    
    static long sync_next;
    
    static Conf_no highest_conf_no;
    static Text_no highest_text_no;
    
    BUGDECL;
    
    /* Define LOGACCESSES if you want to be able to log all accesses to
       the data base. */
    
    #ifdef LOGACCESSES
    extern char *logaccess_file;	/* From ramkomd.c */
    
    typedef enum {
        lt_restart,
        lt_text_stat,
        lt_text_mass,
        lt_conf_stat,
        lt_pers_stat,
        
        lt_text_def,
        lt_conf_def,
        lt_pers_def,
    
        lt_create_text,
        lt_garb_text,
        lt_delete_text,
        lt_create_conf,
        lt_delete_conf,
        lt_create_pers,
        lt_delete_pers,
    
        lt_lock_conf,
        lt_unlock_conf,
        lt_lock_pers,
        lt_unlock_pers,
    
        lt_get_highest,		/* Get highest Local_text_no for a conf. */
        lt_get_conf_type
        /* Note: mark_*_as_changed is not logged. */
    } Log_type;
    
    static FILE *logfile = NULL;
    static int syncing_or_saving = 0;
    static int garb_running = 0;
    
    static void log_access(Log_type t,
    		       int id)
    {
        extern int putw(int, FILE *);
    
        if (garb_running + syncing_or_saving == 0)
        {
    	putc(t, logfile);
    	putw(id, logfile);
        }
    }
    #define LOGACC(a,b) {if (logfile) log_access(a, b);}
    #else
    #define LOGACC(a,b)
    #endif
    
    
    /* Macros */
    
    #define TRACE2(format, arg) if ( buglevel > 2 ) printf(format, arg)
    #define TRACE1(format) if ( buglevel > 2 ) printf(format)
    #define TRACESTR(str)  if ( buglevel > 2 ) s_puts(str)
    
    
    static Person *
    read_person(FILE *fp,
    	    long pos,
    	    long size)
    {
        Person *p;
    
        p = alloc_person();
        fseek(fp, pos+1, SEEK_SET);	/* Skip '+' */
        if ( fparse_person(fp, p) != OK )
        {
    	free_person(p);
    	return NULL;
        }
        else
    	return p;
    }
    
    
    
    static Conference *
    read_conference(FILE *fp,
    		long pos,
    		long size)
    {
        Conference *c;
    
        c = alloc_conference();
        fseek(fp, pos+1, SEEK_SET);	/* Skip '+' */
        if ( fparse_conference(fp, c) != OK )
        {
    	free_conference(c);
    	return NULL;
        }
        else
    	return c;
    }
    	
    
    static Text_stat *
    read_text_stat(FILE *fp,
    	       long pos,
    	       long size)
    {
        Text_stat *t;
    
        t = alloc_text_stat();
        fseek(fp, pos+1, SEEK_SET);	/* Skip '+' */
        if ( fparse_text_stat(fp, t) != OK )
        {
    	free_text_stat(t);
    	return NULL;
        }
        else
    	return t;
    }
    
    static void
    pers_set_mru(Pers_no pers_no)
    {
        set_mru(pers_mcb, pers_no);
    }
    
    static void
    text_set_mru(Text_no text_no)
    {
        set_mru(text_mcb, text_no);
    }
    
    static void
    conf_set_mru(Conf_no conf_no)
    {
        set_mru(conf_mcb, conf_no);
    }
    
    static Cache_node *
    get_pers_node(Pers_no pers_no)
    {
        if ( pers_no >= next_free_num )
    	return NULL;
    
        return get_cache_node(pers_mcb, pers_no);
    }
    
    static void
    unlink_text_lru (Cache_node *node)
    {
        unlink_lru (node, &text_mcb->lru, &text_mcb->mru);
    }
    
    static void
    unlink_conf_lru (Cache_node *node)
    {
        unlink_lru (node, &conf_mcb->lru, &conf_mcb->mru);
    }
    
    static void
    unlink_pers_lru (Cache_node *node)
    {
        unlink_lru (node, &pers_mcb->lru, &pers_mcb->mru);
    }
    
    static Cache_node *
    get_conf_node(Conf_no conf_no)
    {
        if ( conf_no >= next_free_num )
    	return NULL;
    
        return get_cache_node(conf_mcb, conf_no);
    }
    
    static Cache_node *
    get_text_node(Text_no text_no)
    {
        if ( text_no >= next_text_num )
    	return NULL;
    
        return get_cache_node(text_mcb, text_no);
    }
    
    	
    /*
     * Name caching routines
     */
    
    /*
     * change_name changes the cached conference name. It is only called when
     * a conference name is changed or a conference is deleted.
     */
    void
    cached_change_name( Conf_no name_num,
    		    String new_name )
    {
        if ( name_num < 1 || name_num >= next_free_num )
    	restart_kom("cached_change_name(%d, ----): next_free_num==%d",
    		    name_num, next_free_num);
    
        s_clear( &small_conf_arr[name_num]->name );
        s_strcpy( &small_conf_arr[name_num]->name, new_name);
        build_matching_info();
    }
    
    
    extern Conf_type
    cached_get_conf_type (Conf_no conf_no)
    {
        if ( conf_no < 1 || conf_no >= next_free_num )
    	restart_kom("cached_get_conf_type(%d): next_free_num==%d",
    		    conf_no, next_free_num);
    
        if ( small_conf_arr [ conf_no ] == NULL )
    	restart_kom("cached_get_conf_type(%d): conference does not exist.\n",
    		    conf_no);
    
        LOGACC(lt_get_conf_type, conf_no);
    
        return small_conf_arr [ conf_no ]->type;
    }
    
    /*
     * Return number of conferences present.  (Actually, return a number
     * at least as large as the number of conferences present).
     */
    extern Conf_no
    cached_no_of_existing_conferences(void)
    {
        return next_free_num;	/* This is too large, but who cares? */
    }
    
    /*
     * Various function calls to tell the cache that something is changed.
     */
    
    void
    mark_person_as_changed(Pers_no	pers_no)
    {
        Cache_node *node;
    
        node = get_pers_node(pers_no);
        
        TRACE2("Person %d is changed\n", pers_no);
        if ( node == NULL || node->s.exists == 0)
    	restart_kom("mark_person_as_changed(%d): nonexistent.\n", pers_no);
    
        node->s.dirty = 1;
        pers_set_mru( pers_no );
    }
    
    /*
     * Mark the conference as dirty, so that it will be written to
     * the disk.
     *
     * Also update all fields in the Small_conf except then name, so that
     * they are always current.
     *
     * NOTE: You must call cached_change_name when the name changes.
     *       It is not necessary to call cached_change_name after
     *       cached_create_conf.
     */
    
    void
    mark_conference_as_changed(Conf_no	conf_no)
    {
        Cache_node *node;
        Conference *conf_c;
    
        node = get_conf_node (conf_no);
    
        TRACE2("Conf.  %d is changed\n", conf_no);
        if ( node == NULL || node->s.exists == 0)
    	restart_kom("mark_conference_as_changed(%d): nonexistent.\n", conf_no);
    
        node->s.dirty = 1;
        conf_set_mru( conf_no );
    
        conf_c = (Conference *) node->ptr;
    
        small_conf_arr[ conf_no ]->highest_local_no
    	= (conf_c->texts.first_local_no - 1 + conf_c->texts.no_of_texts );
    
        small_conf_arr[ conf_no ]->nice = conf_c->nice;
        small_conf_arr[ conf_no ]->type = conf_c->type;
    }
    
    void
    mark_text_as_changed( Text_no text_no )
    {
        Cache_node *node;
    
        node = get_text_node (text_no);
    
        TRACE2("Text %d is changed.\n", text_no);
        if ( text_no < 1 || text_no >= next_text_num
    	|| node == NULL || node->s.exists == 0)
        {
    	restart_kom("mark_text_as_changed(%d): nonexistent.\n", text_no);
        }
    
        node->s.dirty = 1;
        text_set_mru (text_no);
    }    
    
    
    
    /*
     * Person-related calls
     */
    
    
    extern Success
    cached_create_person( Pers_no person )
    {
        Cache_node *node;
    
        TRACE2("Person %d is being created.\n", person);
    
        if ( person < 1 || person >= next_free_num )
        {
    	restart_kom("cached_create_person(%d): next_free_num == %d.\n",
    		    person, next_free_num);
        }
    
        if ( get_pers_node(person) != NULL )
        {
    	restart_kom("cached_create_person(%d): Person existed.\n",
    		    person);
        }
    
        create_cache_node (pers_mcb, person);
        node = get_pers_node (person);
    
        if ( node == NULL )
    	restart_kom("cached_create_person(): couldn't get cache_node.\n");
    
        node->ptr = alloc_person();
        node->s.dirty = 1;
        node->s.exists = 1;
        pers_set_mru( person );
    
        LOGACC(lt_create_pers, person);
    
        return OK;
    }
    
    
    extern Person *
    cached_get_person_stat( Pers_no	person )
    {
        Cache_node *node;
    
        TRACE2("cached_get_person_stat %d\n", person);
    
        if ( person == 0 )
        {
    	kom_errno = KOM_CONF_ZERO;
    	return NULL;
        }
    
        if ( person >= next_free_num )
        {
    	kom_errno = KOM_UNDEF_PERS;
    	return NULL;
        }
    
        node = get_pers_node (person);
    
        if ( node == NULL || node->s.exists == 0 )
        {
    	kom_errno = KOM_UNDEF_PERS;
    	return NULL;
        }
    
        LOGACC(lt_pers_stat, person);
    
        if ( node->ptr != NULL )
        {
    	pers_set_mru( person );
    	++pers_mcb->hits;
    	return node->ptr;
        }
    
        if ( node->snap_shot != NULL )
        {
    	node->ptr = copy_person (node->snap_shot);
    	pers_set_mru (person);
    	++pers_mcb->hits;
    	return node->ptr;
        }
    
        node->ptr = read_person(file_a, node->pos, node->size);
    
        ++pers_mcb->misses;
        pers_set_mru (person);
        return node->ptr;
    }
    
    
    /*
     * Conference-related calls
     */
    
    
    static int no_of_allocated_small_confs = 0;
    
    static void
    free_small_conf (Small_conf *sc)
    {
        if ( sc != NULL )
        {
    	--no_of_allocated_small_confs;
    	s_clear ( &sc->name );
    	sfree (sc);
        }
    }
    
    
    static Small_conf *
    alloc_small_conf(void)
    {
        Small_conf *s;
        s = smalloc(sizeof(Small_conf));
        *s = EMPTY_SMALL_CONF;
        ++no_of_allocated_small_confs;
    
        return s;
    }
    
    
    /*
     * Create a conference.
     *
     * Set up a Conference and cache the name in the small_conf_array.
     */
    extern Conf_no
    cached_create_conf (String  name)
    {
        Conference * conf_c;
        Conf_no	 conf_no;
        Cache_node  *node;
    
        TRACE1("cached_create_conf( ");
        TRACESTR(name);
        TRACE1(" )\n");
    
        if ( next_free_num >= MAX_CONF )
        {
    	kom_errno = KOM_INDEX_OUT_OF_RANGE;
    	return 0;
        }
        
        conf_no = next_free_num++;
    
        create_cache_node (conf_mcb, conf_no);
        node = get_conf_node (conf_no);
    
        if ( node == NULL )
    	restart_kom("cached_create_conf(): failed to allocate cache_node.\n");
        
        node->s.exists = 1;
        node->s.dirty = 1;
        node->ptr = conf_c = alloc_conference();
        conf_set_mru(conf_no);    
    
        zero_init_cache_node (pers_mcb, conf_no);
        small_conf_arr[ conf_no ] = alloc_small_conf();
    
        conf_c->name = EMPTY_STRING;
        s_strcpy(&conf_c->name, name);
        cached_change_name( conf_no, name);
    
        TRACE2("Created conference number %d\n", conf_no);
    
        LOGACC(lt_create_conf, conf_no);
    
        return conf_no;
    }
    
    
    extern Success
    cached_delete_conf( Conf_no	conf )
    {
        Cache_node *node;
    
        if ( conf == 0 )
        {
    	kom_errno = KOM_CONF_ZERO;
    	return FAILURE;
        }
    
        if ( conf >= next_free_num )
        {
    	kom_errno = KOM_UNDEF_CONF;
    	return FAILURE;
        }
    
        node = get_conf_node (conf);
    
        if ( node == NULL || node->s.exists == 0 )
        {
    	kom_errno = KOM_UNDEF_CONF;
    	return FAILURE;
        }
    
        if ( node->lock_cnt > 0 )
    	log("WNG: cached_delete_conf(%d): lock_cnt === %d\n",
    	    conf, node->lock_cnt);
    
        free_conference(node->ptr);
        node->ptr = NULL;
        node->s.exists = 0;
    
        LOGACC(lt_delete_conf, conf);
    
        return OK;
    }
    
    Success
    cached_delete_person(Pers_no pers)
    {
        Cache_node *node;
    
        if ( pers == 0 )
        {
    	kom_errno = KOM_CONF_ZERO;
    	return FAILURE;
        }
    
        if ( pers >= next_free_num )
        {
    	log("cached_delete_person(%lu): next_free_num == %lu\n",
    	    (u_long)pers, (u_long)next_free_num);
    	kom_errno = KOM_UNDEF_PERS;
    	return FAILURE;
        }
    
        node = get_pers_node (pers);
    
        if ( pers >= next_free_num || node == NULL || node->s.exists == 0 )
        {
    	log("cached_delete_person(): attempt to"
    	    " delete non-existing person.\n");
    	kom_errno = KOM_UNDEF_PERS;
    	return FAILURE;
        }
    
        if ( node->lock_cnt > 0 )
    	log("cached_delete_pers(%lu): lock_cnt === %lu\n",
    	    (u_long)pers, (u_long)node->lock_cnt);
    
        LOGACC(lt_delete_pers, pers);
    
        free_person (node->ptr);
        node->ptr = NULL;
        node->s.exists = 0;
        return OK;
    }
    
    Success
    cached_delete_text(Text_no text)
    {
        Cache_node *node;
    
        if ( text == 0 )
        {
    	kom_errno = KOM_TEXT_ZERO;
    	return FAILURE;
        }
    
        node = get_text_node (text);
    
        if ( text >= next_text_num || node == NULL
    	|| node->s.exists == 0 )
        {
    	log("cached_delete_text(): attempt to"
    	    " delete non-existing text %d.\n", text);
    	kom_errno = KOM_NO_SUCH_TEXT;
    	return FAILURE;
        }
    
        if ( node->lock_cnt > 0 )
    	log("cached_delete_text(%d): lock_cnt === %d\n",
    	    text, node->lock_cnt);
    
    
        free_text_stat(node->ptr);
        node->ptr = NULL;
        node->s.exists = 0;
    
    #ifdef LOGACCESSES
        if (garb_running)
        {
    	LOGACC(lt_garb_text, text);
        }
        else
    	LOGACC(lt_delete_text, text);
    #endif
        return OK;
    }
    
    
    extern Conference *
    cached_get_conf_stat (Conf_no   conf_no)
    {
        Cache_node *node;
    
        TRACE2("cached_get_conf_stat %d\n", conf_no);
    
        if ( conf_no == 0 )
        {
    	kom_errno = KOM_CONF_ZERO;
    	return NULL;
        }
    
        node = get_conf_node (conf_no);
    
        if ( conf_no >= next_free_num || node == NULL || node->s.exists == 0 )
        {
    	kom_errno = KOM_UNDEF_CONF;
    	return NULL;
        }
    
        LOGACC(lt_conf_stat, conf_no);
    
        if ( node->ptr != NULL )
        {
    	conf_set_mru (conf_no);
    	++conf_mcb->hits;
    	return node->ptr;
        }
    
        if ( node->snap_shot != NULL )
        {
    	node->ptr = copy_conf (node->snap_shot);
    	conf_set_mru (conf_no);
    	++conf_mcb->hits;
    	return node->ptr;
        }
    
        node->ptr = read_conference(file_a, node->pos, node->size);
        ++conf_mcb->misses;
    
        conf_set_mru (conf_no);
        return node->ptr;
    }
    
    /*
     * Return TRUE if conf_no exists.
     */
    Bool
    cached_conf_exists(Conf_no conf_no)
    {
        Cache_node *node;
    
        if (conf_no == 0 || conf_no >= next_free_num )
    	return FALSE;
    
        node = get_conf_node (conf_no);
    
        return node != NULL && node->s.exists != 0;
    }
    
        
    /*
     * Calls to handle texts
     */
    
    /*
     * +++ Should return Success.
     */
    extern String
    cached_get_text( Text_no text )
    {
        String  the_string;
        Text_stat *t_stat;
    
        TRACE2("cached_get_text %d\n", text);
    
        if ( (t_stat = cached_get_text_stat (text)) == NULL )
    	return EMPTY_STRING;
        else
        {
    	LOGACC(lt_text_mass, text);
    	the_string.string = tmp_alloc( t_stat->no_of_chars );
    	the_string.len = t_stat->no_of_chars;
    	fseek(text_file, t_stat->file_pos, SEEK_SET);
    
    	if ( fread(the_string.string, sizeof(char), the_string.len, text_file)
    	    != the_string.len )
    	{
    	    log("WARNING: cached_get_text: "
    		"couldn't read enough characters of text %d\n",
    		text);
    	    return EMPTY_STRING;
    	}
    		    
    	return the_string;
        }
    }
    
    
    extern Text_stat *	/* NULL on error */
    cached_get_text_stat(	Text_no		text )
    {
        Cache_node *node;
    
        TRACE2("cached_get_text_stat(%d);  next_text_num == ", text);
        TRACE2("%d\n", next_text_num);
    
        if ( text == 0 )
        {
    	kom_errno = KOM_TEXT_ZERO;
    	return NULL;
        }
    
        node = get_text_node (text);
    
        if ( text >= next_text_num || node == NULL || node->s.exists == 0 )
        {
    	TRACE1("cached_get_text_stat: no such text.\n");
    	kom_errno = KOM_NO_SUCH_TEXT;
    	return NULL;
        }
    
        LOGACC(lt_text_stat, text);
    
        if ( node->ptr != NULL )
        {
    	TRACE1("Found in ptr.\n");
    	text_set_mru( text );
    	++text_mcb->hits;
    	return node->ptr;
        }
    
        if ( node->snap_shot != NULL )
        {
    	TRACE1("Found in snap_shot\n");
    	node->ptr = copy_text_stat(node->snap_shot);
    	text_set_mru (text);
    	++text_mcb->hits;
    	return node->ptr;
        }
    
        TRACE1("Found in file A.\n");
        node->ptr = read_text_stat(file_a, node->pos, node->size);
    
        text_set_mru (text);
        ++text_mcb->misses;
        return node->ptr;
    }
    
    
    
    /*
     * The text is set up with an empty misc-field. The misc field is
     * then initialized by create_text.
     */
    
    extern Text_no
    cached_create_text(const String message)
    {
        Text_no tno;
        Cache_node *node;
    
        tno = next_text_num++;
    
        TRACE2("cached_create_text (len=%d)\n", message.len);
    
        if ( tno >= MAX_TEXT )
        {
    	kom_errno = KOM_INDEX_OUT_OF_RANGE;
    	next_text_num = MAX_TEXT;
    	
    	return 0;
        }
    
        create_cache_node(text_mcb, tno);
        node = get_text_node (tno);
    
        if ( node == NULL )
    	restart_kom("cached_create_text(): couldn't create cache-node.\n");
        
        node->s.exists = 1;
        node->s.dirty = 1;
        node->ptr = alloc_text_stat();
        ((Text_stat *)node->ptr)->no_of_misc = 0;
        ((Text_stat *)node->ptr)->misc_items = NULL;
        ((Text_stat *)node->ptr)->no_of_marks = 0;
        ((Text_stat *)node->ptr)->no_of_lines = 0;
        ((Text_stat *)node->ptr)->no_of_chars = 0;
        fseek(text_file, 0, SEEK_END);
        ((Text_stat *)node->ptr)->file_pos = ftell(text_file);
    
        text_set_mru( tno );
    
        if ( fwrite(message.string, sizeof(char), message.len, text_file)
    	!= message.len ) {
    	log("WARNING: cached_create_text: Couldn't write the text %d\n",
    	    tno);
        }
    
        fflush(text_file);
        
        LOGACC(lt_create_text, tno);
        TRACE2("cached_create_text -> %d\n", tno);
        
        return tno;
    }
    
    
    EXPORT Text_no
    traverse_text(Text_no seed)
    {
        Cache_node *node;
    
        seed++;
        
        while ( seed < next_text_num )
        {
    	node = get_text_node (seed);
    	if ( node != NULL && node->s.exists != 0 )
    	    break;
    
    	seed++;
        }
        
        return (seed >= next_text_num) ? 0 : seed ;
    }
    
    Pers_no
    traverse_person(Pers_no seed)
    {
        Cache_node *node;
    
        seed++;
    
        while ( seed < next_free_num )
        {
    	node = get_pers_node (seed);
    	if (node != NULL && node->s.exists != 0 )
    	    break;
    
    	seed++;
        }
    
        return (seed >= next_free_num) ? 0 : seed ;
    }
    
    Conf_no
    traverse_conference(Conf_no seed)
    {
        Cache_node *node;
    
        seed++;
        
        while ( seed < next_free_num )
        {
    	node = get_conf_node (seed);
    	if (node != NULL && node->s.exists != 0 )
    	    break;
    
    	seed++;
        }
    
        return (seed >= next_free_num) ? 0 : seed ;
    }
    
    extern Garb_nice
    cached_get_garb_nice (Conf_no conf_no)
    {
        return small_conf_arr [ conf_no ]->nice;
    }
    
    extern String
    cached_get_name (Conf_no conf_no)
    {
        return small_conf_arr [ conf_no ]->name;
    }
    
    extern Local_text_no
    cached_get_highest_local_no (Conf_no conf_no)
    {
        LOGACC(lt_get_highest, conf_no);
    
        return small_conf_arr[ conf_no ]->highest_local_no;
    }
    
    /* Lock a person struct in memory. Increase a referenc count. */
    void
    cached_lock_person(Pers_no pers_no)
    {
        Cache_node *node;
    
        LOGACC(lt_lock_pers, pers_no);
    
        node = get_pers_node(pers_no);
        
        if ( node == NULL || node->s.exists == 0 )
    	restart_kom("cached_lock_person(%d): nonexistent.\n", pers_no);
    
        if ( node->ptr == NULL )
        {
    	Person *pers_stat_ptr;
    
    	pers_stat_ptr = cached_get_person_stat( pers_no );
    
    	if ( pers_stat_ptr == NULL )
    	    restart_kom("cached_lock_person(%d): couldn't read in person.\n",
    			pers_no);
    
    	if ( pers_stat_ptr != node->ptr )
    	    restart_kom("cached_lock_person(%d): pers_stat_ptr == %d, node"
    			"->ptr == %d.\n", pers_no, pers_stat_ptr,
    			node->ptr);
        }
    
        node->lock_cnt++;
    }
    
    /* Decrease reference count. If zero, unlock person. */
    void
    cached_unlock_person(Pers_no pers_no)
    {
        Cache_node *node;
    
        LOGACC(lt_unlock_pers, pers_no);
    
        node = get_pers_node (pers_no);
    
        if ( node == NULL )
    	restart_kom("cached_unlock_person(): couldn't get cache-node.\n");
    
        if ( node->lock_cnt <= 0 )
        {
    	log("cached_unlock_person(%d): lock_cnt == %d.\n",
    	    pers_no, node->lock_cnt);
    
    	node->lock_cnt = 0;
        }
        else
    	node->lock_cnt--;
    }
    
    
    /* Lock a conf struct in memory. Increase a reference count. */
    void
    cached_lock_conf(Conf_no conf_no)
    {
        Cache_node *node;
    
        node = get_conf_node(conf_no);
    
        if ( node == NULL)
    	restart_kom("cached_lock_conf(): can't get cache-node.\n");
    
        if ( node->s.exists == 0 )
    	restart_kom("cached_lock_conf(%d): nonexistent.\n", conf_no);
    
        LOGACC(lt_lock_conf, conf_no);
    
        if ( node->ptr == NULL )
        {
    	Conference *conference_ptr;
    
    	conference_ptr = cached_get_conf_stat( conf_no );
    
    	if ( conference_ptr == NULL )
    	    restart_kom("cached_lock_conf(%d): couldn't read in conf.\n",
    			conf_no);
    
    	if ( conference_ptr != node->ptr )
    	    restart_kom("cached_lock_conf(%d): conference_ptr == %d, node"
    			"->ptr == %d.\n", conf_no, conference_ptr,
    			node->ptr);
        }
    
        node->lock_cnt++;
    }
    
    /* Decrease reference count. If zero, unlock conf. */
    void
    cached_unlock_conf(Conf_no conf_no)
    {
        Cache_node *node;
    
        LOGACC(lt_unlock_conf, conf_no);
    
        node = get_conf_node(conf_no);
        if ( node == NULL )
    	restart_kom("cached_unlock_conf(): can't get node.\n");
    
        if ( node->lock_cnt <= 0 )
        {
    	log("cached_unlock_conf(%d): lock_cnt == %d.\n",
    	    conf_no, node->lock_cnt);
    
    	node->lock_cnt = 0;
        }
        else
    	node->lock_cnt--;
    }
    
    
    
    /*
     * And here comes some functions to deal with lookup_names.
     */
    
    /* Free the _contents_ of a match_table. The table itself i _not_ freed. */
    
    static void
    free_match_table(Matching_info *match)
    {
        if ( match == NULL )
    	return;
        
        while ( ! s_empty( match->name ))
        {
    	free_tokens( match->tokens );
    	/* match->name is not freed since it points into conf_arr[]. */
    
    	++match;
        }
    }    
    
    extern Success
    build_matching_info(void)
    {
        Conf_no i;
        Matching_info *match;
        Conf_no	  *conf;
    
        free_match_table(match_table);
        
        match_table = srealloc(match_table, next_free_num * sizeof(Matching_info));
        conf_table = srealloc(conf_table, next_free_num * sizeof(Conf_no));
    
        no_of_match_info = 0;
    
        match = match_table;
        conf = conf_table;
        
        for ( i = 1; i < next_free_num; i++ )
        {
    	if ( small_conf_arr[ i ] != NULL
    	    && ! s_empty ( small_conf_arr[ i ]->name ) )
    	{
    	    match->name = small_conf_arr[ i ]->name;
    	    match->tokens = tokenize(match->name, s_fcrea_str(WHITESPACE));
    	    match->priority = 7;
    	    *conf = i;
    	    ++match;
    	    ++conf;
    	    ++no_of_match_info;
    	}
        }
    
        match->name = EMPTY_STRING;
        match->tokens = NULL;
        match->priority = 0;
        *conf = 0;
        
        return OK;
    }
    
    
    
    /* Map conference name to number */
    extern Success
    cached_lookup_name(const String name,
    		   Conf_list_old *result)
    {
        Parse_info tmp;
        int i;
        
        tmp = parse(name, match_table, FALSE, FALSE,
    		s_fcrea_str(WHITESPACE), DEFAULT_COLLAT_TAB);
    
        if ( tmp.no_of_matches == -1 )
    	return FAILURE;
        
        if ( tmp.no_of_matches == 1 && tmp.indexes[ 0 ] == -1 )
        {
    	/* Return the entire list. */
    	result->no_of_conf_nos = no_of_match_info;
    	result->conf_nos = tmp_alloc(no_of_match_info * sizeof(Conf_no));
    	result->type_of_conf = tmp_alloc(no_of_match_info * sizeof(Conf_type));
    
    	for ( i = 0; i < no_of_match_info; i++ )
    	{
    	    result->conf_nos[ i ] = conf_table[ i ];
    	    result->type_of_conf[ i ] = small_conf_arr[ conf_table[i] ]->type;
    	}
        }
        else
        {
    	/* Return the conferences whose conf_nos are in indexes[]. */
    	result->no_of_conf_nos = tmp.no_of_matches;
    	result->conf_nos = tmp_alloc(tmp.no_of_matches * sizeof(Conf_no));
    	result->type_of_conf = tmp_alloc(tmp.no_of_matches
    					 * sizeof(Conf_type));
    
    	for ( i = 0; i < tmp.no_of_matches; i++ )
    	{
    	    result->conf_nos[ i ] = conf_table[ tmp.indexes[ i ] ];
    	    result->type_of_conf[ i ]
    		= small_conf_arr[ conf_table[ tmp.indexes[ i ] ] ]->type;
    	}
        }		
        sfree(tmp.indexes);
        return OK;
    }
    
    
    static Bool
    is_clean(const char *fn)
    {
        FILE *fp;
    
        if ( (fp = fopen(fn, "rb")) == NULL )
    	return FALSE;
    
        if ( getc(fp) == 'C' && getc(fp) == 'L' && getc(fp) == 'E'
    	&& getc(fp) == 'A' && getc(fp) == 'N' )
        {
    	fclose(fp);
    	return TRUE;
        }
        else
        {
    	fclose(fp);
    	return FALSE;
        }
    }
    
    
    static void
    pre_sync(void)
    {
        long i;
        Cache_node *node;
    
        async_sync_db();
    
        /* Mark up what to save.*/
    
        BUG(("Sync starting\n"));
    
        highest_text_no = next_text_num;
        highest_conf_no = next_free_num;
    
        for ( i = 1; i < highest_conf_no; i++ )
        {
    	node = get_conf_node(i);
    
    	if ( node != NULL )
    	{
    	    node->s.exists_b = node->s.exists;
    
    	    if ( node->s.dirty != 0 && node->s.exists != 0)
    	    {
    		free_conference(node->snap_shot);
    
    		if ( node->lock_cnt == 0 )
    		{
    		    unlink_conf_lru(node);
    		    node->snap_shot = node->ptr;
    		    node->ptr = NULL;
    		}
    		else
    		{
    		    node->snap_shot = copy_conf(node->ptr);
    		}
    		
    		node->s.dirty = 0;
    	    }
    	}
        }
    
        for ( i = 1; i < highest_conf_no; i++ )
        {
    	node = get_pers_node(i);
    
    	if ( node != NULL )
    	{
    	    node->s.exists_b = node->s.exists;
    
    	    if ( node->s.dirty != 0 && node->s.exists != 0)
    	    {
    		free_person(node->snap_shot);
    
    		if ( node->lock_cnt == 0 )
    		{
    		    unlink_pers_lru(node);
    		    node->snap_shot = node->ptr;
    		    node->ptr = NULL;
    		}
    		else
    		{
    		    node->snap_shot
    			= copy_person(node->ptr);
    		}
    		
    		node->s.dirty = 0;
    	    }
    	}
        }
    
        for ( i = 1; i < highest_text_no; i++ )
        {
    	node = get_text_node(i);
    
    	if ( node != NULL )
    	{
    	    node->s.exists_b = node->s.exists;
    
    	    if ( node->s.dirty != 0 && node->s.exists != 0)
    	    {
    		free_text_stat(node->snap_shot);
    
    		if ( node->lock_cnt == 0 )
    		{
    		    unlink_text_lru(node);
    		    node->snap_shot = node->ptr;
    		    node->ptr = NULL;
    		}
    		else
    		{
    		    node->snap_shot
    			= copy_text_stat(node->ptr);
    		}
    		
    		node->s.dirty = 0;
    	    }
    	}
        }
    
        /* All marking is done. Now open file B. */
        
        if ( is_clean(datafilename) )
        {
    	if ( rename(datafilename, backupfilename) != 0 )
    	    restart_kom("pre_sync: can't backup.\n");
        }
        else
    	log("pre_sync: datafile not clean. No backup taken.\n");
    
        if ( file_b != NULL )
        {
    	log("pre_sync: Save in progress aborted.\n");
    	fclose(file_b);
        }
    
        if ( (file_b=fopen(datafilename, "wb") ) == NULL )
        {
    	log("WARNING: pre_sync: can't open file to save in.\n");
    	sync_state = sync_wait;
    
    	return;
        }
    
        fprintf(file_b, "DIRTY\n");		 /* DIRTY-FLAG */
    
        fprintf(file_b, "%d\n", highest_conf_no);	  /* NEXT_FREE_NUM */
        sync_state = sync_save_conf;
        sync_next = 1;
    }
    
    static void
    copy_file(FILE *from,
    	  FILE *to,
    	  long from_pos,
    	  long len)
    {
        char *buf;
        long result;
    
        buf = smalloc(len);
        if ( fseek(from, from_pos, SEEK_SET) == -1 )
        {
    	sync_state = sync_error;
    	log("sync: copy_file(): fseek failed.\n");
    	sfree(buf);
    	return;
        }
        
        if ( (result = fread(buf, 1, len, from)) != len )
        {
    	log("sync: copy_file(): fread failed.\n"
    	    "from_pos = %d, len = %d, result = %d\n",
    	    from_pos, len, result);
    
    	sync_state = sync_error;
    	sfree(buf);
    	return;
        }
    
        if ( fseek(to, 0, SEEK_END) == -1 )
        {
    	sync_state = sync_error;
    	log("sync: copy_file(): second fseek failed.\n");
    	sfree(buf);
    	return;
        }
    
        if ( fwrite(buf, 1, len, to) != len )
        {
    	sync_state = sync_error;
    	log("sync: copy_file(): fwrite failed.\n");
    	sfree(buf);
    	return;
        }
    
        sfree(buf);
    }    
    
    static void
    save_one_conf(void)
    {
        Cache_node *cn;
        
        if (sync_next < highest_conf_no)
        {
    	cn = get_conf_node (sync_next);
    
    	if ( cn == NULL )
    	{
    	    putc('@', file_b);
    	    putc('\n', file_b);
    	}
    	else 
    	{
    	    cn->pos_b = ftell(file_b);
    
    	    if ( cn->s.exists_b == 0 )
    		putc('@', file_b);
    	    else if ( cn->snap_shot != NULL )
    	    {
    		fprintf(file_b, "+ ");
    		foutput_conference(file_b, cn->snap_shot);
    	    }
    	    else if ( cn->s.dirty == 0 && cn->ptr != NULL )
    	    {
    		fprintf(file_b, "+ ");
    		foutput_conference(file_b, cn->ptr);
    	    }
    	    else
    	    {
    		copy_file(file_a, file_b, cn->pos, cn->size - 1);
    	    }
    
    	    putc('\n', file_b);
    	    cn->size_b = ftell(file_b) - cn->pos_b;
    	}
    	sync_next++;
        }
        else			/* All conferences are written. */
        {
    	sync_next = 1;
    	sync_state = sync_save_pers;
        }
    }
    
    static void
    write_pers(FILE *fp,
    	   Person *p)
    {
        fprintf(fp, "+ %dH", PASSWD_LEN);
        fwrite(p->pwd, PASSWD_LEN, 1, fp);
        foutput_person(fp, p);
    }
    
    static void
    save_one_pers(void)
    {
        Cache_node *cn;
        
        if (sync_next < highest_conf_no)
        {
    	cn = get_pers_node (sync_next);
    
    	if ( cn == NULL )
    	{
    	    putc('@', file_b);
    	    putc('\n', file_b);
    	}
    	else 
    	{
    	    cn->pos_b = ftell(file_b);
    
    	    if ( cn->s.exists_b == 0 )
    		putc('@', file_b);
    	    else if ( cn->snap_shot != NULL )
    	    {
    		write_pers(file_b, cn->snap_shot);
    	    }
    	    else if ( cn->s.dirty == 0 && cn->ptr != NULL )
    	    {
    		write_pers(file_b, cn->ptr);
    	    }
    	    else
    	    {
    		copy_file(file_a, file_b, cn->pos, cn->size - 1);
    	    }
    
    	    putc('\n', file_b);
    	    cn->size_b = ftell(file_b) - cn->pos_b;
    	}
    	sync_next++;
        }
        else			/* All persons are written. */
        {
    	fprintf(file_b, "%d\n", highest_text_no);	/* NEXT_TEXT_NUM */
    	sync_next = 1;
    	sync_state = sync_save_text;
        }
    }
    
    static void
    post_sync(void)
    {
        int i;
        Cache_node *node;
    
        async_sync_db();
    
        if ( file_a == NULL )
    	log("WARNING: post_sync(): file_a == NULL. This is only normal "
    	    "if this is the first sync ever on this data file.\n");
        else
    	fclose(file_a);
    
        if ( ( file_a = fopen(datafilename, "rb") ) == NULL )
        {
    	log("post_sync: can't open the file I just saved.\n");
    	sync_state = sync_wait;
    	return;
        }
    
        for ( i = 1; i < highest_conf_no; i++ )
        {
    	node = get_conf_node(i);
    	if ( node != NULL )
    	{
    	    node->pos = node->pos_b;
    	    node->size = node->size_b;
    	    free_conference(node->snap_shot);
    	    node->snap_shot = NULL;
    	    /* +++ Delete if it no longer exists. */
    	}
        }
    
        for ( i = 1; i < highest_conf_no; i++ )
        {
    	node = get_pers_node(i);
    	if ( node != NULL )
    	{
    	    node->pos = node->pos_b;
    	    node->size = node->size_b;
    	    free_person(node->snap_shot);
    	    node->snap_shot = NULL;
    	    /* +++ Delete if it no longer exists. */
    	}
        }
    
        for ( i = 1; i < highest_text_no; i++ )
        {
    	node = get_text_node(i);
    	if ( node != NULL )
    	{
    	    node->pos = node->pos_b;
    	    node->size = node->size_b;
    	    free_text_stat(node->snap_shot);
    	    node->snap_shot = NULL;
    	    /* +++ Delete if it no longer exists. */
    	}
        }
    }
    
        
    
    static void
    save_one_text(void)
    {
        Cache_node *cn;
        long offset;
        long offset2;
        
        while (sync_next < highest_text_no)
        {
    	cn = get_text_node(sync_next);
    
    	if ( cn == NULL )
    	{
    	    putc('@', file_b);
    	    putc('\n', file_b);
    
    	    sync_next++;
    	    continue;
    	}
    	else 
    	{
    	    cn->pos_b = ftell(file_b);
    
    	    if ( cn->s.exists_b == 0 )
    		putc('@', file_b);
    	    else if ( cn->snap_shot != NULL )
    	    {
    		fprintf(file_b, "+ ");
    		foutput_text_stat(file_b, cn->snap_shot);
    	    }
    	    else if ( cn->s.dirty == 0 && cn->ptr != NULL )
    	    {
    		fprintf(file_b, "+ ");
    		foutput_text_stat(file_b, cn->ptr);
    	    }
    	    else
    	    {
    		copy_file(file_a, file_b, cn->pos, cn->size - 1);
    	    }
    
    	    putc('\n', file_b);
    	    cn->size_b = ftell(file_b) - cn->pos_b;
    
    	    sync_next++;
    	    break;
    	}
    	restart_kom("Unreachable statement reached.");
        }
    
        /* If all texts are written, do some clean-up. */
        if (sync_next == highest_text_no)
        {
    	if ( ferror(file_b) != 0 )
    	{
    	    log ("save_one_text(): ferror() detected.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    
    	offset = ftell(file_b);	/* Make sure that the entire file resides
    				   on disk. This test seems to be necessary.
    				   The data file has been corrupted at least
    				   once. */
    
    	if (offset == -1)
    	{
    	    log ("save_one_text(): ftell returned -1.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    	
    	rewind(file_b);
    	if ( ferror(file_b)  != 0 )
    	{
    	    log ("save_one_text(): rewind failed.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    	
    	fprintf(file_b, "CLEAN");
    
    	if ( ferror(file_b)  != 0 )
    	{
    	    log ("save_one_text(): fprintf(CLEAN) failed.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    	
    	if (fflush(file_b) != 0)
    	{
    	    log ("save_one_text(): fflush failed.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    	
    	if ( ferror(file_b)  != 0 )
    	{
    	    log ("save_one_text(): ferror after fflush failed.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    	
    	if ( fclose(file_b) != 0 )
    	{
    	    file_b = NULL;
    
    	    log("Sync: fclose() failed in save_one_text. Deleting "
    		"file and retrying.\n");
    	    remove(datafilename);
    	    sync_state = sync_wait;
    	    return;
    	}
    
    	file_b = fopen(datafilename, "rb");
    	if (file_b == NULL)
    	{
    	    log("save_one_text(): failed to reopen file.\n");
    	    remove (datafilename);
    	    sync_state = sync_wait;
    	    return;
    	}
    
    	if (fseek(file_b, 0, SEEK_END) != 0)
    	{
    	    log("save_one_text(): fseek failed.\n");
    	    sync_state = sync_error;
    	    return;
    	}
    
    	offset2 = ftell (file_b);
    	if ( offset2 != offset )
    	{
    	    log ("save_one_text(): ftell returns different offsets"
    		 "(%l and %l).\n", offset, offset2);
    	    sync_state = sync_error;
    	    return;
    	}
    
    	fclose (file_b);
    	file_b = NULL;
    
    	sync_state = sync_ready;
    
    	BUG(("Sync ready\n"));
    	post_sync();
        }
    }
    
    /*
     * Sync_part() should be called often as long as it returns FALSE.
     * Sync_part() returns TRUE when everything is written to a file.
     * Sync_part() should be called once in a while even when it returns
     * TRUE (because sync() checks to see if it is time to start yet
     * another save.)
     */
    Bool
    sync_part(void)
    {
        static time_t last_sync_start = NO_TIME;
    
        if ( last_sync_start == NO_TIME )
        {
    	last_sync_start = time(NULL);
    	sync_state = sync_idle;
        }
    
    #ifdef LOGACCESSES
        syncing_or_saving = 1;
    #endif
    
        switch(sync_state)
        {
        case sync_save_conf:
    	save_one_conf();
    	break;
    
        case sync_save_pers:
    	save_one_pers();
    	break;
    
        case sync_save_text:
    	save_one_text();
    	break;
    
        case sync_ready:
    	sync_state = sync_idle;
    	return TRUE;
    
        case sync_idle:
    	if ( difftime(time(NULL), last_sync_start) < 60 * SYNC_INTERVAL )
    	{
    #ifdef LOGACCESSES
    	    syncing_or_saving = 0;
    #endif
    	    return TRUE;
    	}
    
    	last_sync_start = time(NULL);
        
    	pre_sync();
    	break;
    
        case sync_wait:
    	if ( difftime(time(NULL), last_sync_start) < 60 * SYNC_RETRY_INTERVAL )
    	{
    #ifdef LOGACCESSES
    	    syncing_or_saving = 0;
    #endif
    	    return TRUE;
    	}
    
    	last_sync_start = time(NULL);
    
    	pre_sync();
    	break;
    
        case sync_error:
    	log("sync: Error saving new file. Discarding file and "
    	    "trying again.\n");
    	fclose(file_b);
    	file_b = NULL;
    	remove(datafilename);
    	sync_state = sync_wait;
    	break;
    
        default:
    	restart_kom("sync(): sync_state==%d", sync_state);
        }
    
        if ( file_b != NULL && ferror(file_b) != 0)
    	sync_state = sync_error;
    
    #ifdef LOGACCESSES
        syncing_or_saving = 0;
    #endif
    	
        return FALSE;
    }
    
    
    static void
    setup_small_conf(Conf_no conf_no,
    		 Conference *conf_c)
    {
        small_conf_arr[ conf_no ] = alloc_small_conf();
        s_strcpy(&small_conf_arr[ conf_no ]->name, conf_c->name);
        small_conf_arr[ conf_no ]->type = conf_c->type;
        small_conf_arr[ conf_no ]->highest_local_no
    	= (conf_c->texts.first_local_no - 1
    	   + conf_c->texts.no_of_texts );
        small_conf_arr[ conf_no ]->nice = conf_c->nice;
    }
    
    
        
    extern Success
    init_cache(void)
    {
        int i;
        Cache_node *node;
        Conference tmp_conf = EMPTY_CONFERENCE;
        Person tmp_pers = EMPTY_PERSON;
        Text_stat tmp_text = EMPTY_TEXT_STAT;
    
        pers_mcb = create_cache_node_mcb(100, MAX_CONF);
        conf_mcb = create_cache_node_mcb(100, MAX_CONF);
        text_mcb = create_cache_node_mcb(100, MAX_TEXT);
    
    #ifdef LOGACCESSES
        if (logaccess_file)
        {
    	logfile = fopen(logaccess_file, "a");
    	if (logfile)
    	    log("Logging db accesses to %s.\n", logaccess_file);
    	else
    	    log("Failed to open db log file %s. Ignoring -L option.\n",
    		logaccess_file);
        }
    #endif
        LOGACC(lt_restart, time(NULL));
    
        if ( (text_file = fopen(textfilename, "a+b")) == NULL )
        {
    	restart_kom("ERROR: init_cache: can't open text file \"%s\"."
    		    " errno = %d\n",
    		    textfilename, errno);
        }
    
        if ( is_clean(datafilename) )
        {
    	if ( (file_a = fopen(datafilename, "rb")) == NULL )
    	{
    	    log("WARNING: init_cache: can't open datafile.\n");
    	    return FAILURE;
    	}
    	log("MSG: init_cache: using datafile.\n");
        }
        else if ( is_clean(backupfilename) )
        {
    	if ( (file_a = fopen(backupfilename, "rb")) == NULL )
    	{
    	    log("WARNING: init_cache: can't open backupfile.\n");
    	    return FAILURE;
    	}
    	log("MSG: init_cache: using backup file.\n");
        }
        else
        {
    	log("WARNING: init_cache: can't find old data base.\n");
    	return FAILURE;
        }
            
        fseek(file_a, 6, SEEK_SET);	/* skip clean/dirty flag. */
    
        next_free_num = fparse_long(file_a);	  /* NEXT_FREE_NUM */
    
        for ( i = 1; i < next_free_num; i++ ) /* CONFS */
        {
    	fskipwhite(file_a);
    	switch(getc(file_a))
    	{
    	case '@':
    	    zero_init_cache_node(conf_mcb, i);
    	    break;
    
    	case '+':
    	    LOGACC(lt_conf_def, i);
    	    create_cache_node(conf_mcb, i);
    	    node = get_conf_node(i);
    	    node->s.exists = 1;
    
    	    node->pos = ftell(file_a) - 1; /* Don't forget the '+' */
    
    	    if ( fparse_conference(file_a, &tmp_conf) != OK )
    		restart_kom("init_cache(): fparse_conference failed. "
    			    "i == %d\n", i);
    
    	    node->size = ftell(file_a) - node->pos;
    	    setup_small_conf(i, &tmp_conf);
    	    clear_conference(&tmp_conf);
    	    
    	    break;
    	}
        }
    
        build_matching_info();
        
        for ( i = 1; i < next_free_num; i++ ) /* PERSONS */
        {
    	fskipwhite(file_a);
    	switch(getc(file_a))
    	{
    	case '@':
    	    zero_init_cache_node(pers_mcb, i);
    	    break;
    
    	case '+':
    	    LOGACC(lt_pers_def, i);
    	    create_cache_node(pers_mcb, i);
    	    node = get_pers_node(i); 
    	    node->s.exists = 1;
    	    node->pos = ftell(file_a) - 1; /* Don't forget the '+' */
    	    if ( fparse_person(file_a, &tmp_pers) != OK )
    		restart_kom("init_cache: fparse_person failed. i == %d\n", i);
    	    
    	    node->size = ftell(file_a) - node->pos;
    	    clear_person(&tmp_pers);
    
    	    break;
    	}
        }
    
        next_text_num = fparse_long(file_a);	/* NEXT_TEXT_NUM */
    
        for ( i = 1; i < next_text_num; i++ ) /* TEXT_STATS */
        {
    	fskipwhite(file_a);
    	switch(getc(file_a))
    	{
    	case '@':
    	    zero_init_cache_node(text_mcb, i);
    	    break;
    
    	case '+':
    	    LOGACC(lt_text_def, i);
    	    create_cache_node(text_mcb, i);
    	    node = get_text_node(i);
    	    node->s.exists = 1;
    
    	    node->pos = ftell(file_a) - 1; /* Don't forget the '+' */
    
    	    if ( fparse_text_stat(file_a, &tmp_text) != OK )
    		restart_kom("init_cache(): fparse_text_stat failed. i == %d\n",
    			    i);
    
    	    node->size = ftell(file_a) - node->pos;
    	    clear_text_stat(&tmp_text);
    
    	    break;
    	}
        }
    
        log("Read %d confs/persons and %d texts\n",
    	next_free_num, next_text_num);
    
        return OK;
    }
    
    /* ramkomd compatibility: */
    extern void
    cache_sync(void)
    {
    }
    
    extern void
    cache_sync_all(void)
    {
        pre_sync();
        while ( sync_part() != TRUE )
    	;
    }
    
    void
    free_all_cache (void)
    {
        int i;
        Cache_node *node;
    
    #ifdef LOGACCESSES
        if (logfile)
    	fclose(logfile);
    #endif
    
        for ( i = 0; i < next_free_num; i++ )
        {
    	node = get_conf_node(i);
    	if ( node != NULL )
    	{
    	    if ( node->snap_shot != NULL )
    	    {
    		free_conference (node->snap_shot);
    		node->snap_shot = NULL;
    	    }
    
    	    if ( node->ptr != NULL )
    	    {
    		free_conference (node->ptr);
    		node->ptr = NULL;
    	    }
    	}
    
    	destruct_cache_node (conf_mcb, i);
    
    	node = get_pers_node(i);
    	if ( node != NULL )
    	{
    	    if ( node->snap_shot != NULL )
    	    {
    		free_person (node->snap_shot);
    		node->snap_shot = NULL;
    	    }
    
    	    if ( node->ptr != NULL )
    	    {
    		free_person (node->ptr);
    		node->ptr = NULL;
    	    }
    	}
    
    	destruct_cache_node (pers_mcb, i);
    
    	if ( small_conf_arr[i] != NULL )
    	{
    	    free_small_conf (small_conf_arr[i]);
    	    small_conf_arr[i] = NULL;
    	}
        }
    
        for ( i = 0; i < next_text_num; i++ )
        {
    	node = get_text_node(i);
    	if ( node != NULL )
    	{
    	    if ( node->snap_shot != NULL )
    	    {
    		free_text_stat (node->snap_shot);
    		node->snap_shot = NULL;
    	    }
    
    	    if ( node->ptr != NULL )
    	    {
    		free_text_stat (node->ptr);
    		node->ptr = NULL;
    	    }
    	}
    
    	destruct_cache_node (text_mcb, i);
        }
    
        free_match_table(match_table);
    
        free_cache_node_mcb(conf_mcb);
        free_cache_node_mcb(text_mcb);
        free_cache_node_mcb(pers_mcb);
    
        sfree (match_table);
        sfree (conf_table);
    }
    
    
    /* Is it allowed to delete this node from the cache? It is, unless
       the node is locked, dirty, or contains a snap-shot.		 */
    
    static Bool
    throwable_p(Cache_node *node)
    {
        return (node->s.dirty == 0
    	    && node->snap_shot == NULL
    	    && node->lock_cnt == 0);
    }
    
    static void
    limit_pers(void)
    {
        Cache_node *node;
        int i;
        
        node = pers_mcb->mru;
    
        /* Skip first CACHE_PERSONS clean persons. */
        for ( i = 0; node != NULL && i < CACHE_PERSONS; i++ )
        {
    	while (node != NULL && !throwable_p(node))
    	    node = node->next;
    	       
    	if ( node != NULL )
    	    node = node->next;
        }
    
        /* Delete any remaining clean persons */
        while ( node != NULL )
        {
    	if (throwable_p(node))
    	{
    	    unlink_pers_lru(node);
    	    free_person (node->ptr);
    	    node->ptr = NULL;
    	    /* +++ delete cache-node if non-existent. */
    	}
    
    	node = node->next;
        }
    }
    
    static void
    limit_conf(void)
    {
        Cache_node *node;
        int i;
        
        node = conf_mcb->mru;
    
        /* Skip first CACHE_CONFERENCES clean confs. */
        for ( i = 0; node != NULL && i < CACHE_CONFERENCES; i++ )
        {
    	while (node != NULL && !throwable_p(node))
    	    node = node->next;
    	       
    	if ( node != NULL )
    	    node = node->next;
        }
    
        /* Delete any remaining clean confs. */
        while ( node != NULL )
        {
    	if (throwable_p(node))
    	{
    	    unlink_conf_lru(node);
    	    free_conference (node->ptr);
    	    node->ptr = NULL;
    	    /* +++ delete if non-existent. */
    	}
    
    	node = node->next;
        }
    }
    
    
    static void
    limit_text_stat(void)
    {
        Cache_node *node;
        int i;
        
        node = text_mcb->mru;
    
        /* Skip first CACHE_TEXT_STATS clean text_stats. */
        for ( i = 0; node != NULL && i < CACHE_TEXT_STATS; i++ )
        {
    	while (node != NULL && !throwable_p(node))
    	    node = node->next;
    	       
    	if (node != NULL)
    	    node = node->next;
        }
    
        /* Delete any remaining clean text_stats. */
        while ( node != NULL )
        {
    	if (throwable_p(node))
    	{
    	    unlink_text_lru(node);
    	    free_text_stat (node->ptr);
    	    node->ptr = NULL;
    	    /* +++ delete if non-existent. */
    	}
    
    	node = node->next;
        }
    }
    
    
    
    /*
     * Limit the number of 'clean' cache entries.
     */
    EXPORT  void
    cache_limit_size(void)
    {
        limit_pers();
        limit_conf();
        limit_text_stat();
    }
    
    EXPORT  void  
    dump_cache_mem_usage(FILE *fp)
    {
        fprintf(fp, "---simple-cache.c:\n"
    	    "\tSmall_confs: %d\n",
    	    no_of_allocated_small_confs);
    }
    
    
    EXPORT void
    dump_cache_stats(FILE *fp)
    {
        fprintf(fp, "---simple-cache.c:\n");
        fprintf(fp,
    	    "\tPersons (cache size: %d):\n"
    	    "\t hits:  %lu\n"
    	    "\t miss:  %lu\n",
    	    CACHE_PERSONS,
    	    pers_mcb->hits,
    	    pers_mcb->misses);
        fprintf(fp,
    	    "\tConferences (cache size: %d):\n"
    	    "\t hits:  %lu\n"
    	    "\t miss:  %lu\n",
    	    CACHE_CONFERENCES,
    	    conf_mcb->hits,
    	    conf_mcb->misses);
        fprintf(fp,
    	    "\tText_stats (cache size: %d):\n"
    	    "\t hits:  %lu\n"
    	    "\t miss:  %lu\n",
    	    CACHE_TEXT_STATS,
    	    text_mcb->hits,
    	    text_mcb->misses);
    }
    
        
    EXPORT int
    query_next_text_num(void)
    {
        return next_text_num;
    }
    
    EXPORT void
    tell_cache_garb_text(int running)
    {
    #ifdef LOGACCESSES
        garb_running = running;
    #endif
    }