// $Id: Desc.cc,v 1.18 2003/06/15 19:16:09 vern Exp $
//
// Copyright (c) 1995, 1996, 1997, 1998, 1999, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <stdlib.h>

#include "Desc.h"

#define DEFAULT_SIZE 128
#define SLOP 10

ODesc::ODesc(desc_type t, FILE* arg_f)
	{
	type = t;
	style = STANDARD_STYLE;
	f = arg_f;

	if ( f == 0 )
		{
		size = DEFAULT_SIZE;
		base = safe_malloc(size);
		((char*) base)[0] = '\0';

		offset = 0;

		if ( ! base )
			OutOfMemory();
		}
	else
		{
		offset = size = 0;
		base = 0;
		}

	indent_level = 0;
	is_short = 0;
	do_flush = 1;
	}

ODesc::~ODesc()
	{
	if ( f )
		{
		if ( do_flush )
			fflush(f);
		}
	else if ( base )
		free(base);
	}

void ODesc::PushIndent()
	{
	++indent_level;
	NL();
	}

void ODesc::PopIndent()
	{
	if ( --indent_level < 0 )
		internal_error("ODesc::PopIndent underflow");
	NL();
	}

void ODesc::Add(const char* s, int do_indent)
	{
	unsigned int n = strlen(s);

	if ( do_indent && IsReadable() && offset > 0 &&
	     ((const char*) base)[offset - 1] == '\n' )
		Indent();

	if ( IsBinary() )
		AddBytes(s, n+1);
	else
		AddBytes(s, n);
	}

void ODesc::Add(int i)
	{
	if ( IsBinary() )
		AddBytes(&i, sizeof(i));
	else
		{
		char tmp[256];
		sprintf(tmp, "%d", i);
		Add(tmp);
		}
	}

void ODesc::Add(uint32 u)
	{
	if ( IsBinary() )
		AddBytes(&u, sizeof(u));
	else
		{
		char tmp[256];
		sprintf(tmp, "%u", u);
		Add(tmp);
		}
	}

void ODesc::Add(double d)
	{
	if ( IsBinary() )
		AddBytes(&d, sizeof(d));
	else
		{
		char tmp[256];
		sprintf(tmp, IsReadable() ? "%.15g" : "%.17g", d);
		Add(tmp);

		if ( d == double(int(d)) )
			// disambiguate from integer
			Add(".0");
		}
	}

void ODesc::AddCS(const char* s)
	{
	int n = strlen(s);
	Add(n);
	if ( ! IsBinary() )
		Add(" ");
	Add(s);
	}

void ODesc::AddBytes(const BroString* s)
	{
	if ( IsReadable() )
		{
		char* exp_str = s->ExpandedString();
		Add(exp_str);
		delete [] exp_str;
		}
	else
		{
		Add(s->Len());
		if ( ! IsBinary() )
			Add(" ");
		AddBytes(s->Bytes(), s->Len());
		}
	}

void ODesc::Indent()
	{
	for ( int i = 0; i < indent_level; ++i )
		Add("\t", 0);
	}

void ODesc::AddBytes(const void* bytes, unsigned int n)
	{
	if ( f )
		{
		if ( n == 0 )
			return;

		if ( ! fwrite((const char*) bytes, n, 1, f) )
			internal_error("ODesc::AddBytes: fwrite failed");
		}

	else
		{
		Grow(n);

		// The following contortions are necessary to keep Centerline
		// happy - it objects to &base[offset] on grounds that it
		// dereferences a void*.  Similar complaint for base+offset.
		memcpy((void*) &((char*) base)[offset], bytes, n);
		offset += n;

		((char*) base)[offset] = '\0';	// ensure that always NUL-term.
		}
	}

void ODesc::Grow(unsigned int n)
	{
	while ( offset + n + SLOP >= size )
		{
		size *= 2;
		base = safe_realloc(base, size);
		if ( ! base )
			OutOfMemory();
		}
	}

void ODesc::OutOfMemory()
	{
	internal_error("out of memory");
	}
