/*********************************************************

      LOADMAT.C

      This function is used to load matrices and Matlab
      strings recorded in Matfile format into memory for
      use by a C program.  Matlab strings are converted to
      C nul-terminated strings.  Memory is allocated on
      the heap for all matrices and strings loaded, so
      the calling program does not have to know beforehand
      how large the arrays are.

      In order to permit this function to be used with 
      arrays of different types (double, float, etc.), the 
      function arguments use (char **) for passing the data 
      arrays.  When the function is called, casts to this
      type should be used in the argument list.  For 
      example, to load floating point arrays, the call would 
      be:

         FILE *fp;
         char type;
         char name[30];
         int rows, columns;
         int imag_flag;
         float *a_real, *a_imag;

         err = loadmat(fp, &type, name, &rows, &columns,
                        &imag_flag, (char **) &a_real,
                        (char **) &a_imag);

      err = 0 if the load is completed successfully,
            EOF if end of file is encountered when trying to
                  read the Fmatrix,
            1 if any other error is encountered.

      Note that space for the matrix name must be allocated 
      by the calling routine, while space for the data 
      arrays is allocated by loadmat, and must be freed by 
      the calling routine when it is no longer needed.

      This version of loadmat does not take into account the 
      machine on which the MATfile was written.  It will 
      work only when loading MATfiles on the same type of
      machine with which they were written.

      This is a major modification of a function written by
      J. N. Little, 86-11-03, for MATLAB.

            Eric Firing
            88-02-12

      Mon  03-13-1989  Added support for machines other than
            the PC, via alternative initialization of
            bytes_per_word.

***********************************************************/
#include "common.h"
#include "matfile.h"

#define invalid_type(x) (x<0 || x>4)

#if (MACHINE_TYPE == PC)
static int  bytes_per_word[] = { 8,   4,   4,   2,   2};
#else
static int  bytes_per_word[] = { 8,   4,   4,   4,   4};
#endif
static char type_letter[]    = {'d', 'f', 'l', 'i', 'u'};

/**** Convert a Matlab string to a nul-terminated string.
      The c-string is returned in the same space as the
      Matlab string was stored.
****/
#if PROTOTYPE_ALLOWED
char *convert_mat_string(double *s, int n)
#else
char *convert_mat_string(s, n)
double *s;
int n;                /* length; from ncols */
#endif
{
   int i;
   char *cstring;

   cstring = (char *) calloc(n+1, sizeof(char));
   if (cstring==NULL)
      return ((char *) NULL);    /* error condition */
   for (i=0; i<n; i++)
      cstring[i] = (char) s[i];
   cstring[n] = '\0';
   strcpy((char *)s, cstring);
   free(cstring);
   return((char *)s);
}                       /* convert_mat_string() */


#if PROTOTYPE_ALLOWED
int loadmat(FILE *fp,
            char *type, char *pname,
            int *mrows, int *ncols, int *imagf,
            char **preal, char **pimag)
#else
int loadmat(fp, type, pname, mrows, ncols, imagf, preal, pimag)
FILE *fp;       /* File pointer */
char *type;     /* Type flag: d, f, l, i, u, t */
int *mrows;     /* row dimension */
int *ncols;     /* column dimension */
int *imagf;     /* imaginary flag */
char *pname;    /* pointer to matrix name */
char **preal;   /* pointer to pointer to real data */
char **pimag;   /* pointer to pointer to imag data */
#endif
{                       /* loadmat() */
   Fmatrix x;
   int mn, namlen;
   int nbytes;
   int num_type,     /* 0->double, 1->float, 2->long, 3->short, 4->ushort */
       text_flag,    /* 1 if text */
       c_storage;    /* 1 if stored by C program: by rows */

   /*
    * Get Fmatrix structure from file
    */
   if (fread((char *)&x, sizeof(Fmatrix), 1, fp) != 1) {
      if (feof(fp))
         return(EOF);
      printf("\nLOADMAT error, can't read Fmatrix.\n");
      return(1);
   }
   c_storage = (x.type%1000)/100;
   num_type = (x.type%100)/10;
   text_flag = (x.type%10);
   if (invalid_type(num_type))  {
      printf("\nLOADMAT error, invalid type: %ld\n", x.type);
      return(1);
   }
   nbytes = bytes_per_word[num_type];

   if (text_flag)
      *type = 't';
   else
      *type = type_letter[num_type];

   if (c_storage || text_flag) {   /* stored C-fashion, by rows */
      *mrows = x.mrows;
      *ncols = x.ncols;
   }
   else  {                     /* stored Fortran-fashion, by columns */
      *mrows = x.ncols;        /*   so the MATfile column is a C row */
      *ncols = x.mrows;        /*   and vise-versa                   */
   }
   *imagf = x.imagf;
   namlen = x.namlen;
   mn = x.mrows * x.ncols * nbytes;

   /*
    * Get matrix name from file
    */
   if (fread(pname, sizeof(char), namlen, fp) != namlen) {
      printf("\nLOADMAT error, can't read matrix name.\n");
      return(1);
   }
   
   /*
    * Get Real part of matrix from file
    */
   if ((*preal = (char *)malloc(mn) ) == NULL) {
      printf("\nLOADMAT error, not enough memory for real part\n");
      return(1);
   }
   if (fread(*preal, 1, mn, fp) != mn) {
      free(*preal);
      return(1);
   }
   if(text_flag)  {
      char *string;     /* temporary pointer, used only here */
      string = convert_mat_string((double *) *preal, (*ncols)*(*mrows));
      if (string == NULL)  {
         printf("LOADMAT error, not enough memory for C string.\n");
         free(*preal);
         return(1);
      }
      else  {
         *preal = string;
         return (0);      /* If it is a string, ignore imaginary part.*/
      }
   }

   /*
    * Get Imag part of matrix from file, if it exists
    */
   if (x.imagf) {
      if ((*pimag = (char *)malloc(mn) ) == NULL) {
         printf("\nLOADMAT error, not enough memory for imaginary part\n");
         free(*preal);
         return(1);
      }
      if (fread(*pimag, 1, mn, fp) != mn) {
         free(*pimag);
         free(*preal);
         return(1);
      }
   }

   return(0);
}                       /* loadmat() */
