blob: 9f357a62122ddab3138f26988540cb93cc7ba7d2 [file] [log] [blame]
wdenk591dda52002-11-18 00:14:45 +00001/*
2 * (C) Copyright 2002
3 * Stäubli Faverges - <www.staubli.com>
4 * Pierre AUBERT p.aubert@staubli.com
5 *
6 * See file CREDITS for list of people who contributed to this
7 * project.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22 * MA 02111-1307 USA
23 */
24
25#include <common.h>
26#include <config.h>
wdenk591dda52002-11-18 00:14:45 +000027#include <linux/ctype.h>
28
29#include "dos.h"
30#include "fdos.h"
31
32static int dir_read (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +000033 Slot_t *dir,
34 Directory_t *dirent,
35 int num,
36 struct vfat_state *v);
wdenk591dda52002-11-18 00:14:45 +000037
38static int unicode_read (char *in, char *out, int num);
39static int match (const char *s, const char *p);
40static unsigned char sum_shortname (char *name);
41static int check_vfat (struct vfat_state *v, Directory_t *dir);
42static char *conv_name (char *name, char *ext, char Case, char *ans);
43
44
45/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000046 * clear_vfat --
wdenk591dda52002-11-18 00:14:45 +000047 *-----------------------------------------------------------------------------
48 */
49static void clear_vfat (struct vfat_state *v)
50{
51 v -> subentries = 0;
52 v -> status = 0;
53}
54
55/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000056 * vfat_lookup --
wdenk591dda52002-11-18 00:14:45 +000057 *-----------------------------------------------------------------------------
58 */
59int vfat_lookup (Slot_t *dir,
wdenk57b2d802003-06-27 21:31:46 +000060 Fs_t *fs,
61 Directory_t *dirent,
62 int *entry,
63 int *vfat_start,
64 char *filename,
65 int flags,
66 char *outname,
67 Slot_t *file)
wdenk591dda52002-11-18 00:14:45 +000068{
69 int found;
70 struct vfat_state vfat;
71 char newfile [VSE_NAMELEN];
72 int vfat_present = 0;
73
74 if (*entry == -1) {
wdenk57b2d802003-06-27 21:31:46 +000075 return -1;
wdenk591dda52002-11-18 00:14:45 +000076 }
77
78 found = 0;
79 clear_vfat (&vfat);
80 while (1) {
wdenk57b2d802003-06-27 21:31:46 +000081 if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) {
82 if (vfat_start) {
83 *vfat_start = *entry;
84 }
85 break;
86 }
87 (*entry)++;
wdenk591dda52002-11-18 00:14:45 +000088
wdenk57b2d802003-06-27 21:31:46 +000089 /* Empty slot */
90 if (dirent -> name[0] == '\0'){
91 if (vfat_start == 0) {
92 break;
93 }
94 continue;
95 }
wdenk591dda52002-11-18 00:14:45 +000096
wdenk57b2d802003-06-27 21:31:46 +000097 if (dirent -> attr == ATTR_VSE) {
98 /* VSE entry, continue */
99 continue;
100 }
101 if ( (dirent -> name [0] == DELMARK) ||
102 ((dirent -> attr & ATTR_DIRECTORY) != 0 &&
103 (flags & ACCEPT_DIR) == 0) ||
104 ((dirent -> attr & ATTR_VOLUME) != 0 &&
105 (flags & ACCEPT_LABEL) == 0) ||
106 (((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) &&
107 (flags & ACCEPT_PLAIN) == 0)) {
108 clear_vfat (&vfat);
109 continue;
110 }
wdenk591dda52002-11-18 00:14:45 +0000111
wdenk57b2d802003-06-27 21:31:46 +0000112 vfat_present = check_vfat (&vfat, dirent);
113 if (vfat_start) {
114 *vfat_start = *entry - 1;
115 if (vfat_present) {
116 *vfat_start -= vfat.subentries;
117 }
118 }
wdenk591dda52002-11-18 00:14:45 +0000119
wdenk57b2d802003-06-27 21:31:46 +0000120 if (dirent -> attr & ATTR_VOLUME) {
121 strncpy (newfile, dirent -> name, 8);
122 newfile [8] = '\0';
123 strncat (newfile, dirent -> ext, 3);
124 newfile [11] = '\0';
125 }
126 else {
127 conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile);
128 }
wdenk591dda52002-11-18 00:14:45 +0000129
wdenk57b2d802003-06-27 21:31:46 +0000130 if (flags & MATCH_ANY) {
131 found = 1;
132 break;
133 }
wdenk591dda52002-11-18 00:14:45 +0000134
wdenk57b2d802003-06-27 21:31:46 +0000135 if ((vfat_present && match (vfat.name, filename)) ||
136 (match (newfile, filename))) {
137 found = 1;
138 break;
139 }
140 clear_vfat (&vfat);
wdenk591dda52002-11-18 00:14:45 +0000141 }
142
143 if (found) {
wdenk57b2d802003-06-27 21:31:46 +0000144 if ((flags & DO_OPEN) && file) {
145 if (open_file (file, dirent) < 0) {
146 return (-1);
147 }
148 }
149 if (outname) {
150 if (vfat_present) {
151 strcpy (outname, vfat.name);
152 }
153 else {
154 strcpy (outname, newfile);
155 }
156 }
157 return (0); /* File found */
wdenk591dda52002-11-18 00:14:45 +0000158 } else {
wdenk57b2d802003-06-27 21:31:46 +0000159 *entry = -1;
160 return -1; /* File not found */
wdenk591dda52002-11-18 00:14:45 +0000161 }
162}
163
164/*-----------------------------------------------------------------------------
165 * dir_read -- Read one directory entry
166 *-----------------------------------------------------------------------------
167 */
168static int dir_read (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +0000169 Slot_t *dir,
170 Directory_t *dirent,
171 int num,
172 struct vfat_state *v)
wdenk591dda52002-11-18 00:14:45 +0000173{
174
175 /* read the directory entry */
176 if (read_file (fs,
wdenk57b2d802003-06-27 21:31:46 +0000177 dir,
178 (char *)dirent,
179 num * MDIR_SIZE,
180 MDIR_SIZE) != MDIR_SIZE) {
181 return (-1);
wdenk591dda52002-11-18 00:14:45 +0000182 }
183
184 if (v && (dirent -> attr == ATTR_VSE)) {
wdenk57b2d802003-06-27 21:31:46 +0000185 struct vfat_subentry *vse;
186 unsigned char id, last_flag;
187 char *c;
wdenk591dda52002-11-18 00:14:45 +0000188
wdenk57b2d802003-06-27 21:31:46 +0000189 vse = (struct vfat_subentry *) dirent;
190 id = vse -> id & VSE_MASK;
191 last_flag = (vse -> id & VSE_LAST);
192 if (id > MAX_VFAT_SUBENTRIES) {
193 /* Invalid VSE entry */
194 return (-1);
195 }
wdenk591dda52002-11-18 00:14:45 +0000196
197
wdenk57b2d802003-06-27 21:31:46 +0000198 /* Decode VSE */
199 if(v -> sum != vse -> sum) {
200 clear_vfat (v);
201 v -> sum = vse -> sum;
202 }
wdenk591dda52002-11-18 00:14:45 +0000203
wdenk591dda52002-11-18 00:14:45 +0000204
wdenk57b2d802003-06-27 21:31:46 +0000205 v -> status |= 1 << (id - 1);
206 if (last_flag) {
207 v -> subentries = id;
208 }
209
210 c = &(v -> name [VSE_NAMELEN * (id - 1)]);
211 c += unicode_read (vse->text1, c, VSE1SIZE);
212 c += unicode_read (vse->text2, c, VSE2SIZE);
213 c += unicode_read (vse->text3, c, VSE3SIZE);
214
215 if (last_flag) {
216 *c = '\0'; /* Null terminate long name */
217 }
218
wdenk591dda52002-11-18 00:14:45 +0000219 }
220 return (0);
221}
222
223/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000224 * unicode_read --
wdenk591dda52002-11-18 00:14:45 +0000225 *-----------------------------------------------------------------------------
226 */
227static int unicode_read (char *in, char *out, int num)
228{
229 int j;
wdenk57b2d802003-06-27 21:31:46 +0000230
wdenk591dda52002-11-18 00:14:45 +0000231 for (j = 0; j < num; ++j) {
wdenk57b2d802003-06-27 21:31:46 +0000232 if (in [1])
233 *out = '_';
234 else
235 *out = in [0];
236 out ++;
237 in += 2;
wdenk591dda52002-11-18 00:14:45 +0000238 }
239 return num;
240}
241
242/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000243 * match --
wdenk591dda52002-11-18 00:14:45 +0000244 *-----------------------------------------------------------------------------
245 */
246static int match (const char *s, const char *p)
247{
248
249 for (; *p != '\0'; ) {
wdenk57b2d802003-06-27 21:31:46 +0000250 if (toupper (*s) != toupper (*p)) {
251 return (0);
252 }
253 p++;
254 s++;
wdenk591dda52002-11-18 00:14:45 +0000255 }
wdenk57b2d802003-06-27 21:31:46 +0000256
wdenk591dda52002-11-18 00:14:45 +0000257 if (*s != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000258 return (0);
wdenk591dda52002-11-18 00:14:45 +0000259 }
260 else {
wdenk57b2d802003-06-27 21:31:46 +0000261 return (1);
wdenk591dda52002-11-18 00:14:45 +0000262 }
263}
264/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000265 * sum_shortname --
wdenk591dda52002-11-18 00:14:45 +0000266 *-----------------------------------------------------------------------------
267 */
268static unsigned char sum_shortname (char *name)
269{
270 unsigned char sum;
271 int j;
wdenk57b2d802003-06-27 21:31:46 +0000272
wdenk591dda52002-11-18 00:14:45 +0000273 for (j = sum = 0; j < 11; ++j) {
wdenk57b2d802003-06-27 21:31:46 +0000274 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) +
275 (name [j] ? name [j] : ' ');
wdenk591dda52002-11-18 00:14:45 +0000276 }
277 return (sum);
278}
279/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000280 * check_vfat --
wdenk591dda52002-11-18 00:14:45 +0000281 * Return 1 if long name is valid, 0 else
282 *-----------------------------------------------------------------------------
283 */
284static int check_vfat (struct vfat_state *v, Directory_t *dir)
285{
286 char name[12];
wdenk57b2d802003-06-27 21:31:46 +0000287
wdenk591dda52002-11-18 00:14:45 +0000288 if (v -> subentries == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000289 return 0;
wdenk591dda52002-11-18 00:14:45 +0000290 }
wdenk57b2d802003-06-27 21:31:46 +0000291
wdenk591dda52002-11-18 00:14:45 +0000292 strncpy (name, dir -> name, 8);
293 strncpy (name + 8, dir -> ext, 3);
294 name [11] = '\0';
wdenk57b2d802003-06-27 21:31:46 +0000295
wdenk591dda52002-11-18 00:14:45 +0000296 if (v -> sum != sum_shortname (name)) {
wdenk57b2d802003-06-27 21:31:46 +0000297 return 0;
wdenk591dda52002-11-18 00:14:45 +0000298 }
wdenk57b2d802003-06-27 21:31:46 +0000299
wdenk591dda52002-11-18 00:14:45 +0000300 if( (v -> status & ((1 << v -> subentries) - 1)) !=
wdenk57b2d802003-06-27 21:31:46 +0000301 (1 << v -> subentries) - 1) {
302 return 0;
wdenk591dda52002-11-18 00:14:45 +0000303 }
304 v->name [VSE_NAMELEN * v -> subentries] = 0;
wdenk57b2d802003-06-27 21:31:46 +0000305
wdenk591dda52002-11-18 00:14:45 +0000306 return 1;
307}
308/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000309 * conv_name --
wdenk591dda52002-11-18 00:14:45 +0000310 *-----------------------------------------------------------------------------
311 */
312static char *conv_name (char *name, char *ext, char Case, char *ans)
313{
314 char tname [9], text [4];
315 int i;
316
317 i = 0;
318 while (i < 8 && name [i] != ' ' && name [i] != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000319 tname [i] = name [i];
320 i++;
wdenk591dda52002-11-18 00:14:45 +0000321 }
322 tname [i] = '\0';
wdenk57b2d802003-06-27 21:31:46 +0000323
wdenk591dda52002-11-18 00:14:45 +0000324 if (Case & BASECASE) {
wdenk57b2d802003-06-27 21:31:46 +0000325 for (i = 0; i < 8 && tname [i]; i++) {
326 tname [i] = tolower (tname [i]);
327 }
wdenk591dda52002-11-18 00:14:45 +0000328 }
329
330 i = 0;
331 while (i < 3 && ext [i] != ' ' && ext [i] != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000332 text [i] = ext [i];
333 i++;
wdenk591dda52002-11-18 00:14:45 +0000334 }
335 text [i] = '\0';
336
337 if (Case & EXTCASE){
wdenk57b2d802003-06-27 21:31:46 +0000338 for (i = 0; i < 3 && text [i]; i++) {
339 text [i] = tolower (text [i]);
340 }
wdenk591dda52002-11-18 00:14:45 +0000341 }
342
343 if (*text) {
wdenk57b2d802003-06-27 21:31:46 +0000344 strcpy (ans, tname);
345 strcat (ans, ".");
346 strcat (ans, text);
wdenk591dda52002-11-18 00:14:45 +0000347 }
348 else {
wdenk57b2d802003-06-27 21:31:46 +0000349 strcpy(ans, tname);
wdenk591dda52002-11-18 00:14:45 +0000350 }
351 return (ans);
352}