#include "newhash.h"
/*	Update history:
	1.10	chrisp added encoding for com*, con, etc...
	1.9	chrisp Added a nicer hashing than level 3 but similar.

	1.8	Chrisp added double letter hashing - level 4
			/aa/bb
	1.7	09/12/2000	DHGB
		Changed all local functions and macros to have prefix _nh_ to avoid external
		conflicts.  The following functions comprise the API and as such are left alone:

			int hash_new(char *dir, char *xto, char *invalid, int level, int append, char *userpart);
			char *encode_invalid(char *dir, char *invalid);
			void encode_last(char *path, char *invalid);
			char *decode_invalid(char *username, char *invalid);

	1.6 04/12/2000	DHGB
		Stopped encode_last() crashing if passed a string without a path separator

	1.5 04/12/2000	DHGB
		Stopped hashing level 0 appending path separators when already present

	1.4 01/12/2000	DHGB
		Added lowercasing of hashed directory component for a second time - original may have been overwritten

	1.3 29/11/2000	matt
	   	Moved default invalid chars into a function, and call it from encode_invalid() as well as hash_new

	1.2	29/11/2000	chrisp
		Changed escape character to ',' for dmail 2.9g

	1.1	23/11/2000	chrisp
		Added default invalid characters and minimized list for unix
		Changed encoding character to '255' (test on nt)
		Added function to unencode file name to get username.
		Ask ralph if there some functions in dpop which rely drop file being username?
		Made ncpy/ncat local functions to avoid conflicts
		Made _NH_BFSZ NEWHASH_BFSZ and changed to 499 so ncpy won't corrupt data in dpop

 *	hash_new hashes the directory 'dir', using the username 'xto', and the
 *	hashing level 'level'.  It strips all characters in 'invalid' from the
 *	username when hashing.
 *
 *	hash_new modifies 'dir' so that it contains the original 'dir', the hashed
 *	component, and the username.  The username will have had all 'invalid'
 *	characters it contains encoded.  The username will only be appended if
 *	'append' is TRUE.
 *
 *	'vdomain_sep' is the separator between vdomain prefix and username.  It is
 *	used if 'drop_prefix' is TRUE.
 *
 *	hash_new also puts the encoded username in string 'userpart', if 'userpart'
 *	is not NULL. (i.e. it is safe for 'userpart' to be NULL).
 *
 *	'gvdms' must be set to TRUE if virtual domains exist.
 *
 *	hash_new returns TRUE if it was able to create a valid hash name, and FALSE
 *	otherwise.
 */

/* snprintf() has an underscore prefix on Win32 platforms */
#ifdef WIN32
#define snprintf _snprintf
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef STANDALONE
#	ifdef WIN32
#		define SEP '\\'
#	else
#		define SEP '/'
#	endif
	extern int strcmpnc(char *s1, char *s2);
	extern int strncmpnc(const char *s1, const char *s2, int n);
#else
#	include "lib.h"
#	include "debug.h"
#endif
#include "lprintf.h"

/*	local versions of string routines, in case they are not available */
char *_nh_ncpy(char *dst,const char *src, int len);
char *_nh_ncat(char *dst,const char *src, int len);
char *_nh_strlwr(char *s);
char *_nh_hash_level4(char *username, char *tmp);
char *_nh_hash_level6(char *username, char *tmp);
char *_nh_hash_level5(char *username, char *tmp);

/*	If 'invalid' is NULL or empty, _nh_get_invalid_chars() returns a pointer 
 *	to a set of operating-system specific default invalid characters.
 *
 *	Otherwise, _nh_get_invalid_chars() returns 'invalid'.
 */
char *_nh_get_invalid_chars(char *invalid);

/*	hash_level1 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level1(char *s, char *tmp);

/*	hash_level2 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level2(char *s, char *tmp);

/*	hash_level3 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level3(char *s, char *tmp);

/*	strip_invalid strips any invalid characters, and '.', from 'username'.
 *  It returns a pointer to a static string containing the stripped user name.
 */
char *_nh_strip_invalid(char *dir, char *invalid, char *tmp);

/*	valid_char returns TRUE if 'ch' is valid, and FALSE otherwise. 'invalid'
 *	contains a base set of invalid characters.
 */
int _nh_valid_char(char ch, char *invalid);

/*	If 'invalid' is NULL or empty, _nh_get_invalid_chars() returns a pointer 
 *	to a set of operating-system specific default invalid characters.
 *
 *	Otherwise, _nh_get_invalid_chars() returns 'invalid'.
 */
char *_nh_get_invalid_chars(char *invalid)
{
	/* Hard coded default added by chrisp and moved to a function by matt*/
#ifdef WIN32
	if ( (invalid==NULL) || (lib_strlen(invalid) == 0) ) return "\\/:*?\"<>|";
#else
	if ( (invalid==NULL) || (lib_strlen(invalid) == 0) ) return "/";
#endif
	return invalid;
}

/*	hash_new hashes the directory 'dir', using the username 'xto', and the 
 *	hashing level 'level'.  It strips all characters in 'invalid' from the
 *	username when hashing.  
 *
 *	hash_new modifies 'dir' so that it contains the original 'dir', the hashed
 *	component, and the encoded username.  The encoded username will only be
 *	appended if 'append' is TRUE.
 *
 *	hash_new also puts the encoded username in string 'userpart', if 'userpart'
 *	is not NULL. (i.e. it is safe for 'userpart' to be NULL).
 *
 *	hash_new returns TRUE if it was able to create a valid hash name, and FALSE
 *	otherwise.
 */
char * _nh_addsep(char *dir)
{
	char cbuf[_NH_BFSZ];
	int n = lib_strlen(dir);
	cbuf[1]=0;
	if (n<1) return dir;
	if ( (dir[n-1]!='\\') && (dir[n-1]!='/') ) {
		cbuf[0] = NH_SEP;
		_nh_ncat(dir, cbuf, _NH_BFSZ-1);
	}
	return dir;
}

int hash_mask(char *dir, int level, int append)
{
	char tmp[_NH_BFSZ];

	switch(level) {
	case 6:
		lprintf(tmp,_NH_BFSZ,"?%c%c??%c%c??",SEP,SEP,SEP,SEP);
		break;
	case 5:
		lprintf(tmp,_NH_BFSZ,"??%c%c??",SEP,SEP);
		break;
	case 4:
		lprintf(tmp,_NH_BFSZ,"??%c%c??",SEP,SEP);
		break;
	case 3:
		lprintf(tmp,_NH_BFSZ,"n_??%c%c??",SEP,SEP);
		break;
	case 2:
		lprintf(tmp,_NH_BFSZ,"?%c%c?",SEP,SEP);
		break;
	case 1:
		lprintf(tmp,_NH_BFSZ,"?");
		break;
	case 0: default:
		break;
	}

	_nh_addsep(dir);

	if (append) lpcat(dir,_NH_BFSZ,"%s%c%c*",tmp,SEP,SEP);
	else _nh_ncat(dir,tmp,_NH_BFSZ-1);

	return TRUE;
}

int hash_new(char *dir, char *xto, char *invalid, int level, int append,
			 char *userpart)
{
	int safe1=1;
	char xto_tohash[_NH_BFSZ];
	char xto_toadd[_NH_BFSZ];
	char tmp[_NH_BFSZ];
	char cbuf[_NH_BFSZ];
	int safe2=1;

	/* Hard coded default added by chrisp */
	invalid = _nh_get_invalid_chars(invalid);

	/* ensure we only copy one character at a time */
	cbuf[1]=0;

	/* init */
	xto_tohash[0] = xto_toadd[0] = 0;

	/*	Some usernames may contain invalid characters, including '.' for the
	 *	hashing of the directory.  If this is the case they need to be stripped
	 *	before hashing.  xto_tohash() also needs vdomain removed from it.
	 */
	_nh_ncpy(xto_tohash, _nh_strip_invalid(xto, invalid,tmp), _NH_BFSZ-1);

	/* we always ignore case when hashing */
	_nh_strlwr(xto_tohash);

	/*	Create new username to append onto end of hashed dir.  Note that all
	 *	invalid characters in the form '\xx' are encoded in the form '$xx'.
	 *	The character '$' is encoded as '$18'. '.' at the start or end of a
	 *	username is considered invalid if not on a UNIX system.  vd1() is used
	 *	to make vdomain trimming dependant upon drop_prefix setting.
	 */
	_nh_ncpy(xto_toadd, encode_invalid(xto, invalid,tmp), _NH_BFSZ-1);

	/* reject a null/empty dir or user */
	if ( (dir==NULL)
			|| (*dir==0)
			|| (xto==NULL)
			|| (*xto==0)
			|| (xto_tohash==NULL)
			|| (*xto_tohash==0)
			|| (xto_toadd==NULL)
			|| (*xto_toadd==0) ) {
		dir[0] = 0;
		return FALSE;
	}

	/* if dir has no trailing separator, add it */
	_nh_addsep(dir);

	/*	Hash according to 'level' and, except for level 3, append the encoded
	 *	username onto the end of the hash result.
	 */
	switch (level)
	{

	case (6):
		_nh_ncat(dir, _nh_hash_level6(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;
	case (5):
		_nh_ncat(dir, _nh_hash_level5(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;
	case (4):
		_nh_ncat(dir, _nh_hash_level4(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;
	case (3):
		_nh_ncat(dir, _nh_hash_level3(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;

	case (2):
		_nh_ncat(dir, _nh_hash_level2(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;

	case (1):
		_nh_ncat(dir, _nh_hash_level1(xto_tohash,tmp), _NH_BFSZ-1);
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;

	case (0):
		/* make certain that we don't insert a '\' or '/' where one exists */
		if (append==TRUE) {
			_nh_addsep(dir);
			_nh_ncat(dir, xto_toadd, _NH_BFSZ-1);
		}
		break;

	/* do not hash if an invalid hashing level is requested */
	default:
		dir[0] = 0;
		return FALSE;
	}

	/* if userpart is not NULL, copy encoded username into it */
	if (userpart != NULL) {
		_nh_ncpy(userpart, xto_toadd, _NH_BFSZ-1);
	}

	return TRUE;
}

/*	_nh_valid_char returns TRUE if 'ch' is valid, and FALSE otherwise. 'invalid'
 *	contains a base set of invalid characters.
 */
int _nh_valid_char(char ch, char *invalid)
{
	ch = tolower(ch);

	if (strchr(invalid, ch)==NULL) {
		return TRUE;
	} else {
		return FALSE;
	}
}

/*	hash_level3 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level3(char *username,char *buffer)
{
	unsigned char total1;			/* total of ASCII values */
	unsigned char total2;			/* total of ASCII values */
	char *p;						/* pointer for duplication of username */
	int i=0;

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	/* shift contents of username right by 9 characters into buffer */
	for (p=&buffer[8]; (*p++=*username++); i++) {
		if (i>=(_NH_BFSZ-10)) {
			*p=0;
			break;
		}
	}

	/* total every odd character into total1, and every even into total2 */
	total1 = total2 = 0;
	for (p=&buffer[8]; *p ; p++){
		total1 += *p++;
		total2 += *p;
		if (!*p) break;
	}

	p = buffer;

	/* calculate hashed directory and set before username */
	buffer[0] = 'n';
	buffer[1] = '_';
	buffer[2] = 'a' + total1/10;
	buffer[3] = '0' + total1%10;
	buffer[4] = NH_SEP;
	buffer[5] = 'a' + total2/10;
	buffer[6] = '0' + total2%10;
	buffer[7] = 0;

	_nh_strlwr(buffer);
	return buffer;
}
/*	hash_level5 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level6(char *username, char *buffer)
{
	char user[_NH_BFSZ];
	char bf2[_NH_BFSZ];
	unsigned char total1;			/* total of ASCII values */
	unsigned char total2;			/* total of ASCII values */
	char *p;						/* pointer for duplication of username */
	int i=0;
	int diskn;

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	_nh_ncpy(user,username,_NH_BFSZ-1);
	_nh_strlwr(user);
	/* total every odd character into total1, and every even into total2 */
	total1 = total2 = 0;
	for (p=user; *p ; p++){
		total1 += *p++;
		total2 += *p;
		if (!*p) break;
	}

	p = buffer;

	/* calculate hashed directory and set before username */
	buffer[0] = 'a' + total1/10;
	buffer[1] = 'a' + total1%10;
	buffer[2] = NH_SEP;
	buffer[3] = 'a' + total2/10;
	buffer[4] = 'a' + total2%10;
	buffer[5] = 0;

	diskn = (total1+total2) % 10;
	if (diskn<0) diskn = -diskn;
	_nh_ncpy(bf2+2,buffer,_NH_BFSZ-10);
	bf2[0] = diskn + '0';
	bf2[1] = NH_SEP;
	_nh_ncpy(buffer,bf2,_NH_BFSZ-1);
	_nh_strlwr(buffer);
	return buffer;
}
char *_nh_hash_level5(char *username, char *buffer)
{
	char user[_NH_BFSZ];
	unsigned char total1;			/* total of ASCII values */
	unsigned char total2;			/* total of ASCII values */
	char *p;						/* pointer for duplication of username */
	int i=0;

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	_nh_ncpy(user,username,_NH_BFSZ-1);
	_nh_strlwr(user);
	/* total every odd character into total1, and every even into total2 */
	total1 = total2 = 0;
	for (p=user; *p ; p++){
		total1 += *p++;
		total2 += *p;
		if (!*p) break;
	}

	p = buffer;

	/* calculate hashed directory and set before username */
	buffer[0] = 'a' + total1/10;
	buffer[1] = 'a' + total1%10;
	buffer[2] = NH_SEP;
	buffer[3] = 'a' + total2/10;
	buffer[4] = 'a' + total2%10;
	buffer[5] = 0;

	_nh_strlwr(buffer);
	return buffer;
}

/*	hash_level2 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level2(char *username, char *buffer)
{

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	/* first level of hashed dir = first char of 'username ' */
	buffer[0] = username[0];
	buffer[1] = NH_SEP;

	/*	Second level of hashed dir = second char of username, or first char if
	 *	second char is undefined.
	 */
	if (username[1] == 0) {
		buffer[2] = username[0];
	} else {
		buffer[2] = username[1];
	}

	buffer[3] = 0;

	_nh_strlwr(buffer);
	return buffer;
}
/*	hash_level4 returns a pointer to a static string containing the
 *	results of hashing 'username'.  fred -->  /fr/ed/fred  ab -> /ab/__/ab
 */
char *_nh_hash_level4(char *username, char *buffer)
{
	char tmp[_NH_BFSZ];

	_nh_ncpy(tmp,username,_NH_BFSZ-20);
	strcat(tmp,"____");
	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	/* first level of hashed dir = first two char of 'username ' */
	buffer[0] = username[0];
	buffer[1] = username[1];
	buffer[2] = NH_SEP;
	buffer[3] = username[2];
	buffer[4] = username[3];
	buffer[5] = NH_SEP;
	buffer[6] = 0;
	_nh_strlwr(buffer);
	return buffer;
}

/*	hash_level1 returns a pointer to a static string containing the
 *	results of hashing 'username'.
 */
char *_nh_hash_level1(char *username, char *buffer)
{
	long seed = 0;					/* total of ASCII values */
	int i;							/* position in 'username */

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	/* total the ASCII values of the first 4 available chars in 'username' */
	for (i=0; i<4; i++){
		seed += username[i];
		if (!username[i]) break;
	}

	/* create first level of hash dir based upon seed */
	buffer[0] = (char)((seed % 26) + 'a');
	buffer[1] = 0;

	_nh_strlwr(buffer);
	return buffer;
}

/*	strip_invalid strips any invalid characters, and '.', from 'username'.
 *  It returns a pointer to a static string containing the stripped user name.
 */
char *_nh_strip_invalid(char *username, char *invalid, char *buffer)
{
	int buffer_pos;					/* position in buffer */
	unsigned int i;					/* position in 'dir' */

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	buffer_pos = 0;

	/* cat each character in 'dir' onto buffer if it is valid */
	for (i=0; i<lib_strlen(username); i++) {
		if (buffer_pos>=_NH_BFSZ-1) break;
		if ( (_nh_valid_char(username[i], invalid)==TRUE) && (username[i]!='.') ) {
			buffer[buffer_pos] = username[i];
			buffer_pos++;
		}
	}

	buffer[buffer_pos] = 0;
	return buffer;
}

/*	encode_invalid encodes all invalid characters in 'username'.  On
 *	non-UNIX systems, leading or trailing '.' are considered invalid.  It
 *	returns a pointer to a static string containing the encoded user name.
 */
char *encode_invalid(char *username, char *invalid, char *buffer)
{
	int length;						/* length of 'username' */
	char cbuffer[_NH_BFSZ];		/* buffer to hold string to concatenate */
	int i;							/* position in 'username' */
	char n;							/* character at position in username */

	/* reset for each call to encode_invalid() */
	buffer[0] = 0;

	invalid = _nh_get_invalid_chars(invalid);

	/* don't return NULL or crash if username is NULL or empty */
	if ((username == NULL) || (*username == 0)) {
		buffer[0] = 0;
		return buffer;
	}

	length = lib_strlen(username);
	for (i=0; i<length; i++) {
		n = (int) (unsigned char) username[i];

		/* encode character if it is invalid */
		if ( (_nh_valid_char(n, invalid) == FALSE) || (n==CHAR_ENCODE) ) {
			snprintf(cbuffer, 4, "%c%02x", CHAR_ENCODE,abs(n));
		} else {
			snprintf(cbuffer, 2, "%c", n);
		}

		/* encode leading or trailing '.' on non-UNIX systems */
		#ifdef WIN32
			if ((i==0 || i==length-1) && (n=='.')) {
				snprintf(cbuffer, 4, "%c%02x", CHAR_ENCODE,abs(n));
			}

		#endif

		_nh_ncat(buffer, cbuffer, _NH_BFSZ-1);
	}
#ifdef WIN32
	/*
		Translate lptN, comN, and con, to $$lptn, $$comn $$con
	*/
	{int fix=FALSE;
	if (strcmpnc(buffer,"con")==0) fix = TRUE;
	if (strcmpnc(buffer,"nul")==0) fix = TRUE;
	if (strcmpnc(buffer,"aux")==0) fix = TRUE;
	if (strcmpnc(buffer,"prn")==0) fix = TRUE;
	if (lib_strlen(buffer)==4) if (strncmpnc(buffer,"com",3)==0) fix = TRUE;
	if (lib_strlen(buffer)==4) if (strncmpnc(buffer,"lpt",3)==0) fix = TRUE;
	if (fix) {
		memmove(buffer+2,buffer,lib_strlen(buffer)+2);
		buffer[0] = '$';
		buffer[1] = '$';
	}
	}
#endif

	return buffer;
}

/*	newhash_hex() returns an integer equal to the contents of string 's',
 *	assuming 's' to represent an hexidecimal number.
 *
 *	Note that the maximum length of 's' is 20 characters.
 */
int _nh_newhash_hex(char *s)
{
	char bf[100];
	int v;
	_nh_ncpy(bf, s, 20);
	bf[2] = 0;
	sscanf(bf,"%x",&v);
	return v;
}

/*	decode_invalid() performs the reverse of encode_invalid().  It returns
 *	a pointer to a string containing the string 'username', with all
 *	encoded characters in 'invalid' replaced by their originals.
 */
char *decode_invalid(char *username, char *invalid, char *bf)
{
	char *s;
	char *out;
	for ( s = username, out = bf; *s != 0;) 
	{
		if ( ( ((unsigned char) *s) == CHAR_ENCODE) && (lib_strlen(s)>=3) ) 
		{
			*out++ = _nh_newhash_hex(s+1);
			s += 3;
		} else {
			*out++ = *s++;
		}
	}
	*out++ = 0;
	return bf;
}

/*	ncpy copies string 'src' into string 'dst', to a maximum of 'len' chars.
 *	This is safer and more efficient than ncpy, which it replaces.
 */
char *_nh_ncpy(char *dst,const char *src, int len)
{
	char *xdst = dst;	/* temporary pointer to destination */
	int xlen = len;		/* position of null terminator */

    /* copy source string over destination string */
	for (;(*src !=0) && (len>0); ) {
		*dst++  = *src++;
		len--;
	}

	/* enure that destination string is safely terminated */
	*dst++ = 0;
	xdst[xlen] = 0;

	return xdst;
}

/*	ncat copies concatenates string 'src' to string 'dst', to a maximum of
 *	'len' chars. This is safer and more efficient than ncat, which it
 *	replaces.
 */
char *_nh_ncat(char *dst,const char *src, int len)
{
	char *xdst = dst;	/* temporary pointer to destination */
	int xlen = len;		/* position of null terminator */

	/* find end of destination string */
	for (;(*dst !=0) && (len>0); dst++) {
		len--;
	}

    /* append source string to end of destination string */
	for (;(*src !=0) && (len>0); ) {
		*dst++  = *src++;
		len--;
	}

	/* enure that destination string is safely terminated */
	*dst++ = 0;
    xdst[xlen] = 0;

	return xdst;
}

/*	encode_last encodes the last level of drop dir, i.e. "/a/a/.a."
 *	into "/a/a/$2ea$2e".
 */
void encode_last(char *path, char *invalid)
{
	char *username_ptr;
	char path_buf[_NH_BFSZ];
	char tmp[_NH_BFSZ];
	char encode_path[_NH_BFSZ];

	if ( path == NULL ) return;
	if ( *path == '\0' ) return;	

	_nh_ncpy(path_buf, path, _NH_BFSZ -1);

	username_ptr = strrchr(path_buf, NH_SEP);
	if (username_ptr == NULL) {
		username_ptr = path;
	} else {
		*username_ptr = 0;
	}

	sprintf(encode_path, "%s%c%s", path_buf, NH_SEP,
		encode_invalid(username_ptr+1, invalid,tmp));

	_nh_ncpy(path, encode_path, _NH_BFSZ-1);
	return;
}

/* newhash_strlwr() converts 's' into a lower-case string, in-place */
char *_nh_strlwr(char *s)
{
        char *ss = s;
        
		for (; *s != 0; s++) 
		{
			if ( sisupper(*s) ) 
			{
				if (*s > 0) *s = tolower(*s);
			}
		}
        return ss;
}

/*	Put any test code in main(), between this #ifdef/#endif pair. #_NH_TEST_HASHING
 *	is defined in newhash.c.
 */
#ifdef _NH_TEST_HASHING
	int main(void)
	{
		char dir[_NH_BFSZ], un[_NH_BFSZ], un_vd[_NH_BFSZ], userpart[_NH_BFSZ];
		char username[10][_NH_BFSZ] = {
			"/a",
			"/b.",
			"/.c",
			"/t.wil$lacy",
			"/tamwillacy",
			"/Ö.wil lacy",
			"/user0",
			""
		};

		int num=8, n, n1;

		for (n1=0; n1<4; n1++)
		{
			printf("--- Hashing type %d\n", n1);
			for (n=0; n<num; n++) {
				printf("%15s:\t", username[n]);

				strcpy(dir, "c:\\dmail\\in");
				strcpy(un, username[n]);
				sprintf(un_vd, "vd0_%s", un);

				if ( hash_new(dir, username[n], NULL, n1, TRUE, userpart)  != FALSE) {
					printf("{%s} - {%s}\n", dir, userpart);
				} else {
					printf("{%s}\t*** returned FALSE ***\n", dir);
				}
			}
			printf("\n");
		}

	return EXIT_SUCCESS;
	}
#endif














