/* 
   Unix SMB/CIFS implementation.
   Samba utility functions
   Copyright (C) Andrew Tridgell 1992-2001
   Copyright (C) Simo Sorce 2001
   
   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 "includes.h"

/* these 2 tables define the unicode case handling.  They are loaded
   at startup either via mmap() or read() from the lib directory */
static void *upcase_table;
static void *lowcase_table;


/*******************************************************************
load the case handling tables
********************************************************************/
static void load_case_tables(void)
{
	int i;
	TALLOC_CTX *mem_ctx;

	mem_ctx = talloc_init("load_case_tables");
	if (!mem_ctx) {
		smb_panic("No memory for case_tables");
	}
	upcase_table = map_file(lib_path(mem_ctx, "upcase.dat"), 0x20000);
	lowcase_table = map_file(lib_path(mem_ctx, "lowcase.dat"), 0x20000);
	talloc_destroy(mem_ctx);
	
	/* we would like Samba to limp along even if these tables are
	   not available */
	if (upcase_table == NULL) {
		DEBUG(1,("creating lame upcase table\n"));
		upcase_table = talloc_named_const(NULL, 0x20000, "upcase_table");
		if (!upcase_table) {
			smb_panic("No memory for upcase tables");
		}
		for (i=0;i<0x10000;i++) {
			SSVAL(upcase_table, i*2, i);
		}
		for (i=0;i<256;i++) {
			SSVAL(upcase_table, i*2, islower(i)?toupper(i):i);
		}
	}

	if (lowcase_table == NULL) {
		DEBUG(1,("creating lame lowcase table\n"));
		lowcase_table = talloc_named_const(NULL, 0x20000, "lowcase_table");
		if (!lowcase_table) {
			smb_panic("No memory for lowcase tables");
		}
		for (i=0;i<0x10000;i++) {
			SSVAL(lowcase_table, i*2, i);
		}
		for (i=0;i<256;i++) {
			SSVAL(lowcase_table, i*2, isupper(i)?tolower(i):i);
		}
	}
}

/*******************************************************************
 Convert a codepoint_t to upper case.
********************************************************************/
codepoint_t toupper_w(codepoint_t val)
{
	if (val & 0xFFFF0000) {
		return val;
	}
	if (val < 128) {
		return toupper(val);
	}
	if (upcase_table == NULL) {
		load_case_tables();
	}
	return SVAL(upcase_table, val*2);
}

/*******************************************************************
 Convert a codepoint_t to lower case.
********************************************************************/
codepoint_t tolower_w(codepoint_t val)
{
	if (val & 0xFFFF0000) {
		return val;
	}
	if (val < 128) {
		return tolower(val);
	}
	if (lowcase_table == NULL) {
		load_case_tables();
	}
	return SVAL(lowcase_table, val*2);
}

/*******************************************************************
return the number of bytes occupied by a buffer in CH_UTF16 format
the result includes the null termination
********************************************************************/
size_t utf16_len(const void *buf)
{
	size_t len;

	for (len = 0; SVAL(buf,len); len += 2) ;

	return len + 2;
}

/*******************************************************************
return the number of bytes occupied by a buffer in CH_UTF16 format
the result includes the null termination
limited by 'n' bytes
********************************************************************/
size_t utf16_len_n(const void *src, size_t n)
{
	size_t len;

	for (len = 0; (len+2 < n) && SVAL(src, len); len += 2) ;

	if (len+2 <= n) {
		len += 2;
	}

	return len;
}


size_t ucs2_align(const void *base_ptr, const void *p, int flags)
{
	if (flags & (STR_NOALIGN|STR_ASCII))
		return 0;
	return PTR_DIFF(p, base_ptr) & 1;
}

/*
  compare two codepoints case insensitively
*/
int codepoint_cmpi(codepoint_t c1, codepoint_t c2)
{
	if (c1 == c2 ||
	    toupper_w(c1) == toupper_w(c2)) {
		return 0;
	}
	return c1 - c2;
}