/*
 *
 * Copyright (C) 1993 Swedish University Network (SUNET)
 *
 *
 * This program is developed by UDAC, Uppsala University by commission
 * of the Swedish University Network (SUNET). 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITTNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 *                                          Martin.Wendel@udac.uu.se
 *
 *                                          Martin Wendel
 *                                          UDAC	
 *                                          Box 174
 *                                          S-751 04 Uppsala
 *                                          Sweden
 */






#include "mk.h"

unsigned long calc_crc();

/*
**	Calculates the CRC of a BinHex file
**
*/

unsigned long
calc_crc(crc, c)
     register unsigned long crc;
     register unsigned int c;
{
  register int i;
  register unsigned long tmk;

  tmk = crc;
  for (i = 0; i < 8; i++)
    {
      c <<= 1;
      if ((tmk <<= 1) & 0x10000)
	tmk = (tmk & 0xffff) ^ 0x1021;
      tmk ^= (c >> 8);
      c &= 0xff;
    }

  return((unsigned short)tmk);
}

/*
**
**	Extract namelen from a BinHex file
**
*/

int
get_binhex_namelen(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  mk->filenamelen = *buf->out;
  mk->binhex->checksum = calc_crc(0, (unsigned char) *buf->out);
  mk->fpart = get_binhex_name;
  mk->fpartlen = 0;
  buf->outpos++;
  return((mk->fpart)(mk, buf));
}

/*
**	Extract name from a BinHex file
**
*/

int
get_binhex_name(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  if (!mk->filename && !mk->fpartlen)
    mk->filename = (char *)xalloc(mk->filenamelen + 1);
  for(; mk->fpartlen < mk->filenamelen; mk->fpartlen++)
    {
      *(mk->filename + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  *(mk->filename + mk->filenamelen) = '\0';
  if (mk->investigate)
    setfilename(mk->filename);
  mk->fpart = get_binhex_type;
  mk->fpartlen = 0;
  mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0');
  buf->outpos++; /* Move over NULL */
  return((mk->fpart)(mk, buf));
}

/*
**
**	Extract type of a BinHex file
**
*/

int
get_binhex_type(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 4; mk->fpartlen++)
    {
      *(mk->binhex->btype + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 4)
    {
      mk->fpart = get_binhex_auth;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**	Extract auth of a BinHex file
*/

int
get_binhex_auth(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 4; mk->fpartlen++)
    {
      *(mk->binhex->bauth + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 4)
    {
      mk->fpart = get_binhex_flag;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**	Extract flags of a BinHex file.
*/

int
get_binhex_flag(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 2; mk->fpartlen++)
    {
      *(mk->binhex->flag + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 2)
    {
      mk->fpart = get_binhex_dlen;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**	Extract datafork length of a BinHex file.
*/

int
get_binhex_dlen(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 4; mk->fpartlen++)
    {
      *(mk->binhex->dclen + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 4)
    {
      mk->binhex->dlen = getlong(mk->binhex->dclen, 4);
      mk->totlen = mk->binhex->dlen;
      mk->fpart = get_binhex_rlen;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**
**	Extract resource fork length of a BinHex file.
**
*/

int
get_binhex_rlen(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 4; mk->fpartlen++)
    {
      *(mk->binhex->rclen + mk->fpartlen) = *(buf->out + buf->outpos);
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 4)
    {
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0');
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0'); 
      mk->binhex->rlen = getlong(mk->binhex->rclen, 4);
      if (mk->binhex->rlen != 0) {
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "BinHex: decode: decode test: file has resource fork");
#endif
	mk->type = TYPEBINHEX;
      }
      mk->fpart = get_binhex_hcrc;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**
**	Extract and check the header CRC of a BinHex file.
**
*/

int
get_binhex_hcrc(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 2; mk->fpartlen++)
    {
      if (((getbits(mk->binhex->checksum, 8 * (2 - mk->fpartlen), 8) &
	    255) ^
	  (*(buf->out + buf->outpos) & 255))) {
#ifdef EMILDEBUG
	Mk_Log(LOG_ERR, "BinHex: decode failure: header fork checksum error");
#endif
	return(ERROR_CHECKSUM);
      }
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 2)
    {
      mk->binhex->checksum = 0;
      mk->fpart = get_binhex_datafork;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**	Extract and output the datafork
**
*/

int
get_binhex_datafork(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  if (buf->outpos)
    {
      bcopy(buf->out + buf->outpos, buf->out, buf->outlen - buf->outpos);
      buf->outlen -= buf->outpos;
      buf->outpos = 0;
    }
  for(; mk->fpartlen < mk->binhex->dlen; mk->fpartlen++)
    {
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));

      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == mk->binhex->dlen)
    {
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0');
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0'); 

      buf->outdiff = buf->outlen - buf->outpos;
      mk->fpart = get_binhex_dcrc;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**
**	Extract and check the datafork CRC
*/

int
get_binhex_dcrc(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (!buf->outdiff)
    buf->outdiff = buf->outlen;
  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen -= buf->outdiff;
      buf->outdiff = 0;
      return(OK);
    }

  for(; mk->fpartlen < 2; mk->fpartlen++)
    {
      if (((getbits(mk->binhex->checksum, 8 * (2 - mk->fpartlen), 8) &
	    255) ^
	  (*(buf->out + buf->outpos) & 255))) {
#ifdef EMILDEBUG
	Mk_Log(LOG_ERR, "BinHex: decode failure: data fork checksum error");
#endif
	return(ERROR_CHECKSUM);
      }
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outlen -= buf->outdiff;
	  buf->outpos = 0;
	  buf->outdiff = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 2)
    {
      mk->binhex->checksum = 0;
      mk->fpart = get_binhex_resourcefork;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**
**	Extract and junk the resource fork
*/

int
get_binhex_resourcefork(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (!buf->outdiff)
    buf->outdiff = buf->outlen;
  if (buf->outpos >= buf->outlen)
    {
      buf->outlen -= buf->outdiff;
      buf->outpos = 0;
      return(OK);
    }
  for(; mk->fpartlen < mk->binhex->rlen; mk->fpartlen++)
    {
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, 
				      (unsigned char)*(buf->out + buf->outpos));
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outlen -= buf->outdiff;
	  buf->outpos = 0;
	  return(OK);
	}
    }

  if (mk->fpartlen == mk->binhex->rlen)
    {
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0');
      mk->binhex->checksum = calc_crc(mk->binhex->checksum, (unsigned char)'\0'); 
      mk->fpart = get_binhex_rcrc;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

/*
**
**	Extract and check the resource fork CRC
**
*/

int
get_binhex_rcrc(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  if (!buf->outdiff)
    buf->outdiff = buf->outlen;
  if (buf->outpos >= buf->outlen)
    {
      buf->outlen -= buf->outdiff;
      buf->outpos = 0;
      return(OK);
    }
  for(; mk->fpartlen < 2; mk->fpartlen++)
    {
      if (((getbits(mk->binhex->checksum, 8 * (2 - mk->fpartlen), 8) &
	    255) ^
	  (*(buf->out + buf->outpos) & 255))) {
#ifdef EMILDEBUG
	Mk_Log(LOG_ERR, "BinHex: decode failure: resource fork cheksum error");
#endif
	return(ERROR_CHECKSUM);
      }
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outlen -= buf->outdiff;
	  buf->outpos = 0;	  
	  if (mk->fpartlen == 2 && mk->last)
	    return(DONE);
	  else
	    return(OK);
	}
    }

  if (mk->fpartlen == 2)
    {
      buf->outlen -= buf->outdiff;
      mk->binhex->checksum = 0;
      if ((buf->outlen - buf->outpos) >= 1 && 
	  *(buf->out + buf->outpos) != '\0') {
#ifdef EMILDEBUG
	Mk_Log(LOG_ERR, "BinHex: decode failure: premature end of data");
#endif
	return(ERROR_MORE);
      }
      else
	return(DONE);
    }
}

/*
 * Check for AppleSingle/AppleDouble
 */

int
get_applefile_type(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{

  if (buf->outpos >= buf->outlen)
    {
      buf->outpos = 0;
      buf->outlen = 0;
      return(OK);
    }
  for(; mk->fpartlen < 4; mk->fpartlen++)
    {
      mk->applefile = (mk->applefile << 8) | *(buf->out + buf->outpos);
      buf->outpos++;
      if (buf->outpos == buf->outlen)      
	{
	  mk->fpartlen++;
	  buf->outpos = 0;
	  buf->outlen = 0;
	  return(OK);
	}
    }
  if (mk->fpartlen == 4)
    {
      if (0x0051607 ==  mk->applefile)
	{
	  mk->type = TYPEAPPLEDOUBLE;
	  Mk_Log(LOG_DEBUG, "File type AppleDouble");
	}
      if (0x0051600 ==  mk->applefile)
	{
	  mk->type = TYPEAPPLESINGLE;
	  Mk_Log(LOG_DEBUG, "File type AppleSingle");
	}
      mk->fpart = nullfunc;
      mk->fpartlen = 0;
      return((mk->fpart)(mk, buf));
    }
}

int nullfunc(mk, buf)
     MessageStruct *mk;
     BufStruct *buf;
{
  return(OK);
}
