1/*  Copyright 1995 David C. Niemi
2 *  Copyright 1996-2003,2005,2007-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 * vfat.c
19 *
20 * Miscellaneous VFAT-related functions
21 */
22
23#include "sysincludes.h"
24#include "msdos.h"
25#include "mtools.h"
26#include "vfat.h"
27#include "file.h"
28#include "dirCache.h"
29#include "file_name.h"
30
31/* #define DEBUG */
32
33const char *short_illegals=";+=[]',\"*\\<>/?:|";
34const char *long_illegals = "\"*\\<>/?:|\005";
35
36/* Automatically derive a new name */
37static void autorename(char *name,
38		       char tilda, char dot, const char *illegals,
39		       int limit, int bump)
40{
41	int tildapos, dotpos;
42	unsigned int seqnum=0, maxseq=0;
43	char tmp;
44	char *p;
45
46#ifdef DEBUG
47	printf("In autorename for name=%s.\n", name);
48#endif
49	tildapos = -1;
50
51	for(p=name; *p ; p++)
52		if (strchr(illegals, *p)) {
53			*p = '_';
54			bump = 0;
55		}
56
57	for(dotpos=0;
58	    name[dotpos] && dotpos < limit && name[dotpos] != dot ;
59	    dotpos++) {
60		if(name[dotpos] == tilda) {
61			tildapos = dotpos;
62			seqnum = 0;
63			maxseq = 1;
64		} else if (name[dotpos] >= '0' && name[dotpos] <= '9') {
65			seqnum = seqnum * 10 + name[dotpos] - '0';
66			maxseq = maxseq * 10;
67		} else
68			tildapos = -1; /* sequence number interrupted */
69	}
70	if(tildapos == -1) {
71		/* no sequence number yet */
72		if(dotpos > limit - 2) {
73			tildapos = limit - 2;
74			dotpos = limit;
75		} else {
76			tildapos = dotpos;
77			dotpos += 2;
78		}
79		seqnum = 1;
80	} else {
81		if(bump)
82			seqnum++;
83		if(seqnum > 999999) {
84			seqnum = 1;
85			tildapos = dotpos - 2;
86			/* this matches Win95's behavior, and also guarantees
87			 * us that the sequence numbers never get shorter */
88		}
89		if (seqnum == maxseq) {
90		    if(dotpos >= limit)
91			tildapos--;
92		    else
93			dotpos++;
94		}
95	}
96
97	tmp = name[dotpos];
98	if((bump && seqnum == 1) || seqnum > 1 || mtools_numeric_tail)
99		sprintf(name+tildapos,"%c%d",tilda, seqnum);
100	if(dot)
101	    name[dotpos]=tmp;
102	/* replace the character if it wasn't a space */
103#ifdef DEBUG
104	printf("Out autorename for name=%s.\n", name);
105#endif
106}
107
108
109void autorename_short(dos_name_t *name, int bump)
110{
111	autorename(name->base, '~', ' ', short_illegals, 8, bump);
112}
113
114void autorename_long(char *name, int bump)
115{
116	autorename(name, '-', '\0', long_illegals, 255, bump);
117}
118
119
120static __inline__ int unicode_read(struct unicode_char *in,
121				   wchar_t *out, int num)
122{
123	wchar_t *end_out = out+num;
124
125	while(out < end_out) {
126#ifdef HAVE_WCHAR_H
127		*out = in->lchar | ((in->uchar) << 8);
128#else
129		if (in->uchar)
130			*out = '_';
131		else
132			*out = in->lchar;
133#endif
134		++out;
135		++in;
136	}
137	return num;
138}
139
140
141void clear_vfat(struct vfat_state *v)
142{
143	v->subentries = 0;
144	v->status = 0;
145	v->present = 0;
146}
147
148
149/* sum_shortname
150 *
151 * Calculate the checksum that results from the short name in *dir.
152 *
153 * The sum is formed by circularly right-shifting the previous sum
154 * and adding in each character, from left to right, padding both
155 * the name and extension to maximum length with spaces and skipping
156 * the "." (hence always summing exactly 11 characters).
157 *
158 * This exact algorithm is required in order to remain compatible
159 * with Microsoft Windows-95 and Microsoft Windows NT 3.5.
160 * Thanks to Jeffrey Richter of Microsoft Systems Journal for
161 * pointing me to the correct algorithm.
162 *
163 * David C. Niemi (niemi@tuxers.net) 95.01.19
164 */
165static __inline__ unsigned char sum_shortname(const dos_name_t *dn)
166{
167	unsigned char sum;
168	const char *name=dn->base;
169	const char *end = name+11;
170
171	for (sum=0; name<end; ++name)
172		sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1)
173		  + (*name ? *name : ' ');
174	return(sum);
175}
176
177/* check_vfat
178 *
179 * Inspect a directory and any associated VSEs.
180 * Return 1 if the VSEs comprise a valid long file name,
181 * 0 if not.
182 */
183static __inline__ void check_vfat(struct vfat_state *v, struct directory *dir)
184{
185	dos_name_t dn;;
186
187	if (! v->subentries) {
188#ifdef DEBUG
189		fprintf(stderr, "check_vfat: no VSEs.\n");
190#endif
191		return;
192	}
193
194	strncpy(dn.base, (char *)dir->name, 8);
195	strncpy(dn.ext, (char *)dir->ext, 3);
196
197	if (v->sum != sum_shortname(&dn))
198		return;
199
200	if( (v->status & ((1<<v->subentries) - 1)) != (1<<v->subentries) - 1)
201		return; /* missing entries */
202
203	/* zero out byte following last entry, for good measure */
204	v->name[VSE_NAMELEN * v->subentries] = 0;
205	v->present = 1;
206}
207
208
209int clear_vses(Stream_t *Dir, int entrySlot, size_t last)
210{
211	direntry_t entry;
212	dirCache_t *cache;
213	int error;
214
215	entry.Dir = Dir;
216	entry.entry = entrySlot;
217
218	/*maximize(last, entry.entry + MAX_VFAT_SUBENTRIES);*/
219	cache = allocDirCache(Dir, last);
220	if(!cache) {
221		fprintf(stderr, "Out of memory error in clear_vses\n");
222		exit(1);
223	}
224	addFreeEntry(cache, entry.entry, last);
225	for (; entry.entry < (signed int) last; ++entry.entry) {
226#ifdef DEBUG
227		fprintf(stderr,"Clearing entry %d.\n", entry.entry);
228#endif
229		dir_read(&entry, &error);
230		if(error)
231		    return error;
232		if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK)
233			break;
234		entry.dir.name[0] = DELMARK;
235		if (entry.dir.attr == 0xf)
236			entry.dir.attr = '\0';
237		low_level_dir_write(&entry);
238	}
239	return 0;
240}
241
242int write_vfat(Stream_t *Dir, dos_name_t *shortname, char *longname, int start,
243	       direntry_t *mainEntry)
244{
245	struct vfat_subentry *vse;
246	int vse_id, num_vses;
247	wchar_t *c;
248	direntry_t entry;
249	dirCache_t *cache;
250	wchar_t unixyName[13];
251	doscp_t *cp = GET_DOSCONVERT(Dir);
252
253	wchar_t wlongname[MAX_VNAMELEN+1];
254	int wlen;
255
256	if(longname) {
257#ifdef DEBUG
258		printf("Entering write_vfat with longname=\"%s\", start=%d.\n",
259		       longname,start);
260#endif
261		entry.Dir = Dir;
262		vse = (struct vfat_subentry *) &entry.dir;
263		/* Fill in invariant part of vse */
264		vse->attribute = 0x0f;
265		vse->hash1 = vse->sector_l = vse->sector_u = 0;
266		vse->sum = sum_shortname(shortname);
267#ifdef DEBUG
268		printf("Wrote checksum=%d for shortname %s.%s\n",
269		       vse->sum,shortname->base,shortname->ext);
270#endif
271
272		wlen = native_to_wchar(longname, wlongname, MAX_VNAMELEN+1,
273				       0, 0);
274		num_vses = (wlen + VSE_NAMELEN - 1)/VSE_NAMELEN;
275		for (vse_id = num_vses; vse_id; --vse_id) {
276			int end = 0;
277
278			c = wlongname + (vse_id - 1) * VSE_NAMELEN;
279
280			c += unicode_write(c, vse->text1, VSE1SIZE, &end);
281			c += unicode_write(c, vse->text2, VSE2SIZE, &end);
282			c += unicode_write(c, vse->text3, VSE3SIZE, &end);
283
284			vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
285#ifdef DEBUG
286			printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n",
287			       longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN,
288			       start + num_vses - vse_id, start + num_vses);
289#endif
290
291			entry.entry = start + num_vses - vse_id;
292			low_level_dir_write(&entry);
293		}
294	} else {
295		num_vses = 0;
296		wlongname[0]='\0';
297	}
298	cache = allocDirCache(Dir, start + num_vses + 1);
299	if(!cache) {
300		fprintf(stderr, "Out of memory error\n");
301		exit(1);
302	}
303	unix_name(cp, shortname->base, shortname->ext, 0, unixyName);
304	addUsedEntry(cache, start, start + num_vses + 1, wlongname, unixyName,
305		     &mainEntry->dir);
306	low_level_dir_write(mainEntry);
307	return start + num_vses;
308}
309
310void dir_write(direntry_t *entry)
311{
312	dirCacheEntry_t *dce;
313	dirCache_t *cache;
314
315	if(entry->entry == -3) {
316		fprintf(stderr, "Attempt to write root directory pointer\n");
317		exit(1);
318	}
319
320	cache = allocDirCache(entry->Dir, entry->entry + 1);
321	if(!cache) {
322		fprintf(stderr, "Out of memory error in dir_write\n");
323		exit(1);
324	}
325	dce = cache->entries[entry->entry];
326	if(dce) {
327		if(entry->dir.name[0] == DELMARK) {
328			addFreeEntry(cache, dce->beginSlot, dce->endSlot);
329		} else {
330			dce->dir = entry->dir;
331		}
332	}
333	low_level_dir_write(entry);
334}
335
336
337/*
338 * The following function translates a series of vfat_subentries into
339 * data suitable for a dircache entry
340 */
341static __inline__ void parse_vses(direntry_t *entry,
342				  struct vfat_state *v)
343{
344	struct vfat_subentry *vse;
345	unsigned char id, last_flag;
346	wchar_t *c;
347
348	vse = (struct vfat_subentry *) &entry->dir;
349
350	id = vse->id & VSE_MASK;
351	last_flag = (vse->id & VSE_LAST);
352	if (id > MAX_VFAT_SUBENTRIES) {
353		fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n",
354			id, entry->entry);
355		return;
356	}
357
358/* 950819: This code enforced finding the VSEs in order.  Well, Win95
359 * likes to write them in *reverse* order for some bizarre reason!  So
360 * we pretty much have to tolerate them coming in any possible order.
361 * So skip this check, we'll do without it (What does this do, Alain?).
362 *
363 * 950820: Totally rearranged code to tolerate any order but to warn if
364 * they are not in reverse order like Win95 uses.
365 *
366 * 950909: Tolerate any order. We recognize new chains by mismatching
367 * checksums. In the event that the checksums match, new entries silently
368 * overwrite old entries of the same id. This should accept all valid
369 * entries, but may fail to reject invalid entries in some rare cases.
370 */
371
372	/* bad checksum, begin new chain */
373	if(v->sum != vse->sum) {
374		clear_vfat(v);
375		v->sum = vse->sum;
376	}
377
378#ifdef DEBUG
379	if(v->status & (1 << (id-1)))
380		fprintf(stderr,
381			"parse_vses: duplicate VSE %d\n", vse->id);
382#endif
383
384	v->status |= 1 << (id-1);
385	if(last_flag)
386		v->subentries = id;
387
388#ifdef DEBUG
389	if (id > v->subentries)
390		/* simple test to detect entries preceding
391		 * the "last" entry (really the first) */
392		fprintf(stderr,
393			"parse_vses: new VSE %d sans LAST flag\n",
394			vse->id);
395#endif
396
397	c = &(v->name[VSE_NAMELEN * (id-1)]);
398	c += unicode_read(vse->text1, c, VSE1SIZE);
399	c += unicode_read(vse->text2, c, VSE2SIZE);
400	c += unicode_read(vse->text3, c, VSE3SIZE);
401#ifdef DEBUG
402	printf("Read VSE %d at %d, subentries=%d, = (%13ls).\n",
403	       id,entry->entry,v->subentries,&(v->name[VSE_NAMELEN * (id-1)]));
404#endif
405	if (last_flag)
406		*c = '\0';	/* Null terminate long name */
407}
408
409
410static dirCacheEntry_t *vfat_lookup_loop_common(doscp_t *cp,
411						direntry_t *direntry,
412						dirCache_t *cache,
413						int lookForFreeSpace,
414						int *io_error)
415{
416	wchar_t newfile[13];
417	int initpos = direntry->entry + 1;
418	struct vfat_state vfat;
419	wchar_t *longname;
420	int error;
421
422	/* not yet cached */
423	*io_error = 0;
424	clear_vfat(&vfat);
425	while(1) {
426		++direntry->entry;
427		if(!dir_read(direntry, &error)){
428			if(error) {
429			    *io_error = error;
430			    return NULL;
431			}
432			addFreeEntry(cache, initpos, direntry->entry);
433			return addEndEntry(cache, direntry->entry);
434		}
435
436		if (direntry->dir.name[0] == '\0'){
437				/* the end of the directory */
438			if(lookForFreeSpace)
439				continue;
440			return addEndEntry(cache, direntry->entry);
441		}
442		if(direntry->dir.name[0] != DELMARK &&
443		   direntry->dir.attr == 0x0f)
444			parse_vses(direntry, &vfat);
445		else
446			/* the main entry */
447			break;
448	}
449
450	/* If we get here, it's a short name FAT entry, maybe erased.
451	 * thus we should make sure that the vfat structure will be
452	 * cleared before the next loop run */
453
454	/* deleted file */
455	if (direntry->dir.name[0] == DELMARK) {
456		return addFreeEntry(cache, initpos,
457				    direntry->entry + 1);
458	}
459
460	check_vfat(&vfat, &direntry->dir);
461	if(!vfat.present)
462		vfat.subentries = 0;
463
464	/* mark space between last entry and this one as free */
465	addFreeEntry(cache, initpos,
466		     direntry->entry - vfat.subentries);
467
468	if (direntry->dir.attr & 0x8){
469		/* Read entry as a label */
470		wchar_t *ptr = newfile;
471		ptr += dos_to_wchar(cp, direntry->dir.name, ptr, 8);
472		ptr += dos_to_wchar(cp, direntry->dir.ext, ptr, 3);
473		*ptr = '\0';
474	} else
475		unix_name(cp,
476			  direntry->dir.name,
477			  direntry->dir.ext,
478			  direntry->dir.Case,
479			  newfile);
480
481	if(vfat.present)
482		longname = vfat.name;
483	else
484		longname = 0;
485
486	return addUsedEntry(cache, direntry->entry - vfat.subentries,
487			    direntry->entry + 1, longname,
488			    newfile, &direntry->dir);
489}
490
491static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_read(doscp_t *cp,
492							     direntry_t *direntry,
493							     dirCache_t *cache,
494							     int *io_error)
495{
496	int initpos = direntry->entry + 1;
497	dirCacheEntry_t *dce;
498
499	*io_error = 0;
500	dce = cache->entries[initpos];
501	if(dce) {
502		direntry->entry = dce->endSlot - 1;
503		return dce;
504	} else {
505		return vfat_lookup_loop_common(cp,
506					       direntry, cache, 0, io_error);
507	}
508}
509
510
511typedef enum result_t {
512	RES_NOMATCH,
513	RES_MATCH,
514	RES_END,
515	RES_ERROR
516} result_t;
517
518
519/*
520 * 0 does not match
521 * 1 matches
522 * 2 end
523 */
524static result_t checkNameForMatch(struct direntry_t *direntry,
525				  dirCacheEntry_t *dce,
526				  const wchar_t *filename,
527				  int length,
528				  int flags)
529{
530	switch(dce->type) {
531		case DCET_FREE:
532			return RES_NOMATCH;
533		case DCET_END:
534			return RES_END;
535		case DCET_USED:
536			break;
537		default:
538			fprintf(stderr, "Unexpected entry type %d\n",
539				dce->type);
540			return RES_ERROR;
541	}
542
543	direntry->dir = dce->dir;
544
545	/* make sure the entry is of an accepted type */
546	if((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL))
547		return RES_NOMATCH;
548
549
550	/*---------- multiple files ----------*/
551	if(!((flags & MATCH_ANY) ||
552	     (dce->longName &&
553	      match(dce->longName, filename, direntry->name, 0, length)) ||
554	     match(dce->shortName, filename, direntry->name, 1, length))) {
555
556		return RES_NOMATCH;
557	}
558
559	/* entry of non-requested type, has to come after name
560	 * checking because of clash handling */
561	if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) {
562		if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
563			char tmp[4*13+1];
564			wchar_to_native(dce->shortName,tmp,13);
565			fprintf(stderr, "Skipping \"%s\", is a directory\n",
566				tmp);
567		}
568		return RES_NOMATCH;
569	}
570
571	if(!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) &&
572	   !(flags & ACCEPT_PLAIN)) {
573		if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
574			char tmp[4*13+1];
575			wchar_to_native(dce->shortName,tmp,13);
576			fprintf(stderr,
577				"Skipping \"%s\", is not a directory\n",
578				tmp);
579		}
580		return RES_NOMATCH;
581	}
582
583	return RES_MATCH;
584}
585
586
587/*
588 * vfat_lookup looks for filenames in directory dir.
589 * if a name if found, it is returned in outname
590 * if applicable, the file is opened and its stream is returned in File
591 */
592
593int vfat_lookup(direntry_t *direntry, const char *filename, int length,
594		int flags, char *shortname, char *longname)
595{
596	dirCacheEntry_t *dce;
597	result_t result;
598	dirCache_t *cache;
599	int io_error;
600	wchar_t wfilename[MAX_VNAMELEN+1];
601	wchar_t *wfilenamep = wfilename;
602	doscp_t *cp = GET_DOSCONVERT(direntry->Dir);
603
604	if(length == -1 && filename)
605		length = strlen(filename);
606
607	if(filename != NULL)
608		length = native_to_wchar(filename, wfilename, MAX_VNAMELEN,
609					 filename+length, 0);
610	else {
611		wfilenamep = NULL;
612		length = 0;
613	}
614
615	if (direntry->entry == -2)
616		return -1;
617
618	cache = allocDirCache(direntry->Dir, direntry->entry+1);
619	if(!cache) {
620		fprintf(stderr, "Out of memory error in vfat_lookup [0]\n");
621		exit(1);
622	}
623
624	do {
625		dce = vfat_lookup_loop_for_read(cp, direntry, cache, &io_error);
626		if(!dce) {
627			if (io_error)
628				return -2;
629			fprintf(stderr, "Out of memory error in vfat_lookup\n");
630			exit(1);
631		}
632		result = checkNameForMatch(direntry, dce,
633					   wfilename,
634					   length, flags);
635	} while(result == RES_NOMATCH);
636
637	if(result == RES_MATCH){
638		if(longname){
639			if(dce->longName)
640				wchar_to_native(dce->longName,
641						longname, MAX_VNAMELEN);
642			else
643				*longname ='\0';
644		}
645		if(shortname)
646			wchar_to_native(dce->shortName, shortname, 12);
647		direntry->beginSlot = dce->beginSlot;
648		direntry->endSlot = dce->endSlot-1;
649		return 0; /* file found */
650	} else {
651		direntry->entry = -2;
652		return -1; /* no file found */
653	}
654}
655
656static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_insert(doscp_t *cp,
657							       direntry_t *direntry,
658							       int initpos,
659							       dirCache_t *cache)
660{
661	dirCacheEntry_t *dce;
662	int io_error;
663
664	dce = cache->entries[initpos];
665	if(dce && dce->type != DCET_END) {
666		return dce;
667	} else {
668		direntry->entry = initpos - 1;
669		dce = vfat_lookup_loop_common(cp,
670					      direntry, cache, 1, &io_error);
671		if(!dce) {
672			if (io_error) {
673				return NULL;
674			}
675			fprintf(stderr,
676				"Out of memory error in vfat_lookup_loop\n");
677			exit(1);
678		}
679		return cache->entries[initpos];
680	}
681}
682
683static void accountFreeSlots(struct scan_state *ssp, dirCacheEntry_t *dce)
684{
685	if(ssp->got_slots)
686		return;
687
688	if(ssp->free_end != dce->beginSlot) {
689		ssp->free_start = dce->beginSlot;
690	}
691	ssp->free_end = dce->endSlot;
692
693	if(ssp->free_end - ssp->free_start >= ssp->size_needed) {
694		ssp->got_slots = 1;
695		ssp->slot = ssp->free_start + ssp->size_needed - 1;
696	}
697}
698
699static void clear_scan(wchar_t *longname, int use_longname,
700		       struct scan_state *s)
701{
702	s->shortmatch = s->longmatch = s->slot = -1;
703	s->free_end = s->got_slots = s->free_start = 0;
704
705	if (use_longname)
706		s->size_needed = 1 +
707			(wcslen(longname) + VSE_NAMELEN - 1)/VSE_NAMELEN;
708	else
709                s->size_needed = 1;
710}
711
712/* lookup_for_insert replaces the old scandir function.  It directly
713 * calls into vfat_lookup_loop, thus eliminating the overhead of the
714 * normal vfat_lookup
715 */
716int lookupForInsert(Stream_t *Dir,
717		    struct direntry_t *direntry,
718		    dos_name_t *dosname,
719		    char *longname,
720		    struct scan_state *ssp,
721		    int ignore_entry,
722		    int source_entry,
723		    int pessimisticShortRename,
724		    int use_longname)
725{
726	direntry_t entry;
727	int ignore_match;
728	dirCacheEntry_t *dce;
729	dirCache_t *cache;
730	int pos; /* position _before_ the next answered entry */
731	wchar_t shortName[13];
732	wchar_t wlongname[MAX_VNAMELEN+1];
733	doscp_t *cp = GET_DOSCONVERT(Dir);
734
735	native_to_wchar(longname, wlongname, MAX_VNAMELEN+1, 0, 0);
736	clear_scan(wlongname, use_longname, ssp);
737
738	ignore_match = (ignore_entry == -2 );
739
740	initializeDirentry(&entry, Dir);
741	ssp->match_free = 0;
742
743	/* hash bitmap of already encountered names.  Speeds up batch appends
744	 * to huge directories, because in the best case, we only need to scan
745	 * the new entries rather than the whole directory */
746	cache = allocDirCache(Dir, 1);
747	if(!cache) {
748		fprintf(stderr, "Out of memory error in lookupForInsert\n");
749		exit(1);
750	}
751
752	if(!ignore_match)
753		unix_name(cp, dosname->base, dosname->ext, 0, shortName);
754
755	pos = cache->nrHashed;
756	if(source_entry >= 0 ||
757	   (pos && isHashed(cache, wlongname))) {
758		pos = 0;
759	} else if(pos && !ignore_match && isHashed(cache, shortName)) {
760		if(pessimisticShortRename) {
761			ssp->shortmatch = -2;
762			return 1;
763		}
764		pos = 0;
765	} else if(growDirCache(cache, pos) < 0) {
766		fprintf(stderr, "Out of memory error in vfat_looup [0]\n");
767		exit(1);
768	}
769	do {
770		dce = vfat_lookup_loop_for_insert(cp, &entry, pos, cache);
771		switch(dce->type) {
772			case DCET_FREE:
773				accountFreeSlots(ssp, dce);
774				break;
775			case DCET_USED:
776				if(!(dce->dir.attr & 0x8) &&
777				   (signed int)dce->endSlot-1 == source_entry)
778				   accountFreeSlots(ssp, dce);
779
780				/* labels never match, neither does the
781				 * ignored entry */
782				if( (dce->dir.attr & 0x8) ||
783				    ((signed int)dce->endSlot-1==ignore_entry))
784					break;
785
786				/* check long name */
787				if((dce->longName &&
788				    !wcscasecmp(dce->longName, wlongname)) ||
789				   (dce->shortName &&
790				    !wcscasecmp(dce->shortName, wlongname))) {
791					ssp->longmatch = dce->endSlot - 1;
792					/* long match is a reason for
793					 * immediate stop */
794					direntry->beginSlot = dce->beginSlot;
795					direntry->endSlot = dce->endSlot-1;
796					return 1;
797				}
798
799				/* Long name or not, always check for
800				 * short name match */
801				if (!ignore_match &&
802				    !wcscasecmp(shortName, dce->shortName))
803					ssp->shortmatch = dce->endSlot - 1;
804				break;
805			case DCET_END:
806				break;
807		}
808		pos = dce->endSlot;
809	} while(dce->type != DCET_END);
810	if (ssp->shortmatch > -1)
811		return 1;
812	ssp->max_entry = dce->beginSlot;
813	if (ssp->got_slots)
814		return 6;	/* Success */
815
816	/* Need more room.  Can we grow the directory? */
817	if(!isRootDir(Dir))
818		return 5;	/* OK, try to grow the directory */
819
820	fprintf(stderr, "No directory slots\n");
821	return -1;
822}
823
824
825
826/* End vfat.c */
827