/*
 *  smoke16/libObj/src/types.c - (de-)serialize basic SMOKE-16 types
 *  $Revision: 1.8 $
 *  Copyright 2000, Benjamin Sittler
 *
 *  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 FITNESS 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.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <smoke16/libObj.h>

/* portable characters */

#define C(x) CTOI(x)

/* this table contains the portable character encoding */
static const int portable[] = {
  C('\0'), EOF    , EOF    , EOF    , EOF    , EOF    , EOF    , C('\a'),
  C('\b'), C('\t'), C('\n'), C('\v'), C('\f'), C('\r'), EOF    , EOF    ,
  EOF    , EOF    , EOF    , EOF    , EOF    , EOF    , EOF    , EOF    ,
  EOF    , EOF    , EOF    , EOF    , EOF    , EOF    , EOF    , EOF    ,
  C(' ') , C('!') , C('\"'), C('#') , C('$') , C('%') , C('&') , C('\''),
  C('(') , C(')') , C('*') , C('+') , C(',') , C('-') , C('.') , C('/') ,
  C('0') , C('1') , C('2') , C('3') , C('4') , C('5') , C('6') , C('7') ,
  C('8') , C('9') , C(':') , C(';') , C('<') , C('=') , C('>') , C('\?'),
  C('@') , C('A') , C('B') , C('C') , C('D') , C('E') , C('F') , C('G') ,
  C('H') , C('I') , C('J') , C('K') , C('L') , C('M') , C('N') , C('O') ,
  C('P') , C('Q') , C('R') , C('S') , C('T') , C('U') , C('V') , C('W') ,
  C('X') , C('Y') , C('Z') , C('[') , C('\\'), C(']') , C('^') , C('_') ,
  C('`') , C('a') , C('b') , C('c') , C('d') , C('e') , C('f') , C('g') ,
  C('h') , C('i') , C('j') , C('k') , C('l') , C('m') , C('n') , C('o') ,
  C('p') , C('q') , C('r') , C('s') , C('t') , C('u') , C('v') , C('w') ,
  C('x') , C('y') , C('z') , C('{') , C('|') , C('}') , C('~')
};

int
htonc(int c) {
  int i;

  for (i = sizeof(portable)/sizeof(*portable); i--;)
    if (portable[i] == c)
      return i;
  return -1;
}

int
ntohc(int c) {
  if ((c >= 0) && (((size_t) c) < sizeof(portable)/sizeof(*portable)))
    c = portable[c];
  else
    c = EOF;
  return c;
}

size_t
fget_as(char *n, size_t nmemb, FILE *stream)
{
  size_t count;
  int c;

  for (count = 0; count < nmemb; count ++)
    if (((c = getc(stream)) == EOF) ||
	((c = ntohc(0xFFU & (u8)c)) == EOF))
      break;
    else
      n[count] = (char) c;
  return count;
}

size_t
fput_as(char *n, size_t nmemb, FILE *stream)
{
  size_t count;
  int c;

  for (count = 0; count < nmemb; count ++)
    if (((c = htonc(CTOI(n[count]))) == -1) ||
	(putc(c, stream) == EOF))
      break;
  return count;
}

/* unsigned bytes */

size_t
fget_u8(u8 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  int c;

  for (count = 0; count < nmemb; count ++)
    if ((c = getc(stream)) == EOF)
      break;
    else
      n[count] = 0xFFU & (u8)c;
  return count;
}

size_t
fput_u8(u8 *n, size_t nmemb, FILE *stream)
{
  size_t count;

  for (count = 0; count < nmemb; count ++)
    if (putc(n[count] & 0xFFU, stream) == EOF)
      break;
  return count;
}

/* signed bytes */

s8 u8_to_s8(u8 uc)
{
    s8 sc;

    uc &= 0xFFU;
    if (uc & 0x80U)
        sc = -(s8) (1U + (uc ^ 0xFFU));
    else
        sc = (s8) uc;
    return sc;
}

size_t
fget_s8(s8 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  int c;
  u8 uc;

  for (count = 0; count < nmemb; count ++)
    if ((c = getc(stream)) == EOF)
      break;
    else {
      uc = 0xFFU & (u8)c;
      n[count] = u8_to_s8(uc);
    }
  return count;
}

size_t
fput_s8(s8 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  int c;
  u8 uc;

  for (count = 0; count < nmemb; count ++) {
    c = n[count];
    if (c < 0)
      uc = 1U + ~(u8)(-c);
    else
      uc = (u8)c;
    if (putc(uc & 0xFFU, stream) == EOF)
      break;
  }
  return count;
}

/* unsigned two-byte numbers */

size_t
fget_u16(u16 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u8 buf[2];

  for (count = 0; count < nmemb; count ++) {
    if (fget_u8(buf, 2, stream) < 2)
      break;
    n[count] = ((((u16)buf[0]) << 8) | (u16)buf[1]) & 0xFFFFU;
  }
  return count;
}

size_t
fput_u16(u16 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u8 buf[2];
  u16 uc;

  for (count = 0; count < nmemb; count ++) {
    uc = n[count];
    buf[0] = (u8)((uc >> 8) & 0xFFU);
    buf[1] = (u8)(uc & 0xFFU);
    if (fput_u8(buf, 2, stream) < 2)
      break;
  }
  return count;
}

/* signed two-byte numbers */

s16 u16_to_s16(u16 uc)
{
    s16 sc;

    uc &= 0xFFFFU;
    if (uc & 0x8000U)
        sc = -(s16) (1U + (uc ^ 0xFFFFU));
    else
        sc = (s16) uc;
    return sc;
}

size_t
fget_s16(s16 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u8 buf[2];
  u16 uc;

  for (count = 0; count < nmemb; count ++) {
    if (fget_u8(buf, 2, stream) < 2)
      break;
    uc = ((((u16)buf[0]) << 8) | (u16)buf[1]) & 0xFFFFU;
    n[count] = u16_to_s16(uc);
  }
  return count;
}

size_t
fput_s16(s16 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u8 buf[2];
  u16 uc;
  s16 c;

  for (count = 0; count < nmemb; count ++) {
    c = n[count];
    if (c < 0)
      uc = 1U + ~(u16)(-c);
    else
      uc = (u16)c;
    buf[0] = (u8)((uc >> 8) & 0xFFU);
    buf[1] = (u8)(uc & 0xFFU);
    if (fput_u8(buf, 2, stream) < 2)
      break;
  }
  return count;
}

/* unsigned four-byte numbers */

size_t
fget_u32(u32 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u16 buf[2];

  for (count = 0; count < nmemb; count ++) {
    if (fget_u16(buf, 2, stream) < 2)
      break;
    n[count] = ((((u32)buf[0]) << 16) | (u32)buf[1]) & 0xFFFFFFFFUL;
  }
  return count;
}

size_t
fput_u32(u32 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u16 buf[2];
  u32 uc;

  for (count = 0; count < nmemb; count ++) {
    uc = n[count];
    buf[0] = (u16)((uc >> 16) & 0xFFFFUL);
    buf[1] = (u16)(uc & 0xFFFFUL);
    if (fput_u16(buf, 2, stream) < 2)
      break;
  }
  return count;
}

/* signed four-byte numbers */

s32 u32_to_s32(u32 uc)
{
    s32 sc;

    uc &= 0xFFFFFFFFUL;
    if (uc & 0x80000000UL)
        sc = -(s32) (1UL + (uc ^ 0xFFFFFFFFUL));
    else
        sc = (s32) uc;
    return sc;
}

size_t
fget_s32(s32 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u16 buf[2];
  u32 uc;

  for (count = 0; count < nmemb; count ++) {
    if (fget_u16(buf, 2, stream) < 2)
      break;
    uc = ((((u32)buf[0]) << 16) | (u32)buf[1]) & 0xFFFFFFFFUL;
    n[count] = u32_to_s32(uc);
  }
  return count;
}

size_t
fput_s32(s32 *n, size_t nmemb, FILE *stream)
{
  size_t count;
  u16 buf[2];
  u32 uc;
  s32 c;

  for (count = 0; count < nmemb; count ++) {
    c = n[count];
    if (c < 0)
      uc = 1UL + ~(u32)(-c);
    else
      uc = (u32)c;
    buf[0] = (u16)((uc >> 16) & 0xFFFFUL);
    buf[1] = (u16)(uc & 0xFFFFUL);
    if (fput_u16(buf, 2, stream) < 2)
      break;
  }
  return count;
}

