#include <stdlib.h>
#include <stdio.h>

#include "postgres.h"
#include "libpq/pqcomm.h"

#ifdef		  HAVE_ENDIAN_H
#include	<endian.h>
#endif


/* --------------------------------------------------------------------- */
/* These definitions for ntoh/hton are the other way around from the
 *	default system definitions, so we roll our own here.
 */

#ifndef BYTE_ORDER
#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN
#endif

#if BYTE_ORDER == LITTLE_ENDIAN
#define ntoh_s(n) n
#define ntoh_l(n) n
#define hton_s(n) n
#define hton_l(n) n
#else							/* BYTE_ORDER != LITTLE_ENDIAN */
#if BYTE_ORDER == BIG_ENDIAN
#define ntoh_s(n) (u_short)(((u_char *)&n)[1] << 8 \
							  | ((u_char *)&n)[0])
#define ntoh_l(n) (uint32) (((u_char *)&n)[3] << 24 \
							  | ((u_char *)&n)[2] << 16 \
							  | ((u_char *)&n)[1] <<  8 \
							  | ((u_char *)&n)[0])
#define hton_s(n) (ntoh_s(n))
#define hton_l(n) (ntoh_l(n))
#else
/* BYTE_ORDER != BIG_ENDIAN */
#if BYTE_ORDER == PDP_ENDIAN
#error PDP_ENDIAN macros not written yet
#else
/* BYTE_ORDER !=  anything known */
#error BYTE_ORDER not defined as anything understood
#endif							/* BYTE_ORDER == PDP_ENDIAN */
#endif							/* BYTE_ORDER == BIG_ENDIAN */
#endif							/* BYTE_ORDER == LITTLE_ENDIAN */

/* --------------------------------------------------------------------- */
int
pqPutShort(int integer, FILE *f)
{
	int			retval = 0;
	u_short		n,
				s;

	s = integer;
	n = hton_s(s);
	if (fwrite(&n, sizeof(u_short), 1, f) != 1)
		retval = EOF;

	return retval;
}

/* --------------------------------------------------------------------- */
int
pqPutLong(int integer, FILE *f)
{
	int			retval = 0;
	uint32		n;

	n = hton_l(integer);
	if (fwrite(&n, sizeof(uint32), 1, f) != 1)
		retval = EOF;

	return retval;
}

/* --------------------------------------------------------------------- */
int
pqGetShort(int *result, FILE *f)
{
	int			retval = 0;
	u_short		n;

	if (fread(&n, sizeof(u_short), 1, f) != 1)
		retval = EOF;

	*result = ntoh_s(n);
	return retval;
}

/* --------------------------------------------------------------------- */
int
pqGetLong(int *result, FILE *f)
{
	int			retval = 0;
	uint32		n;

	if (fread(&n, sizeof(uint32), 1, f) != 1)
		retval = EOF;

	*result = ntoh_l(n);
	return retval;
}

/* --------------------------------------------------------------------- */
/* pqGetNBytes: Read a chunk of exactly len bytes in buffer s.
		Return 0 if ok.
*/
int
pqGetNBytes(char *s, size_t len, FILE *f)
{
	int			cnt;

	if (f == NULL)
		return EOF;

	cnt = fread(s, 1, len, f);
	s[cnt] = '\0';
	/* mjl: actually needs up to len+1 bytes, is this okay? XXX */

	return (cnt == len) ? 0 : EOF;
}

/* --------------------------------------------------------------------- */
int
pqPutNBytes(const char *s, size_t len, FILE *f)
{
	if (f == NULL)
		return 0;

	if (fwrite(s, 1, len, f) != len)
		return EOF;

	return 0;
}

/* --------------------------------------------------------------------- */
int
pqGetString(char *s, size_t len, FILE *f)
{
	int			c;

	if (f == NULL)
		return EOF;

	while (len-- && (c = getc(f)) != EOF && c)
		*s++ = c;
	*s = '\0';
	/* mjl: actually needs up to len+1 bytes, is this okay? XXX */

	return 0;
}

/* --------------------------------------------------------------------- */
int
pqPutString(const char *s, FILE *f)
{
	if (f == NULL)
		return 0;

	if (fputs(s, f) == EOF)
		return EOF;

	fputc('\0', f);				/* important to send an ending \0 since
								 * backend expects it */
	fflush(f);

	return 0;
}

/* --------------------------------------------------------------------- */
int
pqGetByte(FILE *f)
{
	return getc(f);
}

/* --------------------------------------------------------------------- */
int
pqPutByte(int c, FILE *f)
{
	if (!f)
		return 0;

	return (putc(c, f) == c) ? 0 : EOF;
}

/* --------------------------------------------------------------------- */
