/*
 * dbread.c
 * File revision 0
 * Read some data from a database.
 * (c) 2000 Jacob Lundberg, jacob@chaos2.org
 */


/*
 * 2000.10.22	Initial implementation
 */


#include <stdio.h>
#include "db.h"


int db_read(DB_FILE *db_fp, char *key, void *data) {
/*
 * db_read()
 * Read a record of a database.  We make the (reasonable!) assumption
 * that you're smart enough to check the record size first...
 */

   db_header header;
   int data_size = 0;
   off_t data_pos = 0;
   db_data_header data_header;
   int key_size = db_key_size(db_fp);
   int hash_size = db_hash_size(db_fp);
   char *search_key = (char *)malloc(key_size);
   if(search_key == NULL) return(-1);

   /* Take a read lock on the database header. */
   if(db_lock_read(db_fp->file, 0)) return(-1);

   /* Read the header. */
   if(fseek(db_fp->file, 0, SEEK_SET) || !fread(&header, DB_HEADER_SIZE, 1, db_fp->file))
      return(-1);

   /* Drop the read lock from the header. */
   db_unlock_read(db_fp->file, 0);

   /* Determine the expected offset of the correct hash item. */
   data_pos = header.trans_table + DB_LOCK_SIZE + sizeof(int) * hash(key, hash_size);

   /* Take a read lock on the lookup table. */
   if(db_lock_read(db_fp->file, header.trans_table)) return(-1);

   /* Now data_pos changes from the lookup offset to the expected data offset. */
   if(fseek(db_fp->file, data_pos, SEEK_SET) || !fread(&data_pos, sizeof(int), 1, db_fp->file))
      return(-1);

   /* Drop the lock from the lookup table. */
   db_unlock_read(db_fp->file, header.trans_table);

   /* Loop through the potential slink list of data looking for the right key. */
   do {
      /* Take a read lock on the selected data. */
      if(db_lock_read(db_fp->file, data_pos)) return(-1);

      /* Read the header of the data. */
      if(fseek(db_fp->file, data_pos, SEEK_SET) || !fread(&data_header, DB_DATA_HEADER_SIZE, 1, db_fp->file))
         return(-1);

      /* Read the key of the data. */
      if(fseek(db_fp->file, data_pos + DB_DATA_HEADER_SIZE, SEEK_SET) ||
            !fread(search_key, key_size, 1, db_fp->file))
         return(-1);

      /* I'm the anal-retentive type. */
      search_key[key_size - 1] = '\0';

      /* Check whether we found what we want. */
      if(!strncmp(key, search_key, key_size)) {
         /* Read the data. */
         if(fseek(db_fp->file, data_pos + DB_DATA_HEADER_SIZE + key_size, SEEK_SET) ||
               !fread(data, data_header.size, 1, db_fp->file))
            return(-1);
         /* We drop out of this function here in the *success* path. */
         db_unlock_read(db_fp->file, data_pos);
         free(search_key);
         return(0);
      }

      /* Drop the read lock from the selected data. */
      db_unlock_read(db_fp->file, data_pos);

      /* Advance on to the next item in the list. */
      data_pos = data_header.next;

   } while(data_pos >= DB_HEADER_SIZE);

   /* ``I still haven't found what I'm looking for.'' -- U2 */
   free(search_key);
   return(1);

}

