/*  Copyright 1995 David C. Niemi
 *  Copyright 1996-1998,2000-2002,2008,2009 Alain Knaff.
 *  This file is part of mtools.
 *
 *  Mtools 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Mtools 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 Mtools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "sysincludes.h"
#include "msdos.h"
#include "mtools.h"
#include "vfat.h"
#include "codepage.h"
#include "file_name.h"

/* Write a DOS name + extension into a legal unix-style name.  */
char *unix_normalize (doscp_t *cp, char *ans, dos_name_t *dn, size_t ans_size)
{
	char buffer[13];
	wchar_t wbuffer[13];
	char *a;
	int j;

	for (a=buffer,j=0; (j<8) && (dn->base[j] > ' '); ++j,++a)
		*a = dn->base[j];
	if(dn->ext[0] > ' ') {
		*a++ = '.';
		for (j=0; j<3 && dn->ext[j] > ' '; ++j,++a)
			*a = dn->ext[j];
	}
	*a++ = '\0';
	dos_to_wchar(cp, buffer, wbuffer, 13);
	wchar_to_native(wbuffer, ans, 13, ans_size);
	return ans;
}

typedef enum Case_l {
	NONE,
	UPPER,
	LOWER
} Case_t;

static void TranslateToDos(doscp_t *toDos, const char *in, char *out,
			   size_t count, char *end, Case_t *Case, int *mangled)
{
	wchar_t buffer[12];
	wchar_t *s=buffer;
	size_t t_idx = 0;

	/* first convert to wchar, so we get to use towupper etc. */
	native_to_wchar(in, buffer, count, end, mangled);
	buffer[count]='\0';

	*Case = NONE;
	for( ;  *s ; s++) {
		/* skip spaces & dots */
		if(*s == ' ' || *s == '.') {
			*mangled |= 3;
			continue;
		}

		if (iswcntrl((wint_t)*s)) {
			/* "control" characters */
			*mangled |= 3;
			buffer[t_idx] = '_';
		} else if (iswlower((wint_t)*s)) {
			buffer[t_idx] = ch_towupper(*s);
			if(*Case == UPPER && !mtools_no_vfat)
				*mangled |= 1;
			else
				*Case = LOWER;
		} else if (iswupper((wint_t)*s)) {
			buffer[t_idx] = *s;
			if(*Case == LOWER && !mtools_no_vfat)
				*mangled |= 1;
			else
				*Case = UPPER;
		} else
			buffer[t_idx] = *s;
		t_idx++;
	}
	wchar_to_dos(toDos, buffer, out, t_idx, mangled);
}

/* dos_name
 *
 * Convert a Unix-style filename to a legal MSDOS name and extension.
 * Will truncate file and extension names, will substitute
 * the character '~' for any illegal character(s) in the name.
 */
void dos_name(doscp_t *toDos, const char *name, int verbose UNUSEDP,
	      int *mangled, dos_name_t *dn)
{
	char *s, *ext;
	size_t i;
	Case_t BaseCase, ExtCase = UPPER;

	*mangled = 0;

	/* skip drive letter */
	if (name[0] && name[1] == ':')
		name = &name[2];

	/* zap the leading path */
	name = _basename(name);
	if ((s = strrchr(name, '\\')))
		name = s + 1;

	memset(dn, ' ', 11);

	/* skip leading dots and spaces */
	i = strspn(name, ". ");
	if(i) {
		name += i;
		*mangled = 3;
	}

	ext = strrchr(name, '.');

	/* main name */
	TranslateToDos(toDos, name, dn->base, 8, ext, &BaseCase, mangled);
	if(ext)
		TranslateToDos(toDos, ext+1, dn->ext, 3, 0, &ExtCase,  mangled);

	if(*mangled & 2)
		autorename_short(dn, 0);

	if(!*mangled) {
		if(BaseCase == LOWER)
			*mangled |= BASECASE;
		if(ExtCase == LOWER)
			*mangled |= EXTCASE;
	}
}


/*
 * Get rid of spaces in an MSDOS 'raw' name (one that has come from the
 * directory structure) so that it can be used for regular expression
 * matching with a Unix filename.  Also used to 'unfix' a name that has
 * been altered by dos_name().
 */

wchar_t *unix_name(doscp_t *dosCp,
		   const char *base, const char *ext, uint8_t Case, wchar_t *ret)
{
	char *s, tname[9], text[4], ans[13];
	int i;

	strncpy(tname, base, 8);
	tname[8] = '\0';
	if ((s = strchr(tname, ' ')))
		*s = '\0';
	if (tname[0] == '\x05')
		tname[0] = '\xE5';

	if(!(Case & (BASECASE | EXTCASE)) && mtools_ignore_short_case)
		Case |= BASECASE | EXTCASE;

	if(Case & BASECASE)
		for(i=0;i<8 && tname[i];i++)
			tname[i] = ch_tolower(tname[i]);

	strncpy(text, ext, 3);
	text[3] = '\0';
	if ((s = strchr(text, ' ')))
		*s = '\0';

	if(Case & EXTCASE)
		for(i=0;i<3 && text[i];i++)
			text[i] = ch_tolower(text[i]);

	if (*text) {
		strcpy(ans, tname);
		strcat(ans, ".");
		strcat(ans, text);
	} else
		strcpy(ans, tname);

	/* fix special characters (above 0x80) */
	dos_to_wchar(dosCp, ans, ret, 12);
	return ret;
}

/* If null encountered, set *end to 0x40 and write nulls rest of way
 * 950820: Win95 does not like this!  It complains about bad characters.
 * So, instead: If null encountered, set *end to 0x40, write the null, and
 * write 0xff the rest of the way (that is what Win95 seems to do; hopefully
 * that will make it happy)
 */
/* Always return num */
int unicode_write(wchar_t *in, struct unicode_char *out, int num, int *end_p)
{
	int j;

	for (j=0; j<num; ++j) {
		if (*end_p)
			/* Fill with 0xff */
			out->uchar = out->lchar = 0xff;
		else {
			/* TODO / FIXME : handle case where wchat has more
			 * than 2 bytes (i.e. bytes 2 or 3 are set.
			 * ==> generate surrogate pairs?
			 */
			out->uchar = (*in & 0xffff) >> 8;
			out->lchar = *in & 0xff;
			if (! *in) {
				*end_p = VSE_LAST;
			}
		}

		++out;
		++in;
	}
	return num;
}
