/* * $Id: dbck-cache.c,v 0.56 2003/08/23 16:38:17 ceder Exp $ * Copyright (C) 1991-1999, 2001-2003 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 report bugs at http://bugzilla.lysator.liu.se/. */ /* * This module contains some very 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. * Also save time as a time_t instead of a struct tm. */ #ifdef HAVE_CONFIG_H # include #endif #include #ifndef SEEK_END # include #endif #ifndef SEEK_END # include #endif #ifndef SEEK_END # define SEEK_SET 0 # define SEEK_END 2 #endif #ifdef HAVE_STDLIB_H # include #endif #include "timewrap.h" #include #ifdef TIME_SYNC # include #endif #include #include #include "ldifftime.h" #include "exp.h" #include "misc-types.h" #include "s-string.h" #include "kom-types.h" #include "kom-errno.h" #include "cache.h" #include "debug.h" #include "server/smalloc.h" #include "log.h" #include "lyskomd.h" #include "kom-memory.h" #include "ram-io.h" #include "ram-parse.h" #include "ram-output.h" #include "conf-file.h" #include "param.h" #include "dbck-cache.h" #include "local-to-global.h" #include "unused.h" #include "eintr.h" #include "text-store.h" /* * All functions that can fail sets kom_errno to a suitable value * if they fail. */ Person ** pers_arr; Conference ** conf_arr; String * name_list; /* "cache" list */ /* Global var auto init to NULL */ static Conf_no next_free_num = 1; Text_stat ** text_arr; static Text_no next_text_num = 1; /* Defined in standalone.c */ extern Info kom_info; /* Defined in ramkomd.c */ static FILE *text_file = NULL; static FILE *new_text_file = NULL; /* Used when garbage collecting. */ BUGDECL; /* 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) #define INRANGE(str, num, retval) if ( num == 0 || num >= next_free_num ) \ { \ return retval; \ printf("%s: conf_no %d out of range 1 ... %d ", str, num, next_free_num); \ fflush(stdout); \ fflush(stderr); \ abort(); \ } #define TEXT_RANGE(str, num, retval) if ( num == 0 || num >= next_text_num ) \ { \ return retval;\ printf("Text_no out of range 1 ... %ld ", next_text_num); \ printf(str); \ fflush(stdout); \ fflush(stderr); \ abort(); \ } #define VOID_TEXT_RANGE(str, num) if ( num == 0 || num >= next_text_num ) \ { \ return;\ printf("Text_no out of range 1 ... %ld ", next_text_num); \ printf(str); \ fflush(stdout); \ fflush(stderr); \ abort(); \ } extern Conf_type cached_get_conf_type (Conf_no conf_no) { return conf_arr [ conf_no ]->type; } /* * Various function calls to tell the cache that something is changed. */ void mark_person_as_changed(Pers_no pers_no) { TRACE2("Person %d is changed\n", pers_no); return; } void mark_conference_as_changed(Conf_no conf_no) { TRACE2("Conf. %d is changed\n", conf_no); return; } void mark_text_as_changed( Text_no text_no ) { TRACE2("Text %lu is changed.\n", text_no); VOID_TEXT_RANGE("mark_text_as_changed\n", text_no); } /* * Person-related calls */ extern Success cached_create_person( Pers_no person ) { TRACE2("Person %d is being created.\n", person); INRANGE("cached_create_person\n", person, FAILURE); if ( pers_arr[ person ] != NULL ) { TRACE1("pers_arr not NULL"); fflush(stdout); fflush(stderr); abort(); } pers_arr[ person ] = alloc_person(); return OK; } extern Person * cached_get_person_stat( Pers_no person ) { INRANGE("cached_get_person_stat\n", person, NULL); TRACE2("cached_get_person_stat %d\n", person); err_stat = person; kom_errno = KOM_UNDEF_PERS; return pers_arr[ person ]; } /* * Conference-related calls */ extern Conf_no /* Also cache the name */ cached_create_conf (String name) { Conference * conf_c; Conf_no conf_no; TRACE1("cached_create_conf( "); TRACESTR(name); TRACE1(" )\n"); if ( next_free_num >= param.max_conf ) { err_stat = next_free_num; kom_errno = KOM_INDEX_OUT_OF_RANGE; return 0; } conf_no = next_free_num++; conf_c = alloc_conference(); conf_c->name = EMPTY_STRING; s_strcpy(&conf_c->name, name); TRACE2(" number %d\n", conf_no); conf_arr[ conf_no ] = conf_c; pers_arr[ conf_no ] = NULL; return conf_no; } extern Success cached_delete_conf( Conf_no conf ) { INRANGE("cached_delete_conf()", conf, FAILURE); if ( conf_arr[ conf ] == NULL ) { err_stat = conf; kom_errno = KOM_UNDEF_CONF; return FAILURE; } free_conference(conf_arr[ conf ]); conf_arr[ conf ] = NULL; return OK; } Success cached_delete_person(Pers_no pers) { INRANGE("cached_delete_person()", pers, FAILURE); if ( pers_arr[ pers ] == NULL ) { err_stat = pers; kom_errno = KOM_UNDEF_PERS; return FAILURE; } pers_arr[ pers ] = NULL; return OK; } Success cached_delete_text(Text_no text) { TEXT_RANGE("cached_delete_text()", text, FAILURE); if ( text_arr[ text ] == NULL ) { err_stat = text; kom_errno = KOM_NO_SUCH_TEXT; return FAILURE; } free_text_stat(text_arr[ text ]); text_arr[ text ] = NULL; return OK; } extern Conference * cached_get_conf_stat( Conf_no conf_no ) { INRANGE("cached_get_conf_stat\n", conf_no, NULL); TRACE2("cached_get_conf_stat %d\n", conf_no); kom_errno = KOM_UNDEF_CONF; err_stat = conf_no; return conf_arr[ conf_no ]; } /* * Return TRUE if conf_no exists. */ Bool cached_conf_exists(Conf_no conf_no) { return conf_arr[ conf_no ] != NULL ? TRUE : FALSE; } /* * Calls to handle texts */ extern String cached_get_text( Text_no text ) { String the_string = EMPTY_STRING; Text_stat *t_stat; TRACE2("cached_get_text %lu\n", text); if ( (t_stat = cached_get_text_stat (text)) == NULL ) return EMPTY_STRING; else if (text_store_load_text(text, t_stat, &the_string, text_file) != OK) return EMPTY_STRING; return the_string; } extern Text_stat * /* NULL on error */ cached_get_text_stat( Text_no text ) { kom_errno = KOM_NO_SUCH_TEXT; err_stat = text; TEXT_RANGE("cached_get_text_stat\n", text, NULL); TRACE2("cached_get_text_stat %lu\n", text); return text_arr[ text ]; } /* * The text is set up with an empty misc-field. The misc field is * later initialized by create_text. */ #if 0 extern Text_no cached_create_text( String message) { Text_no tno; tno = next_text_num++; TRACE2("cached_create_text (len=%d)\n", message.len); if ( tno >= param.max_text ) { next_text_num = param.max_text; kom_errno = KOM_INDEX_OUT_OF_RANGE; err_stat = next_text_num; return 0; } text_arr[ tno ] = alloc_text_stat(); text_arr[ tno ]->no_of_misc = 0; text_arr[ tno ]->misc_items = NULL; text_arr[ tno ]->no_of_marks = 0; text_arr[ tno ]->no_of_lines = 0; text_arr[ tno ]->no_of_chars = 0; fseek(text_file, 0, SEEK_END); text_arr[ tno ]->file_pos = ftell(text_file); if ( fwrite(message.string, sizeof(char), message.len, text_file) != message.len ) { kom_log("WARNING: cached_create_text: Couldn't write the text %d\n", tno); } fflush(text_file); TRACE2("cached_create_text -> %d\n", tno); return tno; } #endif void cached_flush_text(Text_no text_no, String message) { if ( text_no >= next_text_num ) { kom_log("cached_flush_text(%lu, string): only text %lu exists.\n", (u_long) text_no, (u_long) next_text_num); return; } fseek(new_text_file, 0, SEEK_END); #warning Generation > 0 not yet handled text_arr[ text_no ]->text_store.generation = 0; text_arr[ text_no ]->text_store.file_pos = ftell(new_text_file); text_arr[ text_no ]->text_store.file_size = 0; if ( fwrite(message.string, sizeof(char), message.len, new_text_file) != (size_t)message.len ) { kom_log("WARNING: cached_flush_text: Couldn't write the text %ld\n", (unsigned long)text_no); } fflush(new_text_file); } Text_no traverse_text(Text_no seed) { seed++; while ( seed < next_text_num && text_arr[ seed ] == NULL ) seed++; return (seed == next_text_num) ? 0 : seed ; } Pers_no traverse_person(Pers_no seed) { seed++; while ( seed < next_free_num && pers_arr[ seed ] == NULL ) seed++; return (seed == next_free_num) ? 0 : seed ; } Conf_no traverse_conference(Conf_no seed) { seed++; while ( seed < next_free_num && conf_arr[ seed ] == NULL ) seed++; return (seed == next_free_num) ? 0 : seed ; } extern Garb_nice cached_get_garb_nice (Conf_no conf_no) { return conf_arr [ conf_no ]->nice; } extern Garb_nice cached_get_keep_commented(Conf_no conf_no) { return conf_arr[conf_no]->keep_commented; } extern Local_text_no cached_get_highest_local_no (Conf_no conf_no) { return l2g_first_appendable_key(&conf_arr[conf_no]->texts) - 1; } /* Lock a person struct in memory. Increase a referenc count. */ void cached_lock_person(Pers_no UNUSED(pers_no)) {} /* Decrease reference count. If zero, unlock person. */ void cached_unlock_person(Pers_no UNUSED(pers_no)) {} /* Lock a conf struct in memory. Increase a referenc count. */ void cached_lock_conf(Conf_no UNUSED(conf_no)) {} /* Decrease reference count. If zero, unlock conf. */ void cached_unlock_conf(Conf_no UNUSED(conf_no)) {} static Bool is_clean(const char *fn) { FILE *fp; if ((fp = i_fopen(fn, "rb")) == NULL) return FALSE; if ( getc(fp) == 'C' && getc(fp) == 'L' && getc(fp) == 'E' && getc(fp) == 'A' && getc(fp) == 'N' ) { i_fclose(fp); return TRUE; } else { i_fclose(fp); return FALSE; } } #ifdef TIME_SYNC static long timerdiff(struct timeval a, struct timeval b) { /* Ignore the tv_usec parts. One second more or less doesn't matter. */ return a.tv_sec - b.tv_sec; } #endif extern void /* Write out everything. */ cache_sync_all(void) { struct dbfile *fp; Conf_no ic; Text_no it; #ifdef TIME_SYNC struct rusage start, after_confs, after_persons, after_text_stats, after_text_masses; time_t st, ac, ap, ats, atm; void getrusage(int who, struct rusage *rusage); #endif #ifdef TIME_SYNC getrusage(0, &start); time(&st); #endif if ( is_clean(param.datafile_name) ) { if (is_clean(param.backupfile_name)) { if (i_rename(param.backupfile_name, param.backupfile_name_2) != 0) { kom_log("pre_sync: can't do extra backup.\n"); } } if (i_rename(param.datafile_name, param.backupfile_name) != 0) kom_log("WARNING: cache_sync_all: can't backup.\n"); } else kom_log("cache_sync_all: datafile not clean. No backup taken.\n"); if ((fp = dbfile_open_write(param.datafile_name, "DIRTY", NULL)) == NULL) { kom_log("WARNING: cache_sync_all: can't open file to save in.\n"); return; } /* * Print file header and other things that go before the conferences. */ switch (fp->format) { case 0: fprintf(fp->fp, "%d\n", next_free_num); /* NEXT_FREE_NUM */ break; case 1: case 2: case 3: fprintf(fp->fp, "#C %d\n", next_free_num); fprintf(fp->fp, "#T %ld\n", next_text_num); fprintf(fp->fp, "I"); foutput_info(fp, &kom_info); foutput_newline(fp); break; default: restart_kom("Unknown output file format: %d\n", fp->format); } for (ic = 1; ic < next_free_num; ic++) /* CONFS */ { switch (fp->format) { case 0: if (conf_arr[ic] == NULL) foutput_atsign(fp); else { fprintf(fp->fp, "+ "); foutput_conference(fp, conf_arr[ic]); } foutput_newline(fp); break; case 1: case 2: case 3: if (conf_arr[ic] != NULL) { fprintf(fp->fp, "C %lu", (unsigned long)ic); foutput_conference(fp, conf_arr[ic]); foutput_newline(fp); } break; default: restart_kom("Bad output file format: %d\n", fp->format); } } #ifdef TIME_SYNC getrusage(0, &after_confs); time(&ac); #endif for (ic = 1; ic < next_free_num; ic++) /* PERSONS */ { switch (fp->format) { case 0: if (pers_arr[ic] == NULL) foutput_atsign(fp); else { fprintf(fp->fp, "+ "); fprintf(fp->fp, "%dH", PASSWD_LEN); fwrite(pers_arr[ic]->pwd, PASSWD_LEN, 1, fp->fp); foutput_person(fp, pers_arr[ic]); } foutput_newline(fp); break; case 1: case 2: case 3: if (pers_arr[ic] != NULL ) { fprintf(fp->fp, "P %lu", (unsigned long)ic); fprintf(fp->fp, " %dH", PASSWD_LEN); fwrite(pers_arr[ic]->pwd, PASSWD_LEN, 1, fp->fp); foutput_person(fp, pers_arr[ic]); foutput_newline(fp); } break; default: restart_kom("Bad output file format: %d\n", fp->format); } } #ifdef TIME_SYNC getrusage(0, &after_persons); time(&ap); #endif /* * Print things that go between persons and texts */ switch (fp->format) { case 0: fprintf(fp->fp, "%ld\n", next_text_num); /* NEXT_TEXT_NUM */ break; case 1: case 2: case 3: break; default: restart_kom("Unknown output file format: %d\n", fp->format); } for (it = 1; it < next_text_num; it++) /* TEXT_STATS */ { switch (fp->format) { case 0: if (text_arr[it] == NULL) foutput_atsign(fp); else { fprintf(fp->fp, "+ "); foutput_text_stat(fp, text_arr[it]); } foutput_newline(fp); break; case 1: case 2: case 3: if (text_arr[it] != NULL) { fprintf(fp->fp, "T %lu", it); foutput_text_stat(fp, text_arr[it]); foutput_newline(fp); } break; default: restart_kom("Unknown output file format: %d\n", fp->format); } } /* * Print file footer * * Ho-hum. I thought there was supposed to be a newline at the end * of a version zero file, but it seems I was wrong, so this code * basically does...absolutely nothing. */ switch (fp->format) { case 0: break; case 1: case 2: case 3: break; default: restart_kom("Unknown output file format: %d\n", fp->format); } #ifdef TIME_SYNC getrusage(0, &after_text_stats); time(&ats); getrusage(0, &after_text_masses); time(&atm); #endif dbfile_change_magic(fp, "CLEAN"); dbfile_delete(fp); fp = NULL; #ifdef TIME_SYNC kom_log("Sync ready.\n" "User: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" "Sys: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" "Real: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" "Page faults: %4ld. Swapped: %4ld. Outblocked: %4ld.\n", timerdiff(after_text_masses.ru_utime, start.ru_utime), timerdiff(after_confs.ru_utime, start.ru_utime), timerdiff(after_persons.ru_utime, after_confs.ru_utime), timerdiff(after_text_stats.ru_utime, after_persons.ru_utime), timerdiff(after_text_masses.ru_utime, after_text_stats.ru_utime), timerdiff(after_text_masses.ru_stime, start.ru_stime), timerdiff(after_confs.ru_stime, start.ru_stime), timerdiff(after_persons.ru_stime, after_confs.ru_stime), timerdiff(after_text_stats.ru_stime, after_persons.ru_stime), timerdiff(after_text_masses.ru_stime, after_text_stats.ru_stime), (u_long)ldifftime(atm, st), (u_long)ldifftime(ac, st), (u_long)ldifftime(ap, ac), (u_long)ldifftime(ats, ap), (u_long)ldifftime(atm, ats), after_text_masses.ru_majflt - start.ru_majflt, after_text_masses.ru_nswap - start.ru_nswap, after_text_masses.ru_oublock - start.ru_oublock); #endif } void cache_open_new_text_file(void) { if ((new_text_file = i_fopen(param.textfile_name, "w")) == NULL) { kom_log("Can't open file to save new texts. Goodbye.\n"); exit(1); } } extern Success init_cache(void) { struct dbfile *fp = NULL; unsigned long i; extern int vflag; /* from dbck.c */ extern Bool truncated_texts; /* from dbck.c */ long num; char done, read_text_num, read_conf_num; int c; extern int modifications; extern int pers_pres_conf; extern int conf_pres_conf; extern int motd_conf; extern Text_no motd_of_lyskom; extern int kom_news_conf; read_text_num = 0; read_conf_num = 0; pers_arr = smalloc(sizeof(*pers_arr) * param.max_conf); conf_arr = smalloc(sizeof(*conf_arr) * param.max_conf); text_arr = smalloc(sizeof(*text_arr) * param.max_text); name_list = smalloc(sizeof(*name_list) * param.max_conf); for (i = 0; i < param.max_conf; i++) conf_arr[i] = NULL; for (i = 0; i < param.max_conf; i++) pers_arr[i] = NULL; for (i = 0; i < param.max_text; i++) text_arr[i] = NULL; new_text_file = NULL; if ((text_file = i_fopen(param.textfile_name, "rb")) == NULL) { perror(param.textfile_name); restart_kom("ERROR: init_cache: can't open text file %s.\n", param.textfile_name); } if ( is_clean(param.datafile_name) ) { if ((fp = dbfile_open_read(param.datafile_name, "CLEAN")) == NULL) { kom_log("WARNING: init_cache: can't open datafile.\n"); return FAILURE; } kom_log("MSG: init_cache: using datafile.\n"); } else if ( is_clean(param.backupfile_name) ) { if ((fp = dbfile_open_read(param.backupfile_name, "CLEAN")) == NULL) { kom_log("WARNING: init_cache: can't open backupfile.\n"); return FAILURE; } kom_log("MSG: init_cache: using backup file.\n"); } else { kom_log("WARNING: init_cache: can't find old data base.\n"); return FAILURE; } /* * Read file header */ switch (fp->format) { case 0: fparse_set_pos(fp, 6); /* skip clean/dirty flag. */ next_free_num = fparse_long(fp); /* NEXT_FREE_NUM */ read_conf_num = 1; if (vflag) { kom_log("Data file version is '%d'\n", fp->format); kom_log("Reading %d conferences, starting at pos %ld.\n", next_free_num-1, dbfile_ftell(fp)); } break; case 1: fparse_set_pos(fp, 12); if (vflag) kom_log("Data file version is '%d'\n", fp->format); break; case 2: case 3: fparse_set_pos(fp, 33); if (vflag) kom_log("Data file version is '%d'\n", fp->format); break; default: restart_kom("Unknown input file format: %d\n", fp->format); } done = 0; /* * Loop over records in the database. For a version zero database, * loop next_free_num times and for a version one database, loop * until the end of the file or until done is set */ for ( i = 1; (fp->format == 0 && i < next_free_num && !dbfile_feof(fp)) || (fp->format == 1 && !done) || (fp->format == 2 && !done) || (fp->format == 3 && !done); i++ ) { num = i; fskipwhite(fp); switch(c = dbfile_getc(fp)) { case EOF: switch (fp->format) { case 0: restart_kom("Unexpected end-of-file reading conferences " "in data file version %d.", fp->format); break; case 1: case 2: case 3: done = 1; break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case '@': switch (fp->format) { case 0: conf_arr[num] = NULL; break; case 1: case 2: case 3: restart_kom("@ record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case 'I': switch (fp->format) { case 0: restart_kom("I record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: if (fparse_info(fp, &kom_info) != OK) restart_kom("Invalid I record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); if (pers_pres_conf != -1) { modifications += (kom_info.pers_pres_conf != pers_pres_conf); kom_info.pers_pres_conf = pers_pres_conf; } if (conf_pres_conf != -1) { modifications += (kom_info.conf_pres_conf != conf_pres_conf); kom_info.conf_pres_conf = conf_pres_conf; } if (motd_conf != -1) { modifications += (kom_info.motd_conf != motd_conf); kom_info.motd_conf = motd_conf; } if (kom_news_conf != -1) { modifications += (kom_info.kom_news_conf != kom_news_conf); kom_info.kom_news_conf = kom_news_conf; } if (motd_of_lyskom != (Text_no)-1) { modifications += (kom_info.motd_of_lyskom != motd_of_lyskom); kom_info.motd_of_lyskom = motd_of_lyskom; } break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case 'C': switch (fp->format) { case 0: restart_kom("C record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: num = fparse_long(fp); if (conf_arr[num]) free_conference(conf_arr[num]); conf_arr[num] = alloc_conference(); if (fparse_conference(fp, conf_arr[num]) != OK) restart_kom("init_cache(): %s. i == %lu\n", "fparse_conference failed", i); name_list[num] = EMPTY_STRING; s_strcpy(&name_list[num], conf_arr[ num ]->name); break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case 'P': switch (fp->format) { case 0: restart_kom("P record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: num = fparse_long(fp); if (pers_arr[num]) free_person(pers_arr[num]); pers_arr[num] = alloc_person(); if ( fparse_person(fp, pers_arr[ num ]) != OK ) restart_kom("init_cache: fparse_person failed. num==%ld\n", num); break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case 'T': switch (fp->format) { case 0: restart_kom("T record in data file version %d at %ld\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: num = fparse_long(fp); if (text_arr[num]) free_text_stat(text_arr[num]); text_arr[ num ] = alloc_text_stat(); if ( fparse_text_stat(fp, text_arr[ num ]) != OK ) { /* Simply ignore everything else. The cause of this error might be that the file has been truncated (maybe due to an overfull disk). */ kom_log("init_cache(): fparse_text_stat failed " "for text %ld.\n%s", num, "Everything remaining is lost.\n"); next_text_num = i; read_text_num = 1; truncated_texts = TRUE; done = 1; } break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case '+': switch (fp->format) { case 0: conf_arr[ i ] = alloc_conference(); if ( fparse_conference(fp, conf_arr[ i ]) != OK ) restart_kom("init_cache(): %s. i == %lu\n", "fparse_conference failed", i); name_list[i] = EMPTY_STRING; s_strcpy(&name_list[i], conf_arr[ i ]->name); break; case 1: case 2: case 3: restart_kom("+ record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case '#': switch (fp->format) { case 0: restart_kom("#record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: switch (c = dbfile_getc(fp)) { case 'C': next_free_num = fparse_long(fp); read_conf_num = 1; break; case 'T': next_text_num = fparse_long(fp); read_text_num = 1; break; default: restart_kom("Unknown # record (#%c) in data " "file version %d at %ld.\n", c, fp->format, dbfile_ftell(fp)); } break; default: restart_kom("Unknown input file format: %d\n", fp->format); } break; case '-': switch (fp->format) { case 0: restart_kom("- record in data file version %d at %ld.\n", fp->format, dbfile_ftell(fp)); break; case 1: case 2: case 3: switch(c = dbfile_getc(fp)) { case 'C': num = fparse_long(fp); if (conf_arr[num] != NULL) free_conference(conf_arr[num]); conf_arr[num] = NULL; break; case 'P': num = fparse_long(fp); if (pers_arr[num] != NULL) free_person(pers_arr[num]); pers_arr[num] = NULL; break; case 'T': num = fparse_long(fp); if (text_arr[num] != NULL) free_text_stat(text_arr[num]); text_arr[num] = NULL; break; default: restart_kom("Unknown - record (%c) in data file " "version %d at %ld.\n", c, fp->format, dbfile_ftell(fp)); } break; default: restart_kom("Unknown input file format %d\n", fp->format); } break; default: restart_kom("Unknown record type in data file version " "%d at %ld.\n", fp->format, dbfile_ftell(fp)); } } /* * The rest is only for data file version 0 */ if (fp->format == 0) { if ( vflag ) kom_log("Reading %d persons, starting at pos %ld.\n", next_free_num-1, dbfile_ftell(fp)); for ( i = 1; i < next_free_num; i++ ) /* PERSONS */ { fskipwhite(fp); switch(dbfile_getc(fp)) { case '@': pers_arr[ i ] = NULL; break; case '+': pers_arr[ i ] = alloc_person(); if ( fparse_person(fp, pers_arr[ i ]) != OK ) restart_kom("init_cache: fparse_person failed. i == %ld\n", i); break; } } next_text_num = fparse_long(fp); /* NEXT_TEXT_NUM */ read_text_num = 1; if ( vflag ) kom_log("Reading %ld texts, starting at pos %ld.\n", next_text_num-1, dbfile_ftell(fp)); for ( i = 1; i < next_text_num; i++ ) /* TEXT_STATS */ { fskipwhite(fp); switch(dbfile_getc(fp)) { case '@': text_arr[ i ] = NULL; break; case '+': text_arr[ i ] = alloc_text_stat(); if ( fparse_text_stat(fp, text_arr[ i ]) != OK ) { /* Simply ignore the following texts. The cause of this error might be that the file has been truncated (maybe due to an overfull disk). */ kom_log("init_cache(): fparse_text_stat failed for text %ld.\n" "All remaining texts are lost.\n", i); next_text_num = i; truncated_texts = TRUE; } break; } } } /* if (datafile_version == 0) */ kom_log("Read %d confs/persons and %ld texts, eof at %ld\n", next_free_num-1, next_text_num-1, dbfile_ftell(fp)); if (read_text_num == 0 || read_conf_num == 0) restart_kom("init_cache(): Failed to read next_conf or text num.\n"); dbfile_delete(fp); return OK; } void free_all_dbck_cache(void) { unsigned int i; for ( i = 1; i < next_free_num; i++ ) { if (pers_arr[i] != NULL) free_person(pers_arr[i]); if (conf_arr[i] != NULL) { free_conference(conf_arr[i]); s_clear(&name_list[i]); } } for ( i = 1; i < next_text_num; i++ ) if (text_arr[i] != NULL) free_text_stat(text_arr[i]); sfree(pers_arr); sfree(conf_arr); sfree(text_arr); sfree(name_list); i_fclose(text_file); if (new_text_file != NULL) i_fclose(new_text_file); }