/*
 * readmarc.c,v 1.1 1994/01/28 17:06:42 franktor Exp
 */

#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <sr-general.h>
#include "marc.h"
#ifndef __CEXTRACT__
#include <marcproto.h>
#include <utilproto.h>
#endif
#ifdef __sun__
#include "sunos.h"
#endif

#define DEBUG

/*
 * readmarc(): Parses a marc post in a given buffer and returns the
 * post in the form of an internal data structure.
 */

Marcrecord *
ReadMarcData(octetString *os)
{
  Marcrecord *rec;
  Marcdata *entry;
  char *buf = os->value;
  int index, i; /* Char pointer to our position */

  if (os->len < Pos_directory)
  {
    LOG(facHigh, llevDebug, "Record too short, no room for directory.");
    return (Marcrecord *) NULL;
  }
  rec = (Marcrecord *) malloc(sizeof(Marcrecord));
  rec->entries = (Marcdata *) NULL;

  if (ReadMarcData_Label(&rec->label,buf,999) == False) {
    LOG(facHigh, llevDebug, "Failed to read label.");
    free(rec);
    return (Marcrecord *) NULL;
  }

  /*
   * Read the directories by building up a linked list.
   */
  index = Pos_directory;
  while ( (i = ReadMarcData_Directory(rec, buf + index)) != 0 )
    index += i;

  index++; /* Skip the Field Separator */

  /*
   * Read the data pointed to by the directory.
   */
  if (index != rec->label.base_address_of_data) {
    LOG(facHigh, llevExceptions, "Base address of data in ISO 2709 record not correct.");
    LOG(facHigh, llevExceptions, "(was %d, should be %d)", index, rec->label.base_address_of_data);
    return (Marcrecord *) NULL;	/* ???? Should probably free (or return) rec */
  }
  for (entry = rec->entries; entry != (Marcdata *) NULL; entry = entry->next)
    ReadMarcData_Data(entry, buf + index);

  return rec;
}

/*
 * Read the label from the first 40 chars in a marc record.
 * Put it in the label given in the first argument.
 * Return False if something went wrong, otherwise True.
 */

Boolean
ReadMarcData_Label(Record_label *label, char *record, int length)
{
  /* LOG(facHigh, llevTrace, "ReadMarcData_Label()..."); */
  label->record_length =
    (unsigned int) strntol(record + Pos_record_length, (char **) NULL, 10,
                           Len_record_length);
  label->record_status =
    *(record + Pos_record_status);
  (void) memcpy(label->implementation_codes, record + Pos_implementation_codes,
                 Len_implementation_codes);
  label->implementation_codes[Len_implementation_codes] = '\0';
  if (strlen(label->implementation_codes) != Len_implementation_codes)
    LOG(facHigh, llevDebug, "Warning: NULL encountered in implementation codes.");
  label->indicator_length =
    (unsigned int) strntol(record + Pos_indicator_length, (char **) NULL, 10,
                           Len_indicator_length);
  label->identifier_length =
    (unsigned int) strntol(record + Pos_identifier_length, (char **) NULL, 10,
                           Len_identifier_length);
  label->base_address_of_data =
    (unsigned int) strntol(record + Pos_base_address_of_data, (char **) NULL,
                           10, Len_base_address_of_data);
#if 0
  LOG(facHigh, llevDebug, "Base Address: %d", label->base_address_of_data);
#endif
  if (length < Pos_reserved_user_systems)
  {
    LOG(facHigh, llevDebug,"Warning: Short label field (till user)");
    return True;
  }
  (void) memcpy(label->reserved_user_systems,
                record + Pos_reserved_user_systems, Len_reserved_user_systems);
  label->reserved_user_systems[Len_reserved_user_systems] = '\0';
  if (strlen(label->reserved_user_systems) != Len_reserved_user_systems)
    LOG(facHigh, llevDebug, "Warning: NULL encountered in first reserved field.");
  if (length < Pos_directory_map_length)
  {
    LOG(facHigh, llevDebug,"Warning: Short label field (till directory map length)");
    return True;
  }
  label->directory_map_length =
    (unsigned int) strntol(record + Pos_directory_map_length, (char **) NULL,
                           10, Len_directory_map_length);
  label->directory_map_start =
    (unsigned int) strntol(record + Pos_directory_map_start, (char **) NULL,
                           10, Len_directory_map_length);
  (void) memcpy(label->reserved_future, record + Pos_reserved_future,
                Len_reserved_future);
  label->reserved_future[Len_reserved_future] = '\0';
#ifdef DEBUG
  if (strlen(label->reserved_future) != Len_reserved_future)
    LOG(facHigh, llevDebug, "Warning: NULL encountered in second reserved field.");
#endif

  if (label->directory_map_length + label->directory_map_start != DATAFIELD_LENGTH)
  {
#ifdef DEBUG
    LOG(facHigh, llevDebug, "Error: The sum of the directory map (%d + %d) was not %d as",
            label->directory_map_length, label->directory_map_start,
            DATAFIELD_LENGTH);
    LOG(facHigh, llevDebug, "specified by ISO 2709-1973 : 4.1.7");
#endif
    return False;
  }
#if 0
  LOG(facHigh, llevTrace, "ReadMarcData_Label()...returned True");
#endif
  return True;
}

/*
 * ReadMarcDataDirectory:
 *   Parse the directories in the marc post.
 */

int
ReadMarcData_Directory(Marcrecord *rec, char *pos)
{
  Marcdata *entry, *last;
  char *origpos = pos;

  if (*pos == FIELD_SEPARATOR) {
#if 0
    LOG(facHigh, llevDebug, "ReadMarcData_Directory: Reached FIELD_SEPARATOR");
#endif
    return 0;
  }
  entry = (Marcdata *) malloc(sizeof(Marcdata));
  entry->data = (char *) NULL;
  entry->next = (Marcdata *) NULL;
  entry->indicators = False;

  entry->tag =	strntol(pos, (char **) NULL, 10, TAG_LENGTH);

  pos += TAG_LENGTH;
  entry->length_of_datafield =
    (unsigned int) strntol(pos, (char **) NULL, 10,
                           rec->label.directory_map_length);
  pos += rec->label.directory_map_length;
  entry->starting_character_position =
    (unsigned int) strntol(pos, (char **) NULL, 10,
                           rec->label.directory_map_start);
  pos += rec->label.directory_map_start;

  for (last = rec->entries;
       last != (Marcdata *) NULL && last->next != (Marcdata *) NULL;
       last = last->next);
  if (last == NULL)
    rec->entries = entry;
  else
    last->next = entry;
#ifdef DEBUG
#if 0
  LOG(facHigh, llevDebug, "Directory entry: %d,%d",
          entry->length_of_datafield, entry->starting_character_position);
#endif
#endif
  return (int) (pos - origpos);
}

/*
 * read_data: allocates and stores the data.
 */

void
ReadMarcData_Data(Marcdata *marcdata, char *incoming)
{
  char *ch;

  marcdata->data = (char *) malloc(marcdata->length_of_datafield + 1);
  marcdata->data[marcdata->length_of_datafield] = '\0';
  (void) memcpy(marcdata->data,
                incoming + marcdata->starting_character_position,
                marcdata->length_of_datafield);
  /*
   * Conversions.  The need for these conversions might be based on
   * a bug in the format of the records given by ubobok.
   */
  for (ch = marcdata->data; ch < marcdata->data + marcdata->length_of_datafield; ch++) 
    switch(*ch) {
    case 31: /* Subfield separator */
      *ch = '$';
      break;
    case IS3: /* Field separator */
    case IS2: /* Record separator */
      *ch = '\0';
      break;
    }
#ifdef DEBUG
#if 0
  {
    char *buf = (char *) malloc(marcdata->length_of_datafield + 1);
    (void) memcpy(buf, marcdata->data, marcdata->length_of_datafield);
    * (buf + marcdata->length_of_datafield) = '\0';
    LOG(facHigh, llevDebug, "Entry: %s", buf);
    free(buf);
  }
#endif
#endif
}
