/* ------------------------------------------------------------------- */
/* MARC2709  : Converts a "line format MARC record" into ISO 2709      */
/*                                                                     */
/*             Input string requirements:                              */
/*             1. New MARC tag is indicated by MARC tag in pos 1-3     */
/*                (may be preceded by *). Indicator follows in next 2  */
/*                bytes.                                               */
/*             2. Continuation line is indicated by blank in col 1     */
/*             3. End-of-record (^) may or may not be present          */
/*                                                                     */
/* author    : Ole Husby                                               */
/* updated   : 1998-09-30                                              */
/* ------------------------------------------------------------------- */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define NORMARC                                12

#define SUBFIELD_CODE                          "\x1f"
#define FIELD_TERMINATOR                       "\x1e"
#define RECORD_TERMINATOR                      "\x1d"

#define INDICATOR_COUNT                        '2'
#define SUBFIELD_CODE_COUNT                    '2'
#define LENGTH_OF_LENGTH_OF_FIELD              '4'
#define LENGTH_OF_STARTING_CHARACTER_POSITION  '5'
#define UNUSED                                 "   "
#define TAG001                                 "001"
#define TAG008                                 "008"
#define SKIP                                     41

static char out_record[10240],
	      leader[24+1],
	      directory[10240],
	      field_001[32], 
	      field_008[40+2],
	      data_field[10240];


static char logline[128];
static char rstr[6], dollar, dollarstring[2];
static int skip;

static char direlem[300][12+1];
static int dircntr;

union LDR   {
   char ldr_string[24+1];
   struct {
      char record_length[5],
      record_status,
      record_type,
      bibliographic_level,
      unused1[2],
      indicator_count,
      subfield_code_count,
      base_adress[5],
      unused2[3],
      entry1,
      entry2,
      entry3,
      entry4;
   } ltab;
 } ldr;

static int startet = 0;



/* ------------------------------------------------------------------- */
/* Compare function for qsort                                          */
/* ------------------------------------------------------------------- */

int cmp(const void *a, const void *b)
{
   return strcmp((char*)a, (char*)b);
}




/* ------------------------------------------------------------------- */
/* Convert integer to string of fixed length. Right justified padded   */
/* with zeros. Returns NULL if error                                   */
/* ------------------------------------------------------------------- */

char *itoal(int n, int len)
{
   char *r;
   r = (char *) rstr;

   switch (len)
   {
      case  4 : sprintf(r, "%04d", n); break;
      case  5 : sprintf(r, "%05d", n); break;
      default : return NULL;                       
   }

   r[len] = '\0';

   return r;
}



/* ------------------------------------------------------------------- */
/* One 001 subfield is put into field_001                              */
/* ------------------------------------------------------------------- */

static int make_001(char *data, char subfield)
{
   int len;

   startet = 0;
   switch (subfield)
   {
      case ' ' : break;
      case 'a' : break;
      default  : return 1;
   }

   while (data[0] == ' ') data++;
   strcpy(field_001, data);

   len = strlen(data) + 1;
   skip = SKIP + len;

   sprintf(directory, "00100%2d000000080041000%2d", len, len);
   return 1;
}



/* ------------------------------------------------------------------- */
/* One subfield string is appended to data_field                       */
/* ------------------------------------------------------------------- */

static int out_subfield(char *data, char *ftag, char subfield, char nextsubfield)
{
   int len, subint;
   char subreplace, *p, *up;

   while (data[strlen(data) -1] == ' ') 
          data[strlen(data) -1] = '\0';            /* strip trailing blanks */

   if (subfield == '3')                            /* always discard $3     */
      return 1;

   if (strcmp(ftag, TAG001) == 0)                  /* 001 data treatment     */
   {
      make_001(data, subfield);
      return 1;
   }
   
   if (strcmp(ftag, TAG008) == 0)                  /* 008 data treatment     */
   {
      strncpy(field_008, data, strlen(data)); 
      return 1;
   }
   
   if (subfield != ' ')                             /* handle subfield code  */
   {
      strcat(data_field, SUBFIELD_CODE);
      len = strlen(data_field);
      data_field[len] = subfield;
      data_field[len + 1] = '\0';
   }

   while(data[0] == ' ') data++;                   /* strip leading blanks   */
   if (!data) return 0;

   strcat(data_field, data);                       /* append data            */
   
   return 1;
}



/* ------------------------------------------------------------------- */
/* One MARC field is split into subfields and given to output          */
/* ------------------------------------------------------------------- */

static int out_field(char *data, char *ftag, char *find)
{
   char lnumber[5], snumber[6];
   int fStart, fLen, moresubs;
   char subfield[5120], *sub, subcode, *nextsub, nextsubcode;
   char *v, subfield_v[32];

   if (!startet) return 1;

   if ((strncmp(ftag, "00", 2) == 0) &&
       (strcmp(ftag, TAG001) != 0) &&
       (strcmp(ftag, TAG008) != 0))
      return 1;

   fStart = strlen(data_field);
   
   if (strncmp(data, "  $", 3) == 0) data +=2;   /* possible if 008         */

   if (data[0] != dollar)                        /* Dollar sign missing     */
   {
      if (strncmp(ftag,"00",2) != 0) return 0;   /* only allowed if tag=00x */
	 
      if (strcmp(ftag, TAG001) == 0)             /* 001: skip leading blanks */
         while (data[0] == ' ') 
            data++;  

      subcode = ' ';
      nextsubcode = ' ';

      if (!out_subfield(data, ftag, subcode, nextsubcode)) 
	 return 0;
      return 1;
   }

   else
   {
      data++;

      if (!data) return 0;

      if (strncmp(ftag,"00",2) != 0)             /* append indicators       */
         strcat(data_field, find);
      
      sub = strtok(data, dollarstring);          /* first subfield          */
      if (!sub)
         return 0;

/*    printf("--- %s\n", sub); */

      subcode = sub[0];
      sub++;

      moresubs = 1;

      while (moresubs)                           /* loop while more subflds */
      {  
         nextsub = strtok(NULL, dollarstring);   /* next subfld             */
         if (nextsub) 
         {
/*          printf("--- %s\n", nextsub); */
	    nextsubcode = nextsub[0];
	    nextsub++;
         }
         else
            nextsubcode = ' ';

         if (sub)
         {
	    if (!out_subfield(sub, ftag, subcode, nextsubcode)) 
	       return 0;
/*          printf("Denne |%c| Neste |%c|\n", subcode, nextsubcode);  */
         }

         if (nextsub)
         {
            subcode = nextsubcode;
            sub = nextsub;
         }
         else
            moresubs = 0;
      }
   }

   if (strcmp(ftag,TAG001) == 0 || strcmp(ftag,TAG008) == 0)              
      return 1;                                  /* directory ready         */

   strcat(data_field, FIELD_TERMINATOR);         /* terminate data field   */
   fLen = strlen(data_field) - fStart;           /* length of field        */

   strcpy(lnumber,itoal(fLen,4));                /* convert to 4digit str  */
   if (!lnumber) return 0;
   strcpy(snumber,itoal(fStart + skip,5));       /* convert start          */
   if (!snumber) return 0;

   strcpy(direlem[dircntr], ftag);               /* start direlem with tag */
   strcat(direlem[dircntr], lnumber);            /* append length to dir   */
   strcat(direlem[dircntr], snumber);            /* append start to dir    */
   dircntr++;

   return 1;
}



/* ------------------------------------------------------------------- */
/* Main program                                                        */
/* ------------------------------------------------------------------- */

char *MARC2709(char *record, int last, int first, char recstatus, 
  char rectype, char biblevel)          
{
   static char buffer[10240], field_data[5120], tag[4], ind[3];
   char        alen[3], *cp, *buf = buffer, *cq, logline[128];
   int                  prelen, ii; 
   int                  lastchar, Last, First, x, more;
   int			SOF, digits, newField;

   char test[6];

   strcpy(buf, record);

   Last = First = 0;                        /* initialize             */
   if (last > 0)                            /* Last : linelength if   */
   {                                        /*        fixed           */
      Last = last;                          /* First: starting col    */
      First = first;                        /*        for cont. line  */
   }
  
   prelen = 5;
   dollar = '$';
   strcpy(dollarstring, "$");

   data_field[0] = '\0';
   directory[0]  = '\0';
   strcpy(field_001, "         ");
   strcpy(field_008, "                                        ");
   out_record[0] = '\0';
   dircntr       = 0;
   startet       = 0;

   /* initialize directory  with 9 bytes for 001 */

   sprintf(directory, "001001000000008004100010", alen, alen);
   skip = SKIP + 10;

   more = 1; 
   while (more)                                       /* loop until EOR  */ 
   {
      x = 0;                                          /* x counts bytes  */

      for (cp = buf; *cp && *cp != '\n'; cp++) x++;   /* read until LF   */

      if (!*cp)                                       /* EOR             */
        more = 0; 
      else
        cp++;

      SOF = 0;

      if (buf[0] == '^') break;                       /* "EOR-line"      */
      if (buf[0] == '*')                              /* skip leading *  */
      {
	 buf++;
	 x--;
         SOF = 1;
      }

      if (x == 0) continue;                           /* empty line     */

      digits = 1;                                     /* test if first 3     */ 
      for (ii = 0; ii < 3; ii++)                      /* bytes are digits    */
         if (!isdigit(buf[ii]))
         {
            digits = 0;
            break;
         }
         
      newField = 0;     

      if (SOF)
      {
	if (!digits) continue;                      /* illegal line        */
        else newField = 1;
      } 
      else
	 if (digits) newField = 1;


      if (newField)                                  /* start of new field   */
      {
	 if (!out_field(field_data, tag, ind))       /* output prev. field   */
	    return NULL;

	 startet = 1;
	 field_data[0] = '\0';                       /* reset field buffer   */
	 strncpy(tag, buf, 3);                       /* keep MARC tag        */
	 buf += 3;
	 x -=3;
     
         if (strncmp(tag, "00", 2) != 0) 
         {
            strncpy(ind, buf, 2);                 /* keep indicator       */
            buf += 2;                             /* if not control field */
            x -= 2;
         }

	 strncat(field_data, buf, x);             /* keep data               */
        
	   					/* strip trailing blanks   */
	 while (field_data[strlen(field_data) -1] == ' ')
	 {
	    field_data[strlen(field_data) -1] = '\0';
	    x--;
	 }

	 if (Last && x == (Last - prelen))           /* full line            */
            lastchar = 1;
         else 
            lastchar = 0;
      }

      else                                     /* continuation line     */
      {
	if (Last)
	{
	   cq  = buf + First - 1;              /* skip fixed nmbr chars */
	   x -= (First - 1); 
	   if (!lastchar && cq[0] != ' '       /* and insert one blank  */
	       && cq[0] != dollar)
	      strcat(field_data, " ");         /* if needed             */
	}
	else
	{
	   for (cq = buf; *cq == ' '; cq++) x--;  /* skip leading blanks   */
	   strcat(field_data, " ");               /* insert one blank      */
	}

	strncat(field_data, cq, x);            /* append data           */
                                               /* strip trail. blanks   */
        while (field_data[strlen(field_data) -1] == ' ')
        {
	   field_data[strlen(field_data) -1] = '\0';
	   x--;
	}

	if (Last && x == (Last - 5))           /* full line            */
	   lastchar = 1;
        else lastchar = 0;
        
      }

      while (field_data[strlen(field_data) -1] == ' ')
         field_data[strlen(field_data) - 1] = '\0'; /* rm trailing blanks */
      buf = cp;
   }

   if (!out_field(field_data, tag, ind))       /* output last field     */
      return NULL;

                                               /* terminate field_001   */
   strcat(field_001, FIELD_TERMINATOR); 

   field_008[40] = '\0';                       /* terminate field_008   */
   strcat(field_008, FIELD_TERMINATOR); 

					       /* sort directory elem.  */
   qsort ((void *) direlem, dircntr, sizeof(direlem[0]), cmp); 
   for (ii = 0; ii < dircntr; ii++)
      strcat(directory, direlem[ii]);          /* append to directory   */

   strcat(directory, FIELD_TERMINATOR);        /* terminate dictionary  */ 
   
   strcat(data_field, RECORD_TERMINATOR);      /* terminate data        */

   x = 24 + strlen(directory);                 /* construct leader      */
   
   strcpy(test, itoal(x,5));

   strncpy(ldr.ltab.base_adress, test, 5);

   x += (strlen(data_field)
       + strlen(field_001)
       + strlen(field_008));

   strcpy(test, itoal(x,5));
   
   strncpy(ldr.ltab.record_length, test, 5);
   
   ldr.ltab.record_status = recstatus;
   ldr.ltab.record_type = rectype;
   ldr.ltab.bibliographic_level = biblevel;
   ldr.ltab.indicator_count = INDICATOR_COUNT;
   ldr.ltab.subfield_code_count = SUBFIELD_CODE_COUNT;
   strncpy(ldr.ltab.unused1,UNUSED,2);
   strncpy(ldr.ltab.unused2,UNUSED,3);
   ldr.ltab.entry1 = LENGTH_OF_LENGTH_OF_FIELD;
   ldr.ltab.entry2 = LENGTH_OF_STARTING_CHARACTER_POSITION;
   ldr.ltab.entry3 = '0';     
   ldr.ltab.entry4 = '0';     

   ldr.ldr_string[24] = '\0';

   strcpy(out_record, ldr.ldr_string);          /* put together record  */
   strcat(out_record, directory);
   strcat(out_record, field_001);
   strcat(out_record, field_008);
   strcat(out_record, data_field);
 
   return out_record; 
}
