Release 961222
[wine/gsoc-2012-control.git] / files / dos_fs.c
blob6ce588106ef47e08f70ab9536c6056ed7b05e0af
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
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
20 #include "windows.h"
21 #include "dos_fs.h"
22 #include "drive.h"
23 #include "file.h"
24 #include "heap.h"
25 #include "msdos.h"
26 #include "stddebug.h"
27 #include "debug.h"
28 #include "xmalloc.h"
30 /* Chars we don't want to see in DOS file names */
31 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
33 static const char *DOSFS_Devices[][2] =
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", "" }
49 #define GET_DRIVE(path) \
50 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
52 /* DOS extended error status */
53 WORD DOS_ExtendedError;
54 BYTE DOS_ErrorClass;
55 BYTE DOS_ErrorAction;
56 BYTE DOS_ErrorLocus;
59 /***********************************************************************
60 * DOSFS_ValidDOSName
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 '/'.
66 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
68 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
69 const char *p = name;
70 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
71 int len = 0;
73 if (*p == '.')
75 /* Check for "." and ".." */
76 p++;
77 if (*p == '.') p++;
78 /* All other names beginning with '.' are invalid */
79 return (IS_END_OF_NAME(*p));
81 while (!IS_END_OF_NAME(*p))
83 if (strchr( invalid, *p )) return 0; /* Invalid char */
84 if (*p == '.') break; /* Start of the extension */
85 if (++len > 8) return 0; /* Name too long */
86 p++;
88 if (*p != '.') return 1; /* End of name */
89 p++;
90 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
91 len = 0;
92 while (!IS_END_OF_NAME(*p))
94 if (strchr( invalid, *p )) return 0; /* Invalid char */
95 if (*p == '.') return 0; /* Second extension not allowed */
96 if (++len > 3) return 0; /* Extension too long */
97 p++;
99 return 1;
103 /***********************************************************************
104 * DOSFS_CheckDotDot
106 * Remove all '.' and '..' at the beginning of 'name'.
108 static const char * DOSFS_CheckDotDot( const char *name, char *buffer,
109 char sep , int *len )
111 char *p = buffer + strlen(buffer);
113 while (*name == '.')
115 if (IS_END_OF_NAME(name[1]))
117 name++;
118 while ((*name == '\\') || (*name == '/')) name++;
120 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
122 name += 2;
123 while ((*name == '\\') || (*name == '/')) name++;
124 while ((p > buffer) && (*p != sep)) { p--; (*len)++; }
125 *p = '\0'; /* Remove trailing separator */
127 else break;
129 return name;
133 /***********************************************************************
134 * DOSFS_ToDosFCBFormat
136 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
137 * expanding wild cards and converting to upper-case in the process.
138 * File name can be terminated by '\0', '\\' or '/'.
139 * Return NULL if the name is not a valid DOS name.
141 const char *DOSFS_ToDosFCBFormat( const char *name )
143 static const char invalid_chars[] = INVALID_DOS_CHARS;
144 static char buffer[12];
145 const char *p = name;
146 int i;
148 /* Check for "." and ".." */
149 if (*p == '.')
151 p++;
152 strcpy( buffer, ". " );
153 if (*p == '.') p++;
154 return (!*p || (*p == '/') || (*p == '\\')) ? buffer : NULL;
157 for (i = 0; i < 8; i++)
159 switch(*p)
161 case '\0':
162 case '\\':
163 case '/':
164 case '.':
165 buffer[i] = ' ';
166 break;
167 case '?':
168 p++;
169 /* fall through */
170 case '*':
171 buffer[i] = '?';
172 break;
173 default:
174 if (strchr( invalid_chars, *p )) return NULL;
175 buffer[i] = toupper(*p);
176 p++;
177 break;
181 if (*p == '*')
183 /* Skip all chars after wildcard up to first dot */
184 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
186 else
188 /* Check if name too long */
189 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return NULL;
191 if (*p == '.') p++; /* Skip dot */
193 for (i = 8; i < 11; i++)
195 switch(*p)
197 case '\0':
198 case '\\':
199 case '/':
200 buffer[i] = ' ';
201 break;
202 case '.':
203 return NULL; /* Second extension not allowed */
204 case '?':
205 p++;
206 /* fall through */
207 case '*':
208 buffer[i] = '?';
209 break;
210 default:
211 if (strchr( invalid_chars, *p )) return NULL;
212 buffer[i] = toupper(*p);
213 p++;
214 break;
217 buffer[11] = '\0';
218 return buffer;
222 /***********************************************************************
223 * DOSFS_ToDosDTAFormat
225 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
226 * converting to upper-case in the process.
227 * File name can be terminated by '\0', '\\' or '/'.
228 * Return NULL if the name is not a valid DOS name.
230 const char *DOSFS_ToDosDTAFormat( const char *name )
232 static char buffer[13];
233 char *p;
235 memcpy( buffer, name, 8 );
236 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
237 *p++ = '.';
238 memcpy( p, name + 8, 3 );
239 for (p += 3; p[-1] == ' '; p--);
240 if (p[-1] == '.') p--;
241 *p = '\0';
242 return buffer;
246 /***********************************************************************
247 * DOSFS_MatchShort
249 * Check a DOS file name against a mask (both in FCB format).
251 static int DOSFS_MatchShort( const char *mask, const char *name )
253 int i;
254 for (i = 11; i > 0; i--, mask++, name++)
255 if ((*mask != '?') && (*mask != *name)) return 0;
256 return 1;
260 /***********************************************************************
261 * DOSFS_MatchLong
263 * Check a long file name against a mask.
265 static int DOSFS_MatchLong( const char *mask, const char *name,
266 int case_sensitive )
268 while (*name && *mask)
270 if (*mask == '*')
272 mask++;
273 while (*mask == '*') mask++; /* Skip consecutive '*' */
274 if (!*mask) return 1;
275 if (case_sensitive) while (*name && (*name != *mask)) name++;
276 else while (*name && (toupper(*name) != toupper(*mask))) name++;
277 if (!*name) return 0;
279 else if (*mask != '?')
281 if (case_sensitive)
283 if (*mask != *name) return 0;
285 else if (toupper(*mask) != toupper(*name)) return 0;
287 mask++;
288 name++;
290 return (!*name && !*mask);
294 /***********************************************************************
295 * DOSFS_ToDosDateTime
297 * Convert a Unix time in the DOS date/time format.
299 void DOSFS_ToDosDateTime( time_t unixtime, WORD *pDate, WORD *pTime )
301 struct tm *tm = localtime( &unixtime );
302 if (pTime)
303 *pTime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
304 if (pDate)
305 *pDate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
306 + tm->tm_mday;
309 /***********************************************************************
310 * DOSFS_DosDateTimeToUnixTime
312 * Convert from the DOS (FAT) date/time format into Unix time
313 * (borrowed from files/file.c)
315 time_t DOSFS_DosDateTimeToUnixTime( WORD date, WORD time )
317 struct tm newtm;
319 newtm.tm_sec = (time & 0x1f) * 2;
320 newtm.tm_min = (time >> 5) & 0x3f;
321 newtm.tm_hour = (time >> 11);
322 newtm.tm_mday = (date & 0x1f);
323 newtm.tm_mon = ((date >> 5) & 0x0f) - 1;
324 newtm.tm_year = (date >> 9) + 80;
325 return mktime( &newtm );
329 /***********************************************************************
330 * DOSFS_UnixTimeToFileTime
332 * Convert a Unix time to FILETIME format.
334 void DOSFS_UnixTimeToFileTime( time_t unixtime, FILETIME *filetime )
336 /* FIXME :-) */
337 filetime->dwLowDateTime = unixtime;
338 filetime->dwHighDateTime = 0;
342 /***********************************************************************
343 * DOSFS_FileTimeToUnixTime
345 * Convert a FILETIME format to Unix time.
347 time_t DOSFS_FileTimeToUnixTime( FILETIME *filetime )
349 /* FIXME :-) */
350 return filetime->dwLowDateTime;
354 /***********************************************************************
355 * DOSFS_Hash
357 * Transform a Unix file name into a hashed DOS name. If the name is a valid
358 * DOS name, it is converted to upper-case; otherwise it is replaced by a
359 * hashed version that fits in 8.3 format.
360 * File name can be terminated by '\0', '\\' or '/'.
362 const char *DOSFS_Hash( const char *name, int dir_format, int ignore_case )
364 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
365 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
367 static char buffer[13];
368 const char *p, *ext;
369 char *dst;
370 unsigned short hash;
371 int i;
373 if (dir_format) strcpy( buffer, " " );
375 if (DOSFS_ValidDOSName( name, ignore_case ))
377 /* Check for '.' and '..' */
378 if (*name == '.')
380 buffer[0] = '.';
381 if (!dir_format) buffer[1] = buffer[2] = '\0';
382 if (name[1] == '.') buffer[1] = '.';
383 return buffer;
386 /* Simply copy the name, converting to uppercase */
388 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
389 *dst++ = toupper(*name);
390 if (*name == '.')
392 if (dir_format) dst = buffer + 8;
393 else *dst++ = '.';
394 for (name++; !IS_END_OF_NAME(*name); name++)
395 *dst++ = toupper(*name);
397 if (!dir_format) *dst = '\0';
399 else
401 /* Compute the hash code of the file name */
402 /* If you know something about hash functions, feel free to */
403 /* insert a better algorithm here... */
404 if (ignore_case)
406 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
407 hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
408 hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p); /* Last character*/
410 else
412 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
413 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
414 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
417 /* Find last dot for start of the extension */
418 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
419 if (*p == '.') ext = p;
420 if (ext && IS_END_OF_NAME(ext[1]))
421 ext = NULL; /* Empty extension ignored */
423 /* Copy first 4 chars, replacing invalid chars with '_' */
424 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
426 if (IS_END_OF_NAME(*p) || (p == ext)) break;
427 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
429 /* Pad to 5 chars with '~' */
430 while (i-- >= 0) *dst++ = '~';
432 /* Insert hash code converted to 3 ASCII chars */
433 *dst++ = hash_chars[(hash >> 10) & 0x1f];
434 *dst++ = hash_chars[(hash >> 5) & 0x1f];
435 *dst++ = hash_chars[hash & 0x1f];
437 /* Copy the first 3 chars of the extension (if any) */
438 if (ext)
440 if (!dir_format) *dst++ = '.';
441 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
442 *dst++ = toupper(*ext);
444 if (!dir_format) *dst = '\0';
446 return buffer;
450 /***********************************************************************
451 * DOSFS_FindUnixName
453 * Find the Unix file name in a given directory that corresponds to
454 * a file name (either in Unix or DOS format).
455 * File name can be terminated by '\0', '\\' or '/'.
456 * Return 1 if OK, 0 if no file name matches.
458 static int DOSFS_FindUnixName( const char *path, const char *name,
459 char *buffer, int maxlen, UINT32 drive_flags )
461 DIR *dir;
462 struct dirent *dirent;
464 const char *dos_name = DOSFS_ToDosFCBFormat( name );
465 const char *p = strchr( name, '/' );
466 int len = p ? (int)(p - name) : strlen(name);
468 dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s %s\n", path, name );
470 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
472 if (!(dir = opendir( path )))
474 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
475 path, name );
476 return 0;
478 while ((dirent = readdir( dir )) != NULL)
480 /* Check against Unix name */
481 if (len == strlen(dirent->d_name))
483 if (drive_flags & DRIVE_CASE_SENSITIVE)
485 if (!lstrncmp32A( dirent->d_name, name, len )) break;
487 else
489 if (!lstrncmpi32A( dirent->d_name, name, len )) break;
492 if (dos_name)
494 /* Check against hashed DOS name */
495 const char *hash_name = DOSFS_Hash( dirent->d_name, TRUE,
496 !(drive_flags & DRIVE_CASE_SENSITIVE) );
497 if (!strcmp( dos_name, hash_name )) break;
500 if (dirent) lstrcpyn32A( buffer, dirent->d_name, maxlen );
501 closedir( dir );
502 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s\n",
503 path, name, dirent ? buffer : "** Not found **" );
504 return (dirent != NULL);
508 /***********************************************************************
509 * DOSFS_IsDevice
511 * Check if a DOS file name represents a DOS device. Returns the name
512 * of the associated Unix device, or NULL if not found.
514 const char *DOSFS_IsDevice( const char *name )
516 int i;
517 const char *p;
519 if (name[0] && (name[1] == ':')) name += 2;
520 if ((p = strrchr( name, '/' ))) name = p + 1;
521 if ((p = strrchr( name, '\\' ))) name = p + 1;
522 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
524 const char *dev = DOSFS_Devices[i][0];
525 if (!lstrncmpi32A( dev, name, strlen(dev) ))
527 p = name + strlen( dev );
528 if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
531 return NULL;
535 /***********************************************************************
536 * DOSFS_GetUnixFileName
538 * Convert a file name (DOS or mixed DOS/Unix format) to a valid Unix name.
539 * Return NULL if one of the path components does not exist. The last path
540 * component is only checked if 'check_last' is non-zero.
542 const char * DOSFS_GetUnixFileName( const char * name, int check_last )
544 static char buffer[MAX_PATHNAME_LEN];
545 int drive, len, found;
546 UINT32 flags;
547 char *p, *root;
549 dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: %s\n", name );
550 if (name[0] && (name[1] == ':'))
552 drive = toupper(name[0]) - 'A';
553 name += 2;
555 else if (name[0] == '/') /* Absolute Unix path? */
557 if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
559 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
560 name );
561 /* Assume it really was a DOS name */
562 drive = DRIVE_GetCurrentDrive();
565 else drive = DRIVE_GetCurrentDrive();
567 if (!DRIVE_IsValid(drive))
569 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
570 return NULL;
572 flags = DRIVE_GetFlags(drive);
573 lstrcpyn32A( buffer, DRIVE_GetRoot(drive), MAX_PATHNAME_LEN );
574 if (buffer[1]) root = buffer + strlen(buffer);
575 else root = buffer; /* root directory */
577 if ((*name == '\\') || (*name == '/'))
579 while ((*name == '\\') || (*name == '/')) name++;
581 else
583 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd(drive),
584 MAX_PATHNAME_LEN - (int)(root - buffer) - 1 );
585 if (root[1]) *root = '/';
588 p = buffer[1] ? buffer + strlen(buffer) : buffer;
589 len = MAX_PATHNAME_LEN - strlen(buffer);
590 found = 1;
591 while (*name && found)
593 const char *newname = DOSFS_CheckDotDot( name, root, '/', &len );
594 if (newname != name)
596 p = root + strlen(root);
597 name = newname;
598 continue;
600 if (len <= 1)
602 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
603 return NULL;
605 if ((found = DOSFS_FindUnixName( buffer, name, p+1, len-1, flags )))
607 *p = '/';
608 len -= strlen(p);
609 p += strlen(p);
610 while (!IS_END_OF_NAME(*name)) name++;
612 else if (!check_last)
614 *p++ = '/';
615 for (len--; !IS_END_OF_NAME(*name) && (len > 1); name++, len--)
616 *p++ = tolower(*name);
617 *p = '\0';
619 while ((*name == '\\') || (*name == '/')) name++;
621 if (!found)
623 if (check_last)
625 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
626 return NULL;
628 if (*name) /* Not last */
630 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
631 return NULL;
634 if (!buffer[0]) strcpy( buffer, "/" );
635 dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: returning %s\n", buffer );
636 return buffer;
640 /***********************************************************************
641 * DOSFS_GetDosTrueName
643 * Convert a file name (DOS or Unix format) to a complete DOS name.
644 * Return NULL if the path name is invalid or too long.
645 * The unix_format flag is a hint that the file name is in Unix format.
647 const char * DOSFS_GetDosTrueName( const char *name, int unix_format )
649 static char buffer[MAX_PATHNAME_LEN];
650 int drive, len;
651 UINT32 flags;
652 char *p;
654 dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName(%s,%d)\n", name, unix_format);
655 if (name[0] && (name[1] == ':'))
657 drive = toupper(name[0]) - 'A';
658 name += 2;
660 else if (name[0] == '/') /* Absolute Unix path? */
662 if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
664 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
665 name );
666 /* Assume it really was a DOS name */
667 drive = DRIVE_GetCurrentDrive();
670 else drive = DRIVE_GetCurrentDrive();
672 if (!DRIVE_IsValid(drive))
674 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
675 return NULL;
678 p = buffer;
679 *p++ = 'A' + drive;
680 *p++ = ':';
681 if (IS_END_OF_NAME(*name))
683 while ((*name == '\\') || (*name == '/')) name++;
685 else
687 *p++ = '\\';
688 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
689 if (*p) p += strlen(p); else p--;
691 *p = '\0';
692 len = MAX_PATHNAME_LEN - (int)(p - buffer);
693 flags = DRIVE_GetFlags(drive);
695 while (*name)
697 const char *newname = DOSFS_CheckDotDot( name, buffer+2, '\\', &len );
698 if (newname != name)
700 p = buffer + strlen(buffer);
701 name = newname;
702 continue;
704 if (len <= 1)
706 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
707 return NULL;
709 *p++ = '\\';
710 if (unix_format) /* Hash it into a DOS name */
712 lstrcpyn32A( p, DOSFS_Hash( name, FALSE,
713 !(flags & DRIVE_CASE_SENSITIVE) ),
714 len );
715 len -= strlen(p);
716 p += strlen(p);
717 while (!IS_END_OF_NAME(*name)) name++;
719 else /* Already DOS format, simply upper-case it */
721 while (!IS_END_OF_NAME(*name) && (len > 1))
723 *p++ = toupper(*name);
724 name++;
725 len--;
727 *p = '\0';
729 while ((*name == '\\') || (*name == '/')) name++;
731 if (!buffer[2])
733 buffer[2] = '\\';
734 buffer[3] = '\0';
736 dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName: returning %s\n", buffer );
737 return buffer;
741 /***********************************************************************
742 * DOSFS_FindNext
744 * Find the next matching file. Return the number of entries read to find
745 * the matching one, or 0 if no more entries.
746 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
747 * file name mask. Either or both can be NULL.
749 int DOSFS_FindNext( const char *path, const char *short_mask,
750 const char *long_mask, int drive, BYTE attr,
751 int skip, DOS_DIRENT *entry )
753 static DIR *dir = NULL;
754 struct dirent *dirent;
755 int count = 0;
756 static char buffer[MAX_PATHNAME_LEN];
757 static int cur_pos = 0;
758 static int drive_root = 0;
759 char *p;
760 const char *hash_name;
761 UINT32 flags;
763 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
765 if (skip) return 0;
766 strcpy( entry->name, DRIVE_GetLabel( drive ) );
767 entry->attr = FA_LABEL;
768 entry->size = 0;
769 DOSFS_ToDosDateTime( time(NULL), &entry->date, &entry->time );
770 return 1;
773 /* Check the cached directory */
774 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
775 else /* Not in the cache, open it anew */
777 const char *drive_path;
778 dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
779 path, skip, buffer, cur_pos );
780 cur_pos = skip;
781 if (dir) closedir(dir);
782 if (!*path) path = "/";
783 if (!(dir = opendir(path))) return 0;
784 drive_path = path;
785 drive_root = 0;
786 if (DRIVE_FindDriveRoot( &drive_path ) != -1)
788 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
789 if (!*drive_path) drive_root = 1;
791 dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
792 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
795 strcat( buffer, "/" );
796 p = buffer + strlen(buffer);
797 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
798 flags = DRIVE_GetFlags( drive );
799 hash_name = NULL;
801 while ((dirent = readdir( dir )) != NULL)
803 if (skip-- > 0) continue;
804 count++;
806 /* Don't return '.' and '..' in the root of the drive */
807 if (drive_root && (dirent->d_name[0] == '.') &&
808 (!dirent->d_name[1] ||
809 ((dirent->d_name[1] == '.') && !dirent->d_name[2]))) continue;
811 /* Check the long mask */
813 if (long_mask)
815 if (!DOSFS_MatchLong( long_mask, dirent->d_name,
816 flags & DRIVE_CASE_SENSITIVE )) continue;
819 /* Check the short mask */
821 if (short_mask)
823 hash_name = DOSFS_Hash( dirent->d_name, TRUE,
824 !(flags & DRIVE_CASE_SENSITIVE) );
825 if (!DOSFS_MatchShort( short_mask, hash_name )) continue;
828 /* Check the file attributes */
830 lstrcpyn32A( p, dirent->d_name, sizeof(buffer) - (int)(p - buffer) );
831 if (!FILE_Stat( buffer, &entry->attr, &entry->size,
832 &entry->date, &entry->time ))
834 fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
835 continue;
837 if (entry->attr & ~attr) continue;
839 /* We now have a matching entry; fill the result and return */
841 if (!hash_name)
842 hash_name = DOSFS_Hash( dirent->d_name, TRUE,
843 !(flags & DRIVE_CASE_SENSITIVE) );
844 strcpy( entry->name, hash_name );
845 lstrcpyn32A( entry->unixname, dirent->d_name, sizeof(entry->unixname));
846 if (!(flags & DRIVE_CASE_PRESERVING)) AnsiLower( entry->unixname );
847 dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s %02x %ld\n",
848 entry->name, entry->attr, entry->size );
849 cur_pos += count;
850 p[-1] = '\0'; /* Remove trailing slash in buffer */
851 return count;
853 closedir( dir );
854 dir = NULL;
855 return 0; /* End of directory */
859 /***********************************************************************
860 * GetShortPathNameA (KERNEL32.271)
862 DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
864 LPCSTR dostruename;
866 dprintf_dosfs( stddeb, "GetShortPathName32A(%s,%p,%ld)\n",
867 longpath, shortpath, shortlen );
869 dostruename = DOSFS_GetDosTrueName( longpath, TRUE );
870 lstrcpyn32A( shortpath, dostruename, shortlen );
871 return strlen(dostruename);
875 /***********************************************************************
876 * GetShortPathNameW (KERNEL32.272)
878 DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
880 LPSTR longpatha = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
881 LPCSTR dostruename = DOSFS_GetDosTrueName( longpatha, TRUE );
882 HeapFree( GetProcessHeap(), 0, longpatha );
883 lstrcpynAtoW( shortpath, dostruename, shortlen );
884 return strlen(dostruename);
888 /***********************************************************************
889 * GetFullPathNameA (KERNEL32.272)
891 DWORD GetFullPathName32A( LPCSTR fn, DWORD buflen, LPSTR buf, LPSTR *lastpart)
893 dprintf_file(stddeb,"GetFullPathNameA(%s)\n",fn);
894 /* FIXME */
895 if (buf) {
896 lstrcpyn32A(buf,fn,buflen);
897 if (lastpart) {
898 *lastpart = strrchr(buf,'\\');
899 if (!*lastpart) *lastpart=buf;
902 return strlen(fn);
905 /***********************************************************************
906 * GetFullPathName32W (KERNEL32.273)
908 DWORD GetFullPathName32W(LPCWSTR fn,DWORD buflen,LPWSTR buf,LPWSTR *lastpart) {
909 LPWSTR x;
911 dprintf_file(stddeb,"GetFullPathNameW(%p)\n",fn);
912 /* FIXME */
913 if (buf) {
914 lstrcpyn32W(buf,fn,buflen);
915 if (lastpart) {
916 x = buf+lstrlen32W(buf)-1;
917 while (x>=buf && *x!='\\')
918 x--;
919 if (x>=buf)
920 *lastpart=x;
921 else
922 *lastpart=buf;
925 return lstrlen32W(fn);
929 /***********************************************************************
930 * DosDateTimeToFileTime (KERNEL32.76)
932 BOOL32 DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft )
934 time_t unixtime = DOSFS_DosDateTimeToUnixTime(fatdate,fattime);
935 DOSFS_UnixTimeToFileTime(unixtime,ft);
936 return TRUE;
940 /***********************************************************************
941 * FileTimeToDosDateTime (KERNEL32.111)
943 BOOL32 FileTimeToDosDateTime( LPFILETIME ft, LPWORD fatdate, LPWORD fattime)
945 time_t unixtime = DOSFS_FileTimeToUnixTime(ft);
946 DOSFS_ToDosDateTime(unixtime,fatdate,fattime);
947 return TRUE;
951 /***********************************************************************
952 * LocalFileTimeToFileTime (KERNEL32.373)
954 BOOL32 LocalFileTimeToFileTime( LPFILETIME localft, LPFILETIME utcft )
956 struct tm *xtm;
958 /* convert from local to UTC. Perhaps not correct. FIXME */
959 xtm = gmtime((time_t*)&(localft->dwLowDateTime));
960 utcft->dwLowDateTime = mktime(xtm);
961 utcft->dwHighDateTime = 0;
962 return TRUE;
966 /***********************************************************************
967 * FileTimeToLocalFileTime (KERNEL32.112)
969 BOOL32 FileTimeToLocalFileTime( LPFILETIME utcft, LPFILETIME localft )
971 struct tm *xtm;
973 /* convert from UTC to local. Perhaps not correct. FIXME */
974 xtm = localtime((time_t*)&(utcft->dwLowDateTime));
975 localft->dwLowDateTime = mktime(xtm);
976 localft->dwHighDateTime = 0;
977 return TRUE;
981 /***********************************************************************
982 * FileTimeToSystemTime (KERNEL32.113)
984 BOOL32 FileTimeToSystemTime( LPFILETIME ft, LPSYSTEMTIME syst )
986 struct tm *xtm;
987 time_t xtime = DOSFS_FileTimeToUnixTime(ft);
988 xtm = gmtime(&xtime);
989 syst->wYear = xtm->tm_year;
990 syst->wMonth = xtm->tm_mon;
991 syst->wDayOfWeek = xtm->tm_wday;
992 syst->wDay = xtm->tm_mday;
993 syst->wHour = xtm->tm_hour;
994 syst->wMinute = xtm->tm_min;
995 syst->wSecond = xtm->tm_sec;
996 syst->wMilliseconds = 0; /* FIXME */
997 return TRUE;
1001 /***********************************************************************
1002 * SystemTimeToFileTime (KERNEL32.526)
1004 BOOL32 SystemTimeToFileTime( LPSYSTEMTIME syst, LPFILETIME ft )
1006 struct tm xtm;
1008 xtm.tm_year = syst->wYear;
1009 xtm.tm_mon = syst->wMonth;
1010 xtm.tm_wday = syst->wDayOfWeek;
1011 xtm.tm_mday = syst->wDay;
1012 xtm.tm_hour = syst->wHour;
1013 xtm.tm_min = syst->wMinute;
1014 xtm.tm_sec = syst->wSecond;
1015 DOSFS_UnixTimeToFileTime(mktime(&xtm),ft);
1016 return TRUE;