1/*  Copyright 1995 David C. Niemi
2 *  Copyright 1996-1998,2000-2002,2008,2009 Alain Knaff.
3 *  This file is part of mtools.
4 *
5 *  Mtools is free software: you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation, either version 3 of the License, or
8 *  (at your option) any later version.
9 *
10 *  Mtools is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "sysincludes.h"
20#include "msdos.h"
21#include "mtools.h"
22#include "vfat.h"
23#include "codepage.h"
24#include "file_name.h"
25
26/* Write a DOS name + extension into a legal unix-style name.  */
27char *unix_normalize (doscp_t *cp, char *ans, dos_name_t *dn)
28{
29	char buffer[13];
30	wchar_t wbuffer[13];
31	char *a;
32	int j;
33
34	for (a=buffer,j=0; (j<8) && (dn->base[j] > ' '); ++j,++a)
35		*a = dn->base[j];
36	if(dn->ext[0] > ' ') {
37		*a++ = '.';
38		for (j=0; j<3 && dn->ext[j] > ' '; ++j,++a)
39			*a = dn->ext[j];
40	}
41	*a++ = '\0';
42	dos_to_wchar(cp, buffer, wbuffer, 13);
43	wchar_to_native(wbuffer, ans, 13);
44	return ans;
45}
46
47typedef enum Case_l {
48	NONE,
49	UPPER,
50	LOWER
51} Case_t;
52
53static void TranslateToDos(doscp_t *toDos, const char *in, char *out, int count,
54			   char *end, Case_t *Case, int *mangled)
55{
56	wchar_t buffer[12];
57	wchar_t *s=buffer;
58	wchar_t *t=buffer;
59
60	/* first convert to wchar, so we get to use towupper etc. */
61	native_to_wchar(in, buffer, count, end, mangled);
62	buffer[count]='\0';
63
64	*Case = NONE;
65	for( ;  *s ; s++) {
66		if(!count) {
67			*mangled |= 3;
68			break;
69		}
70		/* skip spaces & dots */
71		if(*s == ' ' || *s == '.') {
72			*mangled |= 3;
73			continue;
74		}
75
76		if (iswcntrl(*s)) {
77			/* "control" characters */
78			*mangled |= 3;
79			*t = '_';
80		} else if (iswlower(*s)) {
81			*t = towupper(*s);
82			if(*Case == UPPER && !mtools_no_vfat)
83				*mangled |= 1;
84			else
85				*Case = LOWER;
86		} else if (iswupper(*s)) {
87			*t = *s;
88			if(*Case == LOWER && !mtools_no_vfat)
89				*mangled |= 1;
90			else
91				*Case = UPPER;
92		} else
93			*t = *s;
94		count--;
95		t++;
96	}
97	wchar_to_dos(toDos, buffer, out, t - buffer, mangled);
98}
99
100/* dos_name
101 *
102 * Convert a Unix-style filename to a legal MSDOS name and extension.
103 * Will truncate file and extension names, will substitute
104 * the character '~' for any illegal character(s) in the name.
105 */
106void dos_name(doscp_t *toDos, const char *name, int verbose, int *mangled,
107	      dos_name_t *dn)
108{
109	char *s, *ext;
110	register int i;
111	Case_t BaseCase, ExtCase;
112
113	*mangled = 0;
114
115	/* skip drive letter */
116	if (name[0] && name[1] == ':')
117		name = &name[2];
118
119	/* zap the leading path */
120	name = (char *) _basename(name);
121	if ((s = strrchr(name, '\\')))
122		name = s + 1;
123
124	memset(dn, ' ', 11);
125
126	/* skip leading dots and spaces */
127	i = strspn(name, ". ");
128	if(i) {
129		name += i;
130		*mangled = 3;
131	}
132
133	ext = strrchr(name, '.');
134
135	/* main name */
136	TranslateToDos(toDos, name, dn->base, 8, ext, &BaseCase, mangled);
137	if(ext)
138		TranslateToDos(toDos, ext+1, dn->ext, 3, 0, &ExtCase,  mangled);
139
140	if(*mangled & 2)
141		autorename_short(dn, 0);
142
143	if(!*mangled) {
144		if(BaseCase == LOWER)
145			*mangled |= BASECASE;
146		if(ExtCase == LOWER)
147			*mangled |= EXTCASE;
148		if((BaseCase == LOWER || ExtCase == LOWER) &&
149		   !mtools_no_vfat) {
150			*mangled |= 1;
151		}
152	}
153}
154
155
156/*
157 * Get rid of spaces in an MSDOS 'raw' name (one that has come from the
158 * directory structure) so that it can be used for regular expression
159 * matching with a Unix filename.  Also used to 'unfix' a name that has
160 * been altered by dos_name().
161 */
162
163wchar_t *unix_name(doscp_t *dosCp,
164		   const char *base, const char *ext, char Case, wchar_t *ret)
165{
166	char *s, tname[9], text[4], ans[13];
167	int i;
168
169	strncpy(tname, base, 8);
170	tname[8] = '\0';
171	if ((s = strchr(tname, ' ')))
172		*s = '\0';
173
174	if(!(Case & (BASECASE | EXTCASE)) && mtools_ignore_short_case)
175		Case |= BASECASE | EXTCASE;
176
177	if(Case & BASECASE)
178		for(i=0;i<8 && tname[i];i++)
179			tname[i] = tolower(tname[i]);
180
181	strncpy(text, ext, 3);
182	text[3] = '\0';
183	if ((s = strchr(text, ' ')))
184		*s = '\0';
185
186	if(Case & EXTCASE)
187		for(i=0;i<3 && text[i];i++)
188			text[i] = tolower(text[i]);
189
190	if (*text) {
191		strcpy(ans, tname);
192		strcat(ans, ".");
193		strcat(ans, text);
194	} else
195		strcpy(ans, tname);
196
197	/* fix special characters (above 0x80) */
198	dos_to_wchar(dosCp, ans, ret, 12);
199	return ret;
200}
201
202/* If null encountered, set *end to 0x40 and write nulls rest of way
203 * 950820: Win95 does not like this!  It complains about bad characters.
204 * So, instead: If null encountered, set *end to 0x40, write the null, and
205 * write 0xff the rest of the way (that is what Win95 seems to do; hopefully
206 * that will make it happy)
207 */
208/* Always return num */
209int unicode_write(wchar_t *in, struct unicode_char *out, int num, int *end_p)
210{
211	int j;
212
213	for (j=0; j<num; ++j) {
214		if (*end_p)
215			/* Fill with 0xff */
216			out->uchar = out->lchar = (char) 0xff;
217		else {
218			out->uchar = *in >> 8;
219			out->lchar = *in;
220			if (! *in) {
221				*end_p = VSE_LAST;
222			}
223		}
224
225		++out;
226		++in;
227	}
228	return num;
229}
230