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