/*
 * 3binout.c
 * code to write to 3binary packets
 * Public domain from M. Kimes
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "3binary.h"


size_t align_fwrite (char *data,int size,int count,FILE *handle) {

  /*
   * write data w/ alignment padding
   */

  size_t len = size * count;

  return fwrite(data,1,len + (len % sizeof(USHORT)),handle);
}


int write_eop3 (FILE *handle) {

  /*
   * write end-of-packet chunk
   */

  SHORT t;

  t = sizeof(SHORT);
#ifdef MOTOROLA
  t = swap3(t);
#endif
  if(fwrite(&t,sizeof(SHORT),1,handle) != 1)
    return WRITEERR3;
  t = EOP3;
  if(fwrite(&t,sizeof(SHORT),1,handle) != 1)
    return WRITEERR3;
  /* set up so next write to packet will overwrite this marker */
  if(fseek(handle,ftell(handle) - (long)(sizeof(USHORT) * sizeof(USHORT)),SEEK_SET) != 0)
    return SEEKERR3;
  return (ferror(handle)) ? WRITEERR3 : NOERR3;
}


USHORT write_chunk3 (FILE *handle,CHUNK3 *info,int *error) {

  /*
   * write a single type 3binary chunk out to disk
   * note info->len should be data length + length of type int
   * (data length + sizeof(USHORT))
   */

  register USHORT olen = 0;
  USHORT          len;

  *error = NOERR3;
  if(info) {
    if(info->len < sizeof(USHORT) ||
       (info->len > sizeof(USHORT) && !info->data)) {
      *error = BADCHUNK3;
      return 0;
    }
#ifdef MOTOROLA
    len = swap3(info->len);
#else
    len = info->len;
#endif
    olen += fwrite(&len,1,sizeof(USHORT),handle);
#ifdef MOTOROLA
    len = swap3(info->type);
#else
    len = info->type;
#endif
    olen += fwrite(&len,1,sizeof(USHORT),handle);
    if(info->len && info->data) {
#ifdef MOTOROLA
      switch(chunk->type) { /* swap bytes around on chunk data we understand */
        case MSG3:
        case PKT3:
        case GLOBAL3:
        case ID3:
        case REF3:
        case ATTRIB3:
          *(long *)chunk->data = swap3l(*(long *)chunk->data);
          break;
        case DATE3:
          (*(BINDATE3 *)chunk->data).year = swap3((*(BINDATE3 *)chunk->data).year);
          (*(BINDATE3 *)chunk->data).gmtoff = swap3((*(BINDATE3 *)chunk->data).gmtoff);
          break;
        case ENCRYPT3:
        case QUOTE3:
          *(USHORT *)chunk->data = swap3(*(USHORT *)chunk->data;
          *(USHORT *)chunk->data + (*(*SHORT *)chunk->data) + sizeof(USHORT) +
          (*(USHORT *)chunk->data % sizeof(USHORT)) =
            swap3(*(USHORT *)chunk->data + (*(*SHORT *)chunk->data) +
            sizeof(USHORT) + (*(USHORT *)chunk->data % sizeof(USHORT)));
          break;
      }
#endif
      len = align_fwrite(info->data,1,info->len - sizeof(USHORT),handle);
    }
  }

  if(len != (USHORT)(info->len + (info->len % sizeof(USHORT))) - sizeof(USHORT) ||
     ferror(handle))
    *error = WRITEERR3;
  return olen + len;
}


long write_chunk3_list (FILE *handle,USHORT type,CHUNK3 *list,int *error) {

  /*
   * write a linked list of chunks to packet.  returns length written.
   * error return in error
   */

  register CHUNK3 *info = list;
  register long    len = 0L,temp,chklen = 0L;
  long             kissmeonce;
  CHUNK3           header;

  *error = NOERR3;
  /* determine size of all subchunks */
  while(info) {
    chklen += ((long)info->len + sizeof(USHORT) + (info->len % sizeof(USHORT)));
    info = info->next;
  }

  /* write main chunk */
#ifdef MOTOROLA
  kissmeonce = swap3l(chklen);
#else
  kissmeonce = chklen;
#endif
  header.data = &kissmeonce;
  header.type = type;
  header.len = sizeof(USHORT) + sizeof(long);
  if(!write_chunk3(handle,&header,error) || *error != NOERR3)
    return 0L;

  /* now write the subchunks */

  info = list;
  while(info) {
    temp = write_chunk3(handle,info,error);
    if(!temp || *error != NOERR3)
      break;
    len += temp;
    info = info->next;
  }

  if(chklen != len)
    *error = BADLENGTH3;
  return len;
}


FILE * wopen_pkt3 (char *filename,CHUNK3 *pkt3,int *error) {

  FILE  *handle;
  long   pos;
  USHORT t = 0x0003;

#ifdef MOTOROLA
  t = swap3(t);
#endif
  *error = NOERR3;
  handle = fopen(filename,"r+b");
  if(handle == NULL)
    handle = fopen(filename,"w+b");
  if(handle != NULL) {
    fseek(handle,0L,SEEK_END);
    pos = ftell(handle);
    if(pos)
      /* seek back past EOP marker */
      fseek(handle,pos - (long)(sizeof(USHORT) * sizeof(USHORT)),SEEK_SET);
    else {
      /* write 0x0003 (start of header) */
      fwrite(&t,sizeof(USHORT),1,handle);
      /* write "packet header */
      write_chunk3_list(handle,PKT3,pkt3,error);
    }
  }
  else
    *error = OPENERR3;

  return handle;
}


FILE * wclose_pkt3 (FILE *handle) {

  if(handle != NULL) {
    write_eop3(handle);
    fclose(handle);
  }
  return NULL;
}
