// $Id: BroString.cc,v 1.19 2003/06/15 19:15:00 vern Exp $
//
// Copyright (c) 1997, 1998, 1999, 2002, 2003
//      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 <ctype.h>

#include "BroString.h"

// This constructor forces the user to specify arg_final_NUL.  When str
// is a *normal* NUL-terminated string, make arg_n == strlen(str) and
// arg_final_NUL == 1; when str is a sequence of n bytes, make
// arg_final_NUL == 0.

BroString::BroString(int arg_final_NUL, byte_vec str, int arg_n)
	{
	b = str;
	n = arg_n;
	final_NUL = arg_final_NUL;
	use_free_to_delete = 0;
	}

BroString::BroString(const u_char* str, int arg_n, int add_NUL)
	{
	n = arg_n;
	b = new u_char[add_NUL ? n + 1 : n];
	memcpy(b, str, n);

	if ( add_NUL )
		{
		b[n] = 0;
		final_NUL = 1;
		}
	else
		final_NUL = 0;

	use_free_to_delete = 0;
	}

BroString::BroString(const char* str)
	{
	n = strlen(str);
	b = new u_char[n+1];
	memcpy(b, str, n+1);
	final_NUL = 1;
	use_free_to_delete = 0;
	}

const char* BroString::CheckString() const
	{
	if ( n == 0 )
		return "";

	if ( memchr(b, '\0', n + final_NUL) != &b[n] )
		{
		// Either an embedded NUL, or no final NUL.
		char* exp_s = ExpandedString();
		if ( b[n-1] != '\0' )
			run_time("string without NUL terminator: \"%s\"", exp_s);
		else
			run_time("string with embedded NUL: \"%s\"", exp_s);

		delete [] exp_s;
		return "<string-with-NUL>";
		}

	return (const char*) b;
	}

char* BroString::ExpandedString(int format) const
	{
	// Maxmimum character expansion is as \xHH, so a factor of 4.
	char* s = new char[n*4 + 1];	// +1 is for final '\0'
	char* sp = s;
	for ( int i = 0; i < n; ++i )
		switch ( b[i] ) {
		case '\0':
			*sp++ = '\\';
			*sp++ = '0';
			break;

		default:
			if ( b[i] == '\x7f' && format == EXPANDED_STRING )
				{
				*sp++ = '^';
				*sp++ = '?';
				}
			
			else if ( b[i] <= 26 && format == EXPANDED_STRING )
				{
				*sp++ = '^';
				*sp++ = b[i] + 'A' - 1;
				}

			else if ( ( b[i] == '\\' || b[i] == '"' ) && format == BRO_STRING_LITERAL )
				{
				*sp++ = '\\';
				*sp++ = b[i];
				}

			else if ( b[i] >= ' ' && b[i] < 127 )
				{
				*sp++ = b[i];
				}
			
			else
				{
				// otherwise ...

				char hex_fmt[16];

				*sp++ = '\\';
				*sp++ = 'x';

				sprintf(hex_fmt, "%02x", b[i]);

				*sp++ = hex_fmt[0];
				*sp++ = hex_fmt[1];
				}
			break;
		}

	*sp = '\0';	// NUL-terminate.

	return s;
	}

void BroString::ToUpper()
	{
	for ( int i = 0; i < n; ++i )
		if ( islower(b[i]) )
			b[i] = toupper(b[i]);
	}

int Bstr_eq(const BroString* s1, const BroString* s2)
	{
	if ( s1->Len() != s2->Len() )
		return 0;

	return memcmp(s1->Bytes(), s2->Bytes(), s1->Len()) == 0;
	}

int Bstr_cmp(const BroString* s1, const BroString* s2)
	{
	int n = min(s1->Len(), s2->Len());
	int cmp = memcmp(s1->Bytes(), s2->Bytes(), n);

	if ( cmp || s1->Len() == s2->Len() )
		return cmp;

	// Compared equal, but one was shorter than the other.  Treat
	// it as less than the other.
	if ( s1->Len() < s2->Len() )
		return -1;
	else
		return 1;
	}

BroString* concatenate(std::vector<data_chunk_t>& v)
	{
	int n = v.size();
	int len = 0;
	int i;
	for ( i = 0; i < n; ++i )
		len += v[i].length;

	char* data = new char[len+1];

	char* b = data;
	for ( i = 0; i < n; ++i )
		{
		memcpy(b, v[i].data, v[i].length);
		b += v[i].length;
		}

	*b = '\0';

	return new BroString(1, (byte_vec) data, len);
	}

BroString* concatenate(std::vector<BroString*>& v)
	{
	int n = v.size();
	int len = 0;
	int i;
	for ( i = 0; i < n; ++i )
		len += v[i]->Len();

	char* data = new char[len+1];

	char* b = data;
	for ( i = 0; i < n; ++i )
		{
		memcpy(b, v[i]->Bytes(), v[i]->Len());
		b += v[i]->Len();
		}
	*b = '\0';

	return new BroString(1, (byte_vec) data, len);
	}

void delete_strings(std::vector<BroString*>& v)
	{
	for ( unsigned int i = 0; i < v.size(); ++i )
		delete v[i];
	v.clear();
	}

