#include "options.h"
/*
Copyright (C) 1986 Rahul Dhesi -- All rights reserved
*/
/**********************
portable.c contains functions needed to make Zoo portable to various
implementations of C.

Note:  Provided a 2's complement machine is used, all functions in
this file are themselves machine-independent and need not be changed
when implementing Zoo on a different machine.  Some code will choke
on 1's complement machines--I think.  

For machine-dependent declarations see files "machine.h" and "options.h". 

For machine-dependent functions see file "machine.c"
*/

/* See if we could just use these for MSC */
#ifdef MSC
#ifndef PORTABLE
#define PORTABLE
#endif
#endif


#ifdef PORTABLE
#include <stdio.h>
#include "various.h"
#include "zoofns.h"

#include "machine.h"
#include "zoo.h"
#include "debug.h"
#include "assert.h"

#ifdef NEEDCTYP
#include <ctype.h>              /* for tolower() */
#endif

#ifdef DEBUG
extern int verbose;
#endif

/* Functions defined for use within this file only.  */
#ifdef LINT_ARGS
long to_long (BYTE[]);
int to_int (BYTE[]);
void b_to_zooh(struct zoo_header *, BYTE[]);
void b_to_dir(struct direntry *, BYTE[]);
int dir_to_b(BYTE[], struct direntry *);
void zooh_to_b(BYTE[], struct zoo_header *);
void splitlong(BYTE[], long);
void splitint(BYTE[], int);
#else
long to_long ();
int to_int ();
void b_to_zooh();
void b_to_dir();
int dir_to_b();
void zooh_to_b();
void splitlong();
void splitint();
#endif

extern unsigned int crccode;


/************************************************************************/
/*** Following are functions that make up for various implementations ***/
/*** of C not having certain library routines.                        ***/
/************************************************************************/

#ifndef MSC
/**********************
strlwr() converts a string to lowercase and returns a pointer to the string 
*/
char *strlwr (str)
char *str;
{
   register char *s;
   s = str;
   while (*s != '\0') {
      *s = toascii(*s);
      if (isupper(*s))
         *s = tolower(*s);
      s++;
   }
   return (str);
}

#ifdef COMMENT
/**********************
strupr() converts a string to uppercase and returns a pointer to the string
*/
char *strupr (str)
char *str;
{
   register char *s;
   s = str;
   while (*s != '\0') {
      *s = toascii(*s);
      if (islower(*s))
         *s = toupper(*s);
      s++;
   }
   return (str);
}
#endif /* COMMENT */

/**********************
strcmpi() compares strings just like strcmp() but it does it without regard to
case.
*/
int strcmpi (s1, s2)
register char *s1, *s2;
{
   for ( ; tolower(*s1) == tolower(*s2);  s1++, s2++)
      if (*s1 == '\0')
         return(0);
   return(tolower(*s1) - tolower(*s2));
}

/**********************
memset() exists in Microsoft C and UNIX System V but not in Xenix.  It sets 
the first "cnt" bytes of "dest" to the character "c" and returns a pointer to
"dest".
*/
char *memset (dest, c, cnt)
char *dest;
int c;
unsigned cnt;
{
   register unsigned i;
   for (i = 0; i < cnt; i++) {
      *(dest + i) = c;
   }
}

/**********************
fputchar() writes a character to stdout.  It is identical to putchar
but is a function, not a macro.
*/
int fputchar (c)
int c;
{
   return (fputc(c, stdout));
}

/**********************
tell() returns the current position of the file handle supplied
*/
long tell (handle)
int handle;
{
   return (lseek (handle, 0L, 1));     /* seek to current position */
}
#endif /* ifndef MSC */


/***********************************************************************/
/*** Following are declarations and functions that are written in a  ***/
/*** machine-independent way but they implement machine-dependent    ***/
/*** activities                                                      ***/
/***********************************************************************/

#ifndef MSC
/**********************
to_long() converts four consecutive bytes, in order of increasing
significance, to a long integer.  It is used to make Zoo independent of the
byte order of the system.  
*/
long to_long(data)
BYTE data[];
{
   return (long) ((unsigned long) data[0] | ((unsigned long) data[1] << 8) |
         ((unsigned long) data[2] << 16) | ((unsigned long) data[3] << 24));
}

/********************
splitlong() converts a long integer to four consecutive BYTEs in order
of increasing significance.
*/
void splitlong(bytes, bigword)
BYTE bytes[];
long bigword;
{
   int i;
   for (i = 0; i < 4; i++) {
      bytes[i] = bigword & 0xff;
      bigword = (unsigned long) bigword >> 8;
   }
}     

/*******************
splitint() converts an integer to two consecutive BYTEs in order
of increasing significance.
*/
void splitint(bytes, word)
BYTE bytes[];
int word;
{
   bytes[0] = word & 0xff;
   word = (unsigned int) word >> 8;
   bytes[1] = word & 0xff;
}

/**********************
to_int() converts two consecutive bytes, in order of increasing
significance, to an integer, in a machine-independent manner
*/
int to_int(data)
BYTE data[];
{
   return (int) ((unsigned int) data[0] | ((unsigned int) data[1] << 8));
}

#else /* else of ifndef MSC */

long to_long(data)
BYTE data[];
{
   return ( * (long *) data );
}

/********************
splitlong() converts a long integer to four consecutive BYTEs in order
of increasing significance.
*/
void splitlong(bytes, bigword)
BYTE bytes[];
long bigword;
{
   * (long *) bytes = bigword;
}     

/*******************
splitint() converts an integer to two consecutive BYTEs in order
of increasing significance.
*/
void splitint(bytes, word)
BYTE bytes[];
int word;
{
   * (int *) bytes = word;
}

/**********************
to_int() converts two consecutive bytes, in order of increasing
significance, to an integer.
*/
int to_int(data)
BYTE data[];
{
   return (*(int *) data);
}

#endif /* ifndef MSC .. else ... */


#ifndef FIZ
/**********************
Function frd_zooh() reads the header of a Zoo archive in a machine-
independent manner, from a FILE.
*/
int frd_zooh(zoo_header, zoo_file)
struct zoo_header *zoo_header;
FILE *zoo_file;
{
   int status;
   BYTE bytes[SIZ_ZOOH];         /* canonical header representation */
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", ftell(zoo_file));
   }
#endif
   status = fread ((char *) bytes, 1, SIZ_ZOOH, zoo_file);
   b_to_zooh (zoo_header, bytes);   /* convert array to structure */
#ifdef DEBUG
   if (verbose) {
      printf("frd_zooh: reading\n");
      show_h(zoo_header);
   }
#endif
   if (status < SIZ_ZOOH)
      return (-1);
   else
      return (0);
}
#endif /* ifndef FIZ */


/**********************
Function frd_dir() reads a directory entry in a machine-independent manner,
from a FILE.
*/
int frd_dir(direntry, zoo_file) 
struct direntry *direntry; 
FILE *zoo_file;
{
   int status;
   BYTE bytes[MAXDIRSIZE];    /* big enough to hold variable part too */

   /* To simplify things, we read the maximum possible size of the
   directory entry including the variable size and discard what is not
   needed */
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", ftell(zoo_file));
   }
#endif
   status = fread ((char *) bytes, 1, MAXDIRSIZE, zoo_file);
   if (status < SIZ_DIR)
      return (-1);
   b_to_dir (direntry, bytes);
#ifdef DEBUG
   if (verbose) {
      printf("frd_dir: reading\n");
      show_dir(direntry);
   }
#endif
   return (0);
}

#ifndef FIZ
/**********************
Function rd_zooh() reads a Zoo archive header in a machine-dependent manner,
from a file handle.
*/
int rd_zooh (header, zoo_han)
struct zoo_header *header;
int zoo_han;
{
   int status;
   BYTE bytes[SIZ_ZOOH];
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", tell(zoo_han));
   }
#endif
   status = read (zoo_han, (char *) bytes, SIZ_ZOOH);
   b_to_zooh (header, bytes);
#ifdef DEBUG
   if (verbose) {
      printf("rd_zooh: reading\n");
      show_h(header);
   }
#endif
   return (status);
}

/**********************
Function rd_dir() reads a directory entry in a machine-independent manner
from a handle.
*/
int rd_dir(direntry, zoo_han)
struct direntry *direntry;
int zoo_han;
{
   int status;
   BYTE bytes[MAXDIRSIZE];    /* big enough to hold variable part too */
   /* To simplify things, we read the maximum possible size of the
   directory entry including the variable size and discard what is not
   needed */
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", tell(zoo_han));
   }
#endif
   status = read (zoo_han, (char *) bytes, MAXDIRSIZE);
   if (status < SIZ_DIR)
      return (-1);
   b_to_dir (direntry, bytes);
#ifdef DEBUG
   if (verbose) {
      printf("rd_dir: reading\n");
      show_dir(direntry);
   }
#endif
   return (0);
}

/***********************
Function fwr_dir() writes a directory entry in a machine-independent manner
to a FILE.  Return value is -1 on error, else 0.
*/
int fwr_dir(direntry, zoo_file)
struct direntry *direntry;
FILE *zoo_file;
{
   int size;
   BYTE bytes[MAXDIRSIZE];
   assert (direntry->type <= 2);
   size = dir_to_b (bytes, direntry);
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", ftell(zoo_file));
      printf("fwr_dir: writing\n");
      show_dir(direntry);
   }
#endif

   if (fwrite ((char *) bytes, 1, size, zoo_file) != size)
      return (-1);
   else
      return (0);
}

/***********************
Function wr_dir() writes a directory entry in a machine-independent manner
to a handle.  Return value is -1 on error else 0.
*/
int wr_dir(direntry, zoo_han)
struct direntry *direntry;
int zoo_han;
{
   int size;
   BYTE bytes[MAXDIRSIZE];
   assert (direntry->type <= 2);
   size = dir_to_b (bytes, direntry);
#ifdef DEBUG
   if (verbose) {
      printf("At file position [%8lx] ", tell(zoo_han));
      printf("wr_dir:  writing\n");
      show_dir(direntry);
   }
#endif
   if (write (zoo_han, (char *) bytes, size) != size)
      return (-1);
   else
      return (0);
}

/***********************
Function wr_zooh() writes an archive header in a machine-independent manner
to a handle.  Return value -1 if error else 0.
*/
int wr_zooh(zoo_header, zoo_han)
struct zoo_header *zoo_header;
int zoo_han;
{
   BYTE bytes[SIZ_DIR];
   zooh_to_b (bytes, zoo_header);
   if (write (zoo_han, (char *) bytes, SIZ_ZOOH) != SIZ_ZOOH)
      return (-1);
   else
      return (0);
}

/***********************
Function fwr_zooh() writes an archive header in a machine-independent manner
to a FILE.  Return value is -1 if error else 0.
*/
int fwr_zooh(zoo_header, zoo_file)
struct zoo_header *zoo_header;
FILE *zoo_file;
{
   BYTE bytes[SIZ_DIR];
   zooh_to_b (bytes, zoo_header);
   if (fwrite ((char *) bytes, 1, SIZ_ZOOH, zoo_file) != SIZ_ZOOH)
      return (-1);
   else
      return (0);
}

/***********************
b_to_zooh() converts an array of BYTE to a zoo_header structure.
*/
void b_to_zooh (zoo_header, bytes)
struct zoo_header *zoo_header;
BYTE bytes[];
{
   int i;
   for (i = 0; i < SIZ_TEXT; i++)                     /* copy text */
      zoo_header->text[i] = bytes[TEXT_I + i];
   zoo_header->zoo_tag = to_long(&bytes[ZTAG_I]);     /* copy zoo_tag */
   zoo_header->zoo_start = to_long(&bytes[ZST_I]);    /* copy zoo_start */
   zoo_header->zoo_minus = to_long(&bytes[ZSTM_I]);
   zoo_header->major_ver = bytes[MAJV_I];          /* copy versions */
   zoo_header->minor_ver = bytes[MINV_I];
}

/***********************
zooh_to_b() converts a zoo_header structure to an array of BYTE.
*/
void zooh_to_b (bytes, zoo_header)
struct zoo_header *zoo_header;
BYTE bytes[];
{
   int i;
   for (i = 0; i < SIZ_TEXT; i++)                     /* copy text */
      bytes[TEXT_I + i] = zoo_header->text[i];
   splitlong (&bytes[ZTAG_I], zoo_header->zoo_tag);
   splitlong (&bytes[ZST_I], zoo_header->zoo_start);
   splitlong (&bytes[ZSTM_I], zoo_header->zoo_minus);
   bytes[MAJV_I] =   zoo_header->major_ver;           /* copy versions */ 
   bytes[MINV_I] =   zoo_header->minor_ver;
} /* zooh_to_b() */

/************************
dir_to_b() converts a directory entry structure to an array of BYTE.
*/
int dir_to_b (bytes, direntry)
struct direntry *direntry;
BYTE bytes[];
{
   int i;
   int cursize;
   int fixsize;
   splitlong(&bytes[DTAG_I], direntry->zoo_tag);
   bytes[DTYP_I] = direntry->type ;
   bytes[PKM_I] = direntry->packing_method ;
   splitlong(&bytes[NXT_I], direntry->next);
   splitlong(&bytes[OFS_I], direntry->offset);
   splitint(&bytes[DAT_I], direntry->date);
   splitint(&bytes[TIM_I], direntry->time);
   splitint(&bytes[CRC_I], direntry->file_crc);
   splitlong(&bytes[ORGS_I], direntry->org_size);
   splitlong(&bytes[SIZNOW_I], direntry->size_now);
   bytes[DMAJ_I] = direntry->major_ver;
   bytes[DMIN_I] = direntry->minor_ver;
   bytes[DEL_I] = direntry->deleted;
   bytes[STRUC_I] = direntry->struc;
   splitlong(&bytes[CMT_I], direntry->comment);
   splitint(&bytes[CMTSIZ_I], direntry->cmt_size);
   for (i = 0; i < FNM_SIZ; i++)
      bytes[FNAME_I + i] = direntry->fname[i];
   bytes[TZ_I] = NO_TZ;       /* assume unknown */
   bytes[NAMLEN_I] = 0;
   bytes[DIRLEN_I] = 0;

   cursize = SIZ_DIR;         /* to count size of directory */
   fixsize = SIZ_DIR;         /* size of fixed part */
   assert (direntry->type <= 2);
   if (direntry->type == 2) { /* handle stuff relevant to type 2 */
      cursize = SIZ_DIRL;
      fixsize = SIZ_DIRL;
      bytes[TZ_I] = direntry->tz;
      assert(direntry->namlen < 256 && direntry->namlen >= 0);
      if (direntry->namlen > 0 || direntry->dirlen > 0)
         cursize += 2;        /* space for namlen and dirlen */
      if (direntry->namlen > 0) {
         bytes[NAMLEN_I] = direntry->namlen;
         for (i = 0; i < direntry->namlen; i++)
            bytes[LFNAME_I+i] = direntry->lfname[i];
         cursize += direntry->namlen;
      } 
      assert(direntry->dirlen < 256 && direntry->dirlen >= 0);
      if (direntry->dirlen > 0) {
         bytes[DIRLEN_I] = direntry->dirlen;
         for (i = 0; i < direntry->dirlen; i++)
            bytes[cursize+i] = direntry->dirname[i];
         cursize += direntry->dirlen;
      }
      splitint(&bytes[cursize], direntry->system_id);
   }

   /* Total length of directory entry is now cursize. */
   splitint(&bytes[VARDIRLEN_I], cursize - fixsize);
   assert(cursize == 
            ((bytes[DIRLEN_I] > 0 || bytes[NAMLEN_I] > 0) ? 2 : 0) +
            fixsize + bytes[DIRLEN_I] + bytes[NAMLEN_I]
         );

   /* Do CRC assuming CRC field is zero, and stuff CRC into field. */
   splitint(&bytes[DCRC_I], 0);           /* fill with zeroes */
   crccode = 0;
   addbfcrc(bytes, cursize);              /* update CRC */
   splitint(&bytes[DCRC_I], crccode);

   /* return total length of directory entry */
   return (cursize);


} /* dir_to_b() */
#endif /* ifndef FIZ */

/* b_to_dir() converts bytes to directory entry structure.  The CRC of the
directory bytes, if any, is checked and a zero or nonzero value is returned
in direntry->dir_crc according as the check is good or bad */

void b_to_dir(direntry, bytes)
struct direntry *direntry;
BYTE bytes[];
{
   int i;
   unsigned int savecrc;
   direntry->zoo_tag = to_long(&bytes[DTAG_I]);
   direntry->type = bytes[DTYP_I];
   direntry->packing_method = bytes[PKM_I];
   direntry->next = to_long(&bytes[NXT_I]);
   direntry->offset = to_long(&bytes[OFS_I]);
   direntry->date = to_int(&bytes[DAT_I]);
   direntry->time = to_int(&bytes[TIM_I]);
   direntry->file_crc = to_int(&bytes[CRC_I]);
   direntry->org_size = to_long(&bytes[ORGS_I]);
   direntry->size_now = to_long(&bytes[SIZNOW_I]);
   direntry->major_ver = bytes[DMAJ_I];
   direntry->minor_ver = bytes[DMIN_I];
   direntry->deleted = bytes[DEL_I];
   direntry->struc = bytes[STRUC_I];
   direntry->comment = to_long(&bytes[CMT_I]);
   direntry->cmt_size = to_int(&bytes[CMTSIZ_I]);
   for (i = 0; i < FNM_SIZ; i++)
      direntry->fname[i] = bytes[FNAME_I + i];

   /* start by assuming variable part is zero bytes */
   direntry->var_dir_len = direntry->dir_crc    = 0;
   direntry->namlen      = direntry->dirlen     = 0;
   direntry->lfname[0]   = direntry->dirname[0] = '\0';
   direntry->tz = NO_TZ;               /* assume unknown */
   direntry->system_id = SYSID_NIX;    /* default system_id if not present */

   assert (direntry->type <= 2);
   if (direntry->type == 2) {
      direntry->var_dir_len = to_int(&bytes[VARDIRLEN_I]);
      assert(direntry->var_dir_len <= MAXDIRSIZE);
      if (direntry->var_dir_len > MAXDIRSIZE)
         direntry->var_dir_len = MAXDIRSIZE;
      direntry->tz = bytes[TZ_I];   
      if (direntry->var_dir_len > 0)
         direntry->namlen = bytes[NAMLEN_I];
      if (direntry->var_dir_len > 1)
         direntry->dirlen = bytes[DIRLEN_I];
      for (i = 0; i < direntry->namlen; i++)
         direntry->lfname[i] = bytes[LFNAME_I + i];
      for (i = 0; i < direntry->dirlen; i++)
         direntry->dirname[i] = bytes[DIRNAME_I + direntry->namlen + i];
      if (direntry->var_dir_len > direntry->namlen + direntry->dirlen + 2) {
         direntry->system_id = to_int(&bytes[DIRNAME_I+direntry->namlen+i]);
      }
      /* do CRC calculation */
      savecrc = (unsigned int) to_int(&bytes[DCRC_I]);
      crccode = 0;
      splitint(&bytes[DCRC_I], 0);
      addbfcrc(bytes, SIZ_DIRL + direntry->var_dir_len);
      direntry->dir_crc = crccode - savecrc;
   }
}

#ifdef DEBUG
/* dump contents of archive header */
show_h (zoo_header)
struct zoo_header *zoo_header;
{
   int i;
   printf ("Header text:\n");
   for (i = 0; i < SIZ_TEXT-1;  i++)   /* all but trailing ^Z */
      putchar(zoo_header->text[i]);
   putchar('\n');
   printf ("zoo_tag = [%8lx] zoo_start = [%8lx] zoo_minus = [%8lx]\n",
            zoo_header->zoo_tag, zoo_header->zoo_start, 
            zoo_header->zoo_minus);
   printf ("major_ver.minor_ver = [%d.%d]\n",
            zoo_header->major_ver, zoo_header->minor_ver);
   printf ("---------\n");
}

/* dump contents of directory entry */
show_dir (direntry)
struct direntry *direntry;
{
   int i;
   printf ("Directory entry for file [%s][%s]:\n",
            direntry->fname, direntry->lfname);
   printf ("tag = [%8lx] type = [%d] PM = [%d] Next = [%8lx] Offset = [%8lx]\n",
            direntry->zoo_tag, (int) direntry->type, 
            (int) direntry->packing_method, direntry->next, 
            direntry->offset);
   printf ("Orig size = [%ld] Size now = [%ld] dmaj_v.dmin_v = [%d.%d]\n",
         direntry->org_size, direntry->size_now,
         (int) direntry->major_ver, (int) direntry->minor_ver);
   printf ("Struc = [%d] DEL = [%d] comment_offset = [%8lx] cmt_size = [%d]\n",
         (int) direntry->struc, (int) direntry->deleted, direntry->comment,
         direntry->cmt_size);
   printf ("var_dir_len = [%d] TZ = [%d] dir_crc = [%4x]\n",
            direntry->var_dir_len, (int) direntry->tz, direntry->dir_crc);
   printf ("system_id = [%d]  dirlen = [%d]  namlen = [%d]\n", 
            direntry->system_id, direntry->dirlen, direntry->namlen);
   if (direntry->dirlen > 0)
      printf ("dirname = [%s]\n", direntry->dirname);
   printf ("---------\n");
}
#endif   /* DEBUG */

#endif   /* PORTABLE */
