Release 960805
[wine] / files / dos_fs.c
1 /*
2  * DOS file system functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <sys/types.h>
9 #include <ctype.h>
10 #include <dirent.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <time.h>
16 #if defined(__svr4__) || defined(_SCO_DS)
17 #include <sys/statfs.h>
18 #endif
19
20 #include "windows.h"
21 #include "dos_fs.h"
22 #include "drive.h"
23 #include "file.h"
24 #include "msdos.h"
25 #include "stddebug.h"
26 #include "debug.h"
27 #include "xmalloc.h"
28 #include "string32.h"
29
30 /* Chars we don't want to see in DOS file names */
31 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
32
33 static const char *DOSFS_Devices[][2] =
34 {
35     { "CON",  "" },
36     { "PRN",  "" },
37     { "NUL",  "/dev/null" },
38     { "AUX",  "" },
39     { "LPT1", "" },
40     { "LPT2", "" },
41     { "LPT3", "" },
42     { "LPT4", "" },
43     { "COM1", "" },
44     { "COM2", "" },
45     { "COM3", "" },
46     { "COM4", "" }
47 };
48
49 #define GET_DRIVE(path) \
50     (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
51
52     /* DOS extended error status */
53 WORD DOS_ExtendedError;
54 BYTE DOS_ErrorClass;
55 BYTE DOS_ErrorAction;
56 BYTE DOS_ErrorLocus;
57
58
59 /***********************************************************************
60  *           DOSFS_ValidDOSName
61  *
62  * Return 1 if Unix file 'name' is also a valid MS-DOS name
63  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
64  * File name can be terminated by '\0', '\\' or '/'.
65  */
66 static int DOSFS_ValidDOSName( const char *name )
67 {
68     static const char invalid_chars[] = INVALID_DOS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
69     const char *p = name;
70     int len = 0;
71
72     if (*p == '.')
73     {
74         /* Check for "." and ".." */
75         p++;
76         if (*p == '.') p++;
77         /* All other names beginning with '.' are invalid */
78         return (IS_END_OF_NAME(*p));
79     }
80     while (!IS_END_OF_NAME(*p))
81     {
82         if (strchr( invalid_chars, *p )) return 0;  /* Invalid char */
83         if (*p == '.') break;  /* Start of the extension */
84         if (++len > 8) return 0;  /* Name too long */
85         p++;
86     }
87     if (*p != '.') return 1;  /* End of name */
88     p++;
89     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
90     len = 0;
91     while (!IS_END_OF_NAME(*p))
92     {
93         if (strchr( invalid_chars, *p )) return 0;  /* Invalid char */
94         if (*p == '.') return 0;  /* Second extension not allowed */
95         if (++len > 3) return 0;  /* Extension too long */
96         p++;
97     }
98     return 1;
99 }
100
101
102 /***********************************************************************
103  *           DOSFS_CheckDotDot
104  *
105  * Remove all '.' and '..' at the beginning of 'name'.
106  */
107 static const char * DOSFS_CheckDotDot( const char *name, char *buffer,
108                                        char sep , int *len )
109 {
110     char *p = buffer + strlen(buffer);
111
112     while (*name == '.')
113     {
114         if (IS_END_OF_NAME(name[1]))
115         {
116             name++;
117             while ((*name == '\\') || (*name == '/')) name++;
118         }
119         else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
120         {
121             name += 2;
122             while ((*name == '\\') || (*name == '/')) name++;
123             while ((p > buffer) && (*p != sep)) { p--; (*len)++; }
124             *p = '\0';  /* Remove trailing separator */
125         }
126         else break;
127     }
128     return name;
129 }
130
131
132 /***********************************************************************
133  *           DOSFS_ToDosFCBFormat
134  *
135  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
136  * expanding wild cards and converting to upper-case in the process.
137  * File name can be terminated by '\0', '\\' or '/'.
138  * Return NULL if the name is not a valid DOS name.
139  */
140 const char *DOSFS_ToDosFCBFormat( const char *name )
141 {
142     static const char invalid_chars[] = INVALID_DOS_CHARS;
143     static char buffer[12];
144     const char *p = name;
145     int i;
146
147     /* Check for "." and ".." */
148     if (*p == '.')
149     {
150         p++;
151         strcpy( buffer, ".          " );
152         if (*p == '.') p++;
153         return (!*p || (*p == '/') || (*p == '\\')) ? buffer : NULL;
154     }
155
156     for (i = 0; i < 8; i++)
157     {
158         switch(*p)
159         {
160         case '\0':
161         case '\\':
162         case '/':
163         case '.':
164             buffer[i] = ' ';
165             break;
166         case '?':
167             p++;
168             /* fall through */
169         case '*':
170             buffer[i] = '?';
171             break;
172         default:
173             if (strchr( invalid_chars, *p )) return NULL;
174             buffer[i] = toupper(*p);
175             p++;
176             break;
177         }
178     }
179
180     if (*p == '*')
181     {
182         /* Skip all chars after wildcard up to first dot */
183         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
184     }
185     else
186     {
187         /* Check if name too long */
188         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return NULL;
189     }
190     if (*p == '.') p++;  /* Skip dot */
191
192     for (i = 8; i < 11; i++)
193     {
194         switch(*p)
195         {
196         case '\0':
197         case '\\':
198         case '/':
199             buffer[i] = ' ';
200             break;
201         case '.':
202             return NULL;  /* Second extension not allowed */
203         case '?':
204             p++;
205             /* fall through */
206         case '*':
207             buffer[i] = '?';
208             break;
209         default:
210             if (strchr( invalid_chars, *p )) return NULL;
211             buffer[i] = toupper(*p);
212             p++;
213             break;
214         }
215     }
216     buffer[11] = '\0';
217     return buffer;
218 }
219
220
221 /***********************************************************************
222  *           DOSFS_ToDosDTAFormat
223  *
224  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
225  * converting to upper-case in the process.
226  * File name can be terminated by '\0', '\\' or '/'.
227  * Return NULL if the name is not a valid DOS name.
228  */
229 const char *DOSFS_ToDosDTAFormat( const char *name )
230 {
231     static char buffer[13];
232     char *p;
233
234     memcpy( buffer, name, 8 );
235     for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
236     *p++ = '.';
237     memcpy( p, name + 8, 3 );
238     for (p += 3; p[-1] == ' '; p--);
239     if (p[-1] == '.') p--;
240     *p = '\0';
241     return buffer;
242 }
243
244
245 /***********************************************************************
246  *           DOSFS_Match
247  *
248  * Check a DOS file name against a mask (both in FCB format).
249  */
250 int DOSFS_Match( const char *mask, const char *name )
251 {
252     int i;
253     for (i = 11; i > 0; i--, mask++, name++)
254         if ((*mask != '?') && (*mask != *name)) return 0;
255     return 1;
256 }
257
258
259 /***********************************************************************
260  *           DOSFS_ToDosDateTime
261  *
262  * Convert a Unix time in the DOS date/time format.
263  */
264 void DOSFS_ToDosDateTime( time_t unixtime, WORD *pDate, WORD *pTime )
265 {
266     struct tm *tm = localtime( &unixtime );
267     if (pTime)
268         *pTime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
269     if (pDate)
270         *pDate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
271                  + tm->tm_mday;
272 }
273
274
275 /***********************************************************************
276  *           DOSFS_Hash
277  *
278  * Transform a Unix file name into a hashed DOS name. If the name is a valid
279  * DOS name, it is converted to upper-case; otherwise it is replaced by a
280  * hashed version that fits in 8.3 format.
281  * File name can be terminated by '\0', '\\' or '/'.
282  */
283 const char *DOSFS_Hash( const char *name, int dir_format )
284 {
285     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
286     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
287
288     static char buffer[13];
289     const char *p, *ext;
290     char *dst;
291     unsigned short hash;
292     int i;
293
294     if (dir_format) strcpy( buffer, "           " );
295
296     if (DOSFS_ValidDOSName( name ))
297     {
298         /* Check for '.' and '..' */
299         if (*name == '.')
300         {
301             buffer[0] = '.';
302             if (!dir_format) buffer[1] = buffer[2] = '\0';
303             if (name[1] == '.') buffer[1] = '.';
304             return buffer;
305         }
306
307         /* Simply copy the name, converting to uppercase */
308
309         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
310             *dst++ = toupper(*name);
311         if (*name == '.')
312         {
313             if (dir_format) dst = buffer + 8;
314             else *dst++ = '.';
315             for (name++; !IS_END_OF_NAME(*name); name++)
316                 *dst++ = toupper(*name);
317         }
318         if (!dir_format) *dst = '\0';
319     }
320     else
321     {
322         /* Compute the hash code of the file name */
323         /* If you know something about hash functions, feel free to */
324         /* insert a better algorithm here... */
325         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
326             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
327         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
328         
329         /* Find last dot for start of the extension */
330         for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
331             if (*p == '.') ext = p;
332         if (ext && IS_END_OF_NAME(ext[1]))
333             ext = NULL;  /* Empty extension ignored */
334
335         /* Copy first 4 chars, replacing invalid chars with '_' */
336         for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
337         {
338             if (IS_END_OF_NAME(*p) || (p == ext)) break;
339             *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
340         }
341         /* Pad to 5 chars with '~' */
342         while (i-- >= 0) *dst++ = '~';
343
344         /* Insert hash code converted to 3 ASCII chars */
345         *dst++ = hash_chars[(hash >> 10) & 0x1f];
346         *dst++ = hash_chars[(hash >> 5) & 0x1f];
347         *dst++ = hash_chars[hash & 0x1f];
348
349         /* Copy the first 3 chars of the extension (if any) */
350         if (ext)
351         {
352             if (!dir_format) *dst++ = '.';
353             for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
354                 *dst++ = toupper(*ext);
355         }
356         if (!dir_format) *dst = '\0';
357     }
358     return buffer;
359 }
360
361
362 /***********************************************************************
363  *           DOSFS_FindUnixName
364  *
365  * Find the Unix file name in a given directory that corresponds to
366  * a file name (either in Unix or DOS format).
367  * File name can be terminated by '\0', '\\' or '/'.
368  * Return 1 if OK, 0 if no file name matches.
369  */
370 static int DOSFS_FindUnixName( const char *path, const char *name,
371                                char *buffer, int maxlen )
372 {
373     DIR *dir;
374     struct dirent *dirent;
375
376     const char *dos_name = DOSFS_ToDosFCBFormat( name );
377     const char *p = strchr( name, '/' );
378     int len = p ? (int)(p - name) : strlen(name);
379
380     dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s %s\n", path, name );
381
382     if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
383
384     if (!(dir = opendir( path )))
385     {
386         dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
387                        path, name );
388         return 0;
389     }
390     while ((dirent = readdir( dir )) != NULL)
391     {
392         /* Check against Unix name */
393         if ((len == strlen(dirent->d_name) &&
394              !memcmp( dirent->d_name, name, len ))) break;
395         if (dos_name)
396         {
397             /* Check against hashed DOS name */
398             const char *hash_name = DOSFS_Hash( dirent->d_name, TRUE );
399             if (!strcmp( dos_name, hash_name )) break;
400         }
401     }
402     if (dirent) lstrcpyn32A( buffer, dirent->d_name, maxlen );
403     closedir( dir );
404     dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s\n",
405                    path, name, dirent ? buffer : "** Not found **" );
406     return (dirent != NULL);
407 }
408
409
410 /***********************************************************************
411  *           DOSFS_IsDevice
412  *
413  * Check if a DOS file name represents a DOS device. Returns the name
414  * of the associated Unix device, or NULL if not found.
415  */
416 const char *DOSFS_IsDevice( const char *name )
417 {
418     int i;
419     const char *p;
420
421     if (name[0] && (name[1] == ':')) name += 2;
422     if ((p = strrchr( name, '/' ))) name = p + 1;
423     if ((p = strrchr( name, '\\' ))) name = p + 1;
424     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
425     {
426         const char *dev = DOSFS_Devices[i][0];
427         if (!lstrncmpi32A( dev, name, strlen(dev) ))
428         {
429             p = name + strlen( dev );
430             if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
431         }
432     }
433     return NULL;
434 }
435
436
437 /***********************************************************************
438  *           DOSFS_GetUnixFileName
439  *
440  * Convert a file name (DOS or mixed DOS/Unix format) to a valid Unix name.
441  * Return NULL if one of the path components does not exist. The last path
442  * component is only checked if 'check_last' is non-zero.
443  */
444 const char * DOSFS_GetUnixFileName( const char * name, int check_last )
445 {
446     static char buffer[MAX_PATHNAME_LEN];
447     int drive, len, found;
448     char *p, *root;
449
450     dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: %s\n", name );
451     if (name[0] && (name[1] == ':'))
452     {
453         drive = toupper(name[0]) - 'A';
454         name += 2;
455     }
456     else if (name[0] == '/') /* Absolute Unix path? */
457     {
458         if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
459         {
460             fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
461                      name );
462             /* Assume it really was a DOS name */
463             drive = DRIVE_GetCurrentDrive();            
464         }
465     }
466     else drive = DRIVE_GetCurrentDrive();
467
468     if (!DRIVE_IsValid(drive))
469     {
470         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
471         return NULL;
472     }
473     lstrcpyn32A( buffer, DRIVE_GetRoot(drive), MAX_PATHNAME_LEN );
474     if (buffer[1]) root = buffer + strlen(buffer);
475     else root = buffer;  /* root directory */
476
477     if ((*name == '\\') || (*name == '/'))
478     {
479         while ((*name == '\\') || (*name == '/')) name++;
480     }
481     else
482     {
483         lstrcpyn32A( root + 1, DRIVE_GetUnixCwd(drive),
484                      MAX_PATHNAME_LEN - (int)(root - buffer) - 1 );
485         if (root[1]) *root = '/';
486     }
487
488     p = buffer[1] ? buffer + strlen(buffer) : buffer;
489     len = MAX_PATHNAME_LEN - strlen(buffer);
490     found = 1;
491     while (*name && found)
492     {
493         const char *newname = DOSFS_CheckDotDot( name, root, '/', &len );
494         if (newname != name)
495         {
496             p = root + strlen(root);
497             name = newname;
498             continue;
499         }
500         if (len <= 1)
501         {
502             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
503             return NULL;
504         }
505         if ((found = DOSFS_FindUnixName( buffer, name, p+1, len-1 )))
506         {
507             *p = '/';
508             len -= strlen(p);
509             p += strlen(p);
510             while (!IS_END_OF_NAME(*name)) name++;
511         }
512         else if (!check_last)
513         {
514             *p++ = '/';
515             for (len--; !IS_END_OF_NAME(*name) && (len > 1); name++, len--)
516                 *p++ = tolower(*name);
517             *p = '\0';
518         }
519         while ((*name == '\\') || (*name == '/')) name++;
520     }
521     if (!found)
522     {
523         if (check_last)
524         {
525             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
526             return NULL;
527         }
528         if (*name)  /* Not last */
529         {
530             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
531             return NULL;
532         }
533     }
534     if (!buffer[0]) strcpy( buffer, "/" );
535     dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: returning %s\n", buffer );
536     return buffer;
537 }
538
539
540 /***********************************************************************
541  *           DOSFS_GetDosTrueName
542  *
543  * Convert a file name (DOS or Unix format) to a complete DOS name.
544  * Return NULL if the path name is invalid or too long.
545  * The unix_format flag is a hint that the file name is in Unix format.
546  */
547 const char * DOSFS_GetDosTrueName( const char *name, int unix_format )
548 {
549     static char buffer[MAX_PATHNAME_LEN];
550     int drive, len;
551     char *p;
552
553     dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName(%s,%d)\n", name, unix_format);
554     if (name[0] && (name[1] == ':'))
555     {
556         drive = toupper(name[0]) - 'A';
557         name += 2;
558     }
559     else if (name[0] == '/') /* Absolute Unix path? */
560     {
561         if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
562         {
563             fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
564                      name );
565             /* Assume it really was a DOS name */
566             drive = DRIVE_GetCurrentDrive();            
567         }
568     }
569     else drive = DRIVE_GetCurrentDrive();
570
571     if (!DRIVE_IsValid(drive))
572     {
573         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
574         return NULL;
575     }
576
577     p = buffer;
578     *p++ = 'A' + drive;
579     *p++ = ':';
580     if (IS_END_OF_NAME(*name))
581     {
582         while ((*name == '\\') || (*name == '/')) name++;
583     }
584     else
585     {
586         *p++ = '\\';
587         lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
588         if (*p) p += strlen(p); else p--;
589     }
590     *p = '\0';
591     len = MAX_PATHNAME_LEN - (int)(p - buffer);
592
593     while (*name)
594     {
595         const char *newname = DOSFS_CheckDotDot( name, buffer+2, '\\', &len );
596         if (newname != name)
597         {
598             p = buffer + strlen(buffer);
599             name = newname;
600             continue;
601         }
602         if (len <= 1)
603         {
604             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
605             return NULL;
606         }
607         *p++ = '\\';
608         if (unix_format)  /* Hash it into a DOS name */
609         {
610             lstrcpyn32A( p, DOSFS_Hash( name, FALSE ), len );
611             len -= strlen(p);
612             p += strlen(p);
613             while (!IS_END_OF_NAME(*name)) name++;
614         }
615         else  /* Already DOS format, simply upper-case it */
616         {
617             while (!IS_END_OF_NAME(*name) && (len > 1))
618             {
619                 *p++ = toupper(*name);
620                 name++;
621                 len--;
622             }
623             *p = '\0';
624         }
625         while ((*name == '\\') || (*name == '/')) name++;
626     }
627     if (!buffer[2])
628     {
629         buffer[2] = '\\';
630         buffer[3] = '\0';
631     }
632     dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName: returning %s\n", buffer );
633     return buffer;
634 }
635
636
637 /***********************************************************************
638  *           DOSFS_FindNext
639  *
640  * Find the next matching file. Return the number of entries read to find
641  * the matching one, or 0 if no more entries.
642  */
643 int DOSFS_FindNext( const char *path, const char *mask, int drive,
644                     BYTE attr, int skip, DOS_DIRENT *entry )
645 {
646     static DIR *dir = NULL;
647     struct dirent *dirent;
648     int count = 0;
649     static char buffer[MAX_PATHNAME_LEN];
650     static int cur_pos = 0;
651     static int drive_root = 0;
652     char *p;
653     const char *hash_name;
654     
655     if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
656     {
657         if (skip) return 0;
658         strcpy( entry->name, DRIVE_GetLabel( drive ) );
659         entry->attr = FA_LABEL;
660         entry->size = 0;
661         DOSFS_ToDosDateTime( time(NULL), &entry->date, &entry->time );
662         return 1;
663     }
664
665     /* Check the cached directory */
666     if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
667     else  /* Not in the cache, open it anew */
668     {
669         const char *drive_path;
670         dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
671                        path, skip, buffer, cur_pos );
672         cur_pos = skip;
673         if (dir) closedir(dir);
674         if (!(dir = opendir( path ))) return 0;
675         drive_path = path;
676         drive_root = 0;
677         if (DRIVE_FindDriveRoot( &drive_path ) != -1)
678         {
679             while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
680             if (!*drive_path) drive_root = 1;
681         }
682         dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
683         lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
684         
685     }
686     strcat( buffer, "/" );
687     p = buffer + strlen(buffer);
688     attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
689
690     while ((dirent = readdir( dir )) != NULL)
691     {
692         if (skip-- > 0) continue;
693         count++;
694         hash_name = DOSFS_Hash( dirent->d_name, TRUE );
695         if (!DOSFS_Match( mask, hash_name )) continue;
696         /* Don't return '.' and '..' in the root of the drive */
697         if (drive_root && (dirent->d_name[0] == '.') &&
698             (!dirent->d_name[1] ||
699              ((dirent->d_name[1] == '.') && !dirent->d_name[2]))) continue;
700         lstrcpyn32A( p, dirent->d_name, sizeof(buffer) - (int)(p - buffer) );
701
702         if (!FILE_Stat( buffer, &entry->attr, &entry->size,
703                         &entry->date, &entry->time ))
704         {
705             fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
706             continue;
707         }
708         if (entry->attr & ~attr) continue;
709         strcpy( entry->name, hash_name );
710         lstrcpyn32A( entry->unixname, dirent->d_name, sizeof(entry->unixname));
711         dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s %02x %ld\n",
712                        entry->name, entry->attr, entry->size );
713         cur_pos += count;
714         p[-1] = '\0';  /* Remove trailing slash in buffer */
715         return count;
716     }
717     closedir( dir );
718     dir = NULL;
719     return 0;  /* End of directory */
720 }
721
722
723 /***********************************************************************
724  *           GetShortPathNameA   (KERNEL32.271)
725  */
726 DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
727 {
728     LPCSTR dostruename;
729
730     dprintf_dosfs( stddeb, "GetShortPathName32A(%s,%p,%ld)\n",
731                    longpath, shortpath, shortlen );
732
733     dostruename = DOSFS_GetDosTrueName( longpath, 0 );
734     lstrcpyn32A( shortpath, dostruename, shortlen );
735     return strlen(dostruename);
736 }
737
738
739 /***********************************************************************
740  *           GetShortPathNameW   (KERNEL32.272)
741  */
742 DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
743 {
744     LPSTR longpatha = STRING32_DupUniToAnsi( longpath );
745     LPCSTR dostruename = DOSFS_GetDosTrueName( longpatha, 0 );
746     free( longpatha );
747     lstrcpynAtoW( shortpath, dostruename, shortlen );
748     return strlen(dostruename);
749 }