Fixed WM_GETTEXTLENGTH handling.
[wine/multimedia.git] / files / dos_fs.c
blob6860b57da9c845b91eef367f37070e1943c1bdf7
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
9 #include <sys/types.h>
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #ifdef HAVE_SYS_ERRNO_H
14 #include <sys/errno.h>
15 #endif
16 #include <fcntl.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <time.h>
22 #include <unistd.h>
24 #include "windef.h"
25 #include "ntddk.h"
26 #include "winnls.h"
27 #include "wine/winbase16.h"
28 #include "wine/unicode.h"
29 #include "winerror.h"
30 #include "drive.h"
31 #include "file.h"
32 #include "heap.h"
33 #include "msdos.h"
34 #include "server.h"
35 #include "options.h"
36 #include "debugtools.h"
38 DEFAULT_DEBUG_CHANNEL(dosfs);
39 DECLARE_DEBUG_CHANNEL(file);
41 /* Define the VFAT ioctl to get both short and long file names */
42 /* FIXME: is it possible to get this to work on other systems? */
43 #ifdef linux
44 /* We want the real kernel dirent structure, not the libc one */
45 typedef struct
47 long d_ino;
48 long d_off;
49 unsigned short d_reclen;
50 char d_name[256];
51 } KERNEL_DIRENT;
53 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
55 #else /* linux */
56 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
57 #endif /* linux */
59 /* Chars we don't want to see in DOS file names */
60 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
62 static const DOS_DEVICE DOSFS_Devices[] =
63 /* name, device flags (see Int 21/AX=0x4400) */
65 { "CON", 0xc0d3 },
66 { "PRN", 0xa0c0 },
67 { "NUL", 0x80c4 },
68 { "AUX", 0x80c0 },
69 { "LPT1", 0xa0c0 },
70 { "LPT2", 0xa0c0 },
71 { "LPT3", 0xa0c0 },
72 { "LPT4", 0xc0d3 },
73 { "COM1", 0x80c0 },
74 { "COM2", 0x80c0 },
75 { "COM3", 0x80c0 },
76 { "COM4", 0x80c0 },
77 { "SCSIMGR$", 0xc0c0 },
78 { "HPSCAN", 0xc0c0 }
81 #define GET_DRIVE(path) \
82 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
84 /* Directory info for DOSFS_ReadDir */
85 typedef struct
87 DIR *dir;
88 #ifdef VFAT_IOCTL_READDIR_BOTH
89 int fd;
90 char short_name[12];
91 KERNEL_DIRENT dirent[2];
92 #endif
93 } DOS_DIR;
95 /* Info structure for FindFirstFile handle */
96 typedef struct
98 LPSTR path;
99 LPSTR long_mask;
100 LPSTR short_mask;
101 BYTE attr;
102 int drive;
103 int cur_pos;
104 DOS_DIR *dir;
105 } FIND_FIRST_INFO;
109 /***********************************************************************
110 * DOSFS_ValidDOSName
112 * Return 1 if Unix file 'name' is also a valid MS-DOS name
113 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
114 * File name can be terminated by '\0', '\\' or '/'.
116 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
118 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
119 const char *p = name;
120 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
121 int len = 0;
123 if (*p == '.')
125 /* Check for "." and ".." */
126 p++;
127 if (*p == '.') p++;
128 /* All other names beginning with '.' are invalid */
129 return (IS_END_OF_NAME(*p));
131 while (!IS_END_OF_NAME(*p))
133 if (strchr( invalid, *p )) return 0; /* Invalid char */
134 if (*p == '.') break; /* Start of the extension */
135 if (++len > 8) return 0; /* Name too long */
136 p++;
138 if (*p != '.') return 1; /* End of name */
139 p++;
140 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
141 len = 0;
142 while (!IS_END_OF_NAME(*p))
144 if (strchr( invalid, *p )) return 0; /* Invalid char */
145 if (*p == '.') return 0; /* Second extension not allowed */
146 if (++len > 3) return 0; /* Extension too long */
147 p++;
149 return 1;
153 /***********************************************************************
154 * DOSFS_ToDosFCBFormat
156 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
157 * expanding wild cards and converting to upper-case in the process.
158 * File name can be terminated by '\0', '\\' or '/'.
159 * Return FALSE if the name is not a valid DOS name.
160 * 'buffer' must be at least 12 characters long.
162 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
164 static const char invalid_chars[] = INVALID_DOS_CHARS;
165 const char *p = name;
166 int i;
168 /* Check for "." and ".." */
169 if (*p == '.')
171 p++;
172 strcpy( buffer, ". " );
173 if (*p == '.')
175 buffer[1] = '.';
176 p++;
178 return (!*p || (*p == '/') || (*p == '\\'));
181 for (i = 0; i < 8; i++)
183 switch(*p)
185 case '\0':
186 case '\\':
187 case '/':
188 case '.':
189 buffer[i] = ' ';
190 break;
191 case '?':
192 p++;
193 /* fall through */
194 case '*':
195 buffer[i] = '?';
196 break;
197 default:
198 if (strchr( invalid_chars, *p )) return FALSE;
199 buffer[i] = FILE_toupper(*p);
200 p++;
201 break;
205 if (*p == '*')
207 /* Skip all chars after wildcard up to first dot */
208 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
210 else
212 /* Check if name too long */
213 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
215 if (*p == '.') p++; /* Skip dot */
217 for (i = 8; i < 11; i++)
219 switch(*p)
221 case '\0':
222 case '\\':
223 case '/':
224 buffer[i] = ' ';
225 break;
226 case '.':
227 return FALSE; /* Second extension not allowed */
228 case '?':
229 p++;
230 /* fall through */
231 case '*':
232 buffer[i] = '?';
233 break;
234 default:
235 if (strchr( invalid_chars, *p )) return FALSE;
236 buffer[i] = FILE_toupper(*p);
237 p++;
238 break;
241 buffer[11] = '\0';
243 /* at most 3 character of the extension are processed
244 * is something behind this ?
246 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
247 return IS_END_OF_NAME(*p);
251 /***********************************************************************
252 * DOSFS_ToDosDTAFormat
254 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
255 * converting to upper-case in the process.
256 * File name can be terminated by '\0', '\\' or '/'.
257 * 'buffer' must be at least 13 characters long.
259 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
261 char *p;
263 memcpy( buffer, name, 8 );
264 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
265 *p++ = '.';
266 memcpy( p, name + 8, 3 );
267 for (p += 3; p[-1] == ' '; p--);
268 if (p[-1] == '.') p--;
269 *p = '\0';
273 /***********************************************************************
274 * DOSFS_MatchShort
276 * Check a DOS file name against a mask (both in FCB format).
278 static int DOSFS_MatchShort( const char *mask, const char *name )
280 int i;
281 for (i = 11; i > 0; i--, mask++, name++)
282 if ((*mask != '?') && (*mask != *name)) return 0;
283 return 1;
287 /***********************************************************************
288 * DOSFS_MatchLong
290 * Check a long file name against a mask.
292 * Tests (done in W95 DOS shell - case insensitive):
293 * *.txt test1.test.txt *
294 * *st1* test1.txt *
295 * *.t??????.t* test1.ta.tornado.txt *
296 * *tornado* test1.ta.tornado.txt *
297 * t*t test1.ta.tornado.txt *
298 * ?est* test1.txt *
299 * ?est??? test1.txt -
300 * *test1.txt* test1.txt *
301 * h?l?o*t.dat hellothisisatest.dat *
303 static int DOSFS_MatchLong( const char *mask, const char *name,
304 int case_sensitive )
306 const char *lastjoker = NULL;
307 const char *next_to_retry = NULL;
309 if (!strcmp( mask, "*.*" )) return 1;
310 while (*name && *mask)
312 if (*mask == '*')
314 mask++;
315 while (*mask == '*') mask++; /* Skip consecutive '*' */
316 lastjoker = mask;
317 if (!*mask) return 1; /* end of mask is all '*', so match */
319 /* skip to the next match after the joker(s) */
320 if (case_sensitive) while (*name && (*name != *mask)) name++;
321 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
323 if (!*name) break;
324 next_to_retry = name;
326 else if (*mask != '?')
328 int mismatch = 0;
329 if (case_sensitive)
331 if (*mask != *name) mismatch = 1;
333 else
335 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
337 if (!mismatch)
339 mask++;
340 name++;
341 if (*mask == '\0')
343 if (*name == '\0')
344 return 1;
345 if (lastjoker)
346 mask = lastjoker;
349 else /* mismatch ! */
351 if (lastjoker) /* we had an '*', so we can try unlimitedly */
353 mask = lastjoker;
355 /* this scan sequence was a mismatch, so restart
356 * 1 char after the first char we checked last time */
357 next_to_retry++;
358 name = next_to_retry;
360 else
361 return 0; /* bad luck */
364 else /* '?' */
366 mask++;
367 name++;
370 while ((*mask == '.') || (*mask == '*'))
371 mask++; /* Ignore trailing '.' or '*' in mask */
372 return (!*name && !*mask);
376 /***********************************************************************
377 * DOSFS_OpenDir
379 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
381 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
382 if (!dir)
384 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
385 return NULL;
388 /* Treat empty path as root directory. This simplifies path split into
389 directory and mask in several other places */
390 if (!*path) path = "/";
392 #ifdef VFAT_IOCTL_READDIR_BOTH
394 /* Check if the VFAT ioctl is supported on this directory */
396 if ((dir->fd = open( path, O_RDONLY )) != -1)
398 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
400 close( dir->fd );
401 dir->fd = -1;
403 else
405 /* Set the file pointer back at the start of the directory */
406 lseek( dir->fd, 0, SEEK_SET );
407 dir->dir = NULL;
408 return dir;
411 #endif /* VFAT_IOCTL_READDIR_BOTH */
413 /* Now use the standard opendir/readdir interface */
415 if (!(dir->dir = opendir( path )))
417 HeapFree( GetProcessHeap(), 0, dir );
418 return NULL;
420 return dir;
424 /***********************************************************************
425 * DOSFS_CloseDir
427 static void DOSFS_CloseDir( DOS_DIR *dir )
429 #ifdef VFAT_IOCTL_READDIR_BOTH
430 if (dir->fd != -1) close( dir->fd );
431 #endif /* VFAT_IOCTL_READDIR_BOTH */
432 if (dir->dir) closedir( dir->dir );
433 HeapFree( GetProcessHeap(), 0, dir );
437 /***********************************************************************
438 * DOSFS_ReadDir
440 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
441 LPCSTR *short_name )
443 struct dirent *dirent;
445 #ifdef VFAT_IOCTL_READDIR_BOTH
446 if (dir->fd != -1)
448 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
449 if (!dir->dirent[0].d_reclen) return FALSE;
450 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
451 dir->short_name[0] = '\0';
452 *short_name = dir->short_name;
453 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
454 else *long_name = dir->dirent[0].d_name;
455 return TRUE;
458 #endif /* VFAT_IOCTL_READDIR_BOTH */
460 if (!(dirent = readdir( dir->dir ))) return FALSE;
461 *long_name = dirent->d_name;
462 *short_name = NULL;
463 return TRUE;
467 /***********************************************************************
468 * DOSFS_Hash
470 * Transform a Unix file name into a hashed DOS name. If the name is a valid
471 * DOS name, it is converted to upper-case; otherwise it is replaced by a
472 * hashed version that fits in 8.3 format.
473 * File name can be terminated by '\0', '\\' or '/'.
474 * 'buffer' must be at least 13 characters long.
476 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
477 BOOL ignore_case )
479 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
480 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
482 const char *p, *ext;
483 char *dst;
484 unsigned short hash;
485 int i;
487 if (dir_format) strcpy( buffer, " " );
489 if (DOSFS_ValidDOSName( name, ignore_case ))
491 /* Check for '.' and '..' */
492 if (*name == '.')
494 buffer[0] = '.';
495 if (!dir_format) buffer[1] = buffer[2] = '\0';
496 if (name[1] == '.') buffer[1] = '.';
497 return;
500 /* Simply copy the name, converting to uppercase */
502 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
503 *dst++ = FILE_toupper(*name);
504 if (*name == '.')
506 if (dir_format) dst = buffer + 8;
507 else *dst++ = '.';
508 for (name++; !IS_END_OF_NAME(*name); name++)
509 *dst++ = FILE_toupper(*name);
511 if (!dir_format) *dst = '\0';
512 return;
515 /* Compute the hash code of the file name */
516 /* If you know something about hash functions, feel free to */
517 /* insert a better algorithm here... */
518 if (ignore_case)
520 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
521 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
522 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
524 else
526 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
527 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
528 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
531 /* Find last dot for start of the extension */
532 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
533 if (*p == '.') ext = p;
534 if (ext && IS_END_OF_NAME(ext[1]))
535 ext = NULL; /* Empty extension ignored */
537 /* Copy first 4 chars, replacing invalid chars with '_' */
538 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
540 if (IS_END_OF_NAME(*p) || (p == ext)) break;
541 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
543 /* Pad to 5 chars with '~' */
544 while (i-- >= 0) *dst++ = '~';
546 /* Insert hash code converted to 3 ASCII chars */
547 *dst++ = hash_chars[(hash >> 10) & 0x1f];
548 *dst++ = hash_chars[(hash >> 5) & 0x1f];
549 *dst++ = hash_chars[hash & 0x1f];
551 /* Copy the first 3 chars of the extension (if any) */
552 if (ext)
554 if (!dir_format) *dst++ = '.';
555 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
556 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
558 if (!dir_format) *dst = '\0';
562 /***********************************************************************
563 * DOSFS_FindUnixName
565 * Find the Unix file name in a given directory that corresponds to
566 * a file name (either in Unix or DOS format).
567 * File name can be terminated by '\0', '\\' or '/'.
568 * Return TRUE if OK, FALSE if no file name matches.
570 * 'long_buf' must be at least 'long_len' characters long. If the long name
571 * turns out to be larger than that, the function returns FALSE.
572 * 'short_buf' must be at least 13 characters long.
574 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
575 INT long_len, LPSTR short_buf, BOOL ignore_case)
577 DOS_DIR *dir;
578 LPCSTR long_name, short_name;
579 char dos_name[12], tmp_buf[13];
580 BOOL ret;
582 const char *p = strchr( name, '/' );
583 int len = p ? (int)(p - name) : strlen(name);
584 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
585 /* Ignore trailing dots and spaces */
586 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
587 if (long_len < len + 1) return FALSE;
589 TRACE("%s,%s\n", path, name );
591 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
593 if (!(dir = DOSFS_OpenDir( path )))
595 WARN("(%s,%s): can't open dir: %s\n",
596 path, name, strerror(errno) );
597 return FALSE;
600 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
602 /* Check against Unix name */
603 if (len == strlen(long_name))
605 if (!ignore_case)
607 if (!strncmp( long_name, name, len )) break;
609 else
611 if (!FILE_strncasecmp( long_name, name, len )) break;
614 if (dos_name[0])
616 /* Check against hashed DOS name */
617 if (!short_name)
619 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
620 short_name = tmp_buf;
622 if (!strcmp( dos_name, short_name )) break;
625 if (ret)
627 if (long_buf) strcpy( long_buf, long_name );
628 if (short_buf)
630 if (short_name)
631 DOSFS_ToDosDTAFormat( short_name, short_buf );
632 else
633 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
635 TRACE("(%s,%s) -> %s (%s)\n",
636 path, name, long_name, short_buf ? short_buf : "***");
638 else
639 WARN("'%s' not found in '%s'\n", name, path);
640 DOSFS_CloseDir( dir );
641 return ret;
645 /***********************************************************************
646 * DOSFS_GetDevice
648 * Check if a DOS file name represents a DOS device and return the device.
650 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
652 int i;
653 const char *p;
655 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
656 if (name[0] && (name[1] == ':')) name += 2;
657 if ((p = strrchr( name, '/' ))) name = p + 1;
658 if ((p = strrchr( name, '\\' ))) name = p + 1;
659 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
661 const char *dev = DOSFS_Devices[i].name;
662 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
664 p = name + strlen( dev );
665 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
668 return NULL;
672 /***********************************************************************
673 * DOSFS_GetDeviceByHandle
675 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
677 const DOS_DEVICE *ret = NULL;
678 SERVER_START_REQ
680 struct get_file_info_request *req = server_alloc_req( sizeof(*req), 0 );
682 req->handle = hFile;
683 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
685 if ((req->attr >= 0) &&
686 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
687 ret = &DOSFS_Devices[req->attr];
690 SERVER_END_REQ;
691 return ret;
695 /**************************************************************************
696 * DOSFS_CreateCommPort
698 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access)
700 HANDLE ret;
701 char devname[40];
703 TRACE("%s %lx\n", name, access);
705 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
706 if(!devname[0])
707 return 0;
709 TRACE("opening %s as %s\n", devname, name);
711 SERVER_START_REQ
713 size_t len = strlen(devname);
714 struct create_serial_request *req = server_alloc_req( sizeof(*req), len );
716 req->access = access;
717 req->inherit = 0; /*FIXME*/
718 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
719 memcpy( server_data_ptr(req), devname, len );
720 SetLastError(0);
721 server_call( REQ_CREATE_SERIAL );
722 ret = req->handle;
724 SERVER_END_REQ;
726 TRACE("return %08X\n", ret );
727 return ret;
730 /***********************************************************************
731 * DOSFS_OpenDevice
733 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
734 * Returns 0 on failure.
736 HANDLE DOSFS_OpenDevice( const char *name, DWORD access )
738 int i;
739 const char *p;
740 HANDLE handle;
742 if (name[0] && (name[1] == ':')) name += 2;
743 if ((p = strrchr( name, '/' ))) name = p + 1;
744 if ((p = strrchr( name, '\\' ))) name = p + 1;
745 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
747 const char *dev = DOSFS_Devices[i].name;
748 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
750 p = name + strlen( dev );
751 if (!*p || (*p == '.') || (*p == ':')) {
752 /* got it */
753 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
754 return FILE_CreateFile( "/dev/null", access,
755 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
756 OPEN_EXISTING, 0, 0, TRUE );
757 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
758 HANDLE to_dup;
759 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
760 case GENERIC_READ:
761 to_dup = GetStdHandle( STD_INPUT_HANDLE );
762 break;
763 case GENERIC_WRITE:
764 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
765 break;
766 default:
767 FIXME("can't open CON read/write\n");
768 return 0;
770 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
771 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
772 handle = 0;
773 return handle;
775 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
776 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
778 return FILE_CreateDevice( i, access, NULL );
781 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access)) )
782 return handle;
784 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
785 return 0;
789 return 0;
793 /***********************************************************************
794 * DOSFS_GetPathDrive
796 * Get the drive specified by a given path name (DOS or Unix format).
798 static int DOSFS_GetPathDrive( const char **name )
800 int drive;
801 const char *p = *name;
803 if (*p && (p[1] == ':'))
805 drive = FILE_toupper(*p) - 'A';
806 *name += 2;
808 else if (*p == '/') /* Absolute Unix path? */
810 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
812 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
813 /* Assume it really was a DOS name */
814 drive = DRIVE_GetCurrentDrive();
817 else drive = DRIVE_GetCurrentDrive();
819 if (!DRIVE_IsValid(drive))
821 SetLastError( ERROR_INVALID_DRIVE );
822 return -1;
824 return drive;
828 /***********************************************************************
829 * DOSFS_GetFullName
831 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
832 * Unix name / short DOS name pair.
833 * Return FALSE if one of the path components does not exist. The last path
834 * component is only checked if 'check_last' is non-zero.
835 * The buffers pointed to by 'long_buf' and 'short_buf' must be
836 * at least MAX_PATHNAME_LEN long.
838 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
840 BOOL found;
841 UINT flags;
842 char *p_l, *p_s, *root;
844 TRACE("%s (last=%d)\n", name, check_last );
846 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
847 flags = DRIVE_GetFlags( full->drive );
849 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
850 sizeof(full->long_name) );
851 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
852 else root = full->long_name; /* root directory */
854 strcpy( full->short_name, "A:\\" );
855 full->short_name[0] += full->drive;
857 if ((*name == '\\') || (*name == '/')) /* Absolute path */
859 while ((*name == '\\') || (*name == '/')) name++;
861 else /* Relative path */
863 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
864 sizeof(full->long_name) - (root - full->long_name) - 1 );
865 if (root[1]) *root = '/';
866 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
867 sizeof(full->short_name) - 3 );
870 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
871 : full->long_name;
872 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
873 : full->short_name + 2;
874 found = TRUE;
876 while (*name && found)
878 /* Check for '.' and '..' */
880 if (*name == '.')
882 if (IS_END_OF_NAME(name[1]))
884 name++;
885 while ((*name == '\\') || (*name == '/')) name++;
886 continue;
888 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
890 name += 2;
891 while ((*name == '\\') || (*name == '/')) name++;
892 while ((p_l > root) && (*p_l != '/')) p_l--;
893 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
894 *p_l = *p_s = '\0'; /* Remove trailing separator */
895 continue;
899 /* Make sure buffers are large enough */
901 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
902 (p_l >= full->long_name + sizeof(full->long_name) - 1))
904 SetLastError( ERROR_PATH_NOT_FOUND );
905 return FALSE;
908 /* Get the long and short name matching the file name */
910 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
911 sizeof(full->long_name) - (p_l - full->long_name) - 1,
912 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
914 *p_l++ = '/';
915 p_l += strlen(p_l);
916 *p_s++ = '\\';
917 p_s += strlen(p_s);
918 while (!IS_END_OF_NAME(*name)) name++;
920 else if (!check_last)
922 *p_l++ = '/';
923 *p_s++ = '\\';
924 while (!IS_END_OF_NAME(*name) &&
925 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
926 (p_l < full->long_name + sizeof(full->long_name) - 1))
928 *p_s++ = FILE_tolower(*name);
929 /* If the drive is case-sensitive we want to create new */
930 /* files in lower-case otherwise we can't reopen them */
931 /* under the same short name. */
932 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
933 else *p_l++ = *name;
934 name++;
936 /* Ignore trailing dots and spaces */
937 while(p_l[-1] == '.' || p_l[-1] == ' ') {
938 --p_l;
939 --p_s;
941 *p_l = *p_s = '\0';
943 while ((*name == '\\') || (*name == '/')) name++;
946 if (!found)
948 if (check_last)
950 SetLastError( ERROR_FILE_NOT_FOUND );
951 return FALSE;
953 if (*name) /* Not last */
955 SetLastError( ERROR_PATH_NOT_FOUND );
956 return FALSE;
959 if (!full->long_name[0]) strcpy( full->long_name, "/" );
960 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
961 TRACE("returning %s = %s\n", full->long_name, full->short_name );
962 return TRUE;
966 /***********************************************************************
967 * GetShortPathNameA (KERNEL32.271)
969 * NOTES
970 * observed:
971 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
972 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
974 * more observations ( with NT 3.51 (WinDD) ):
975 * longpath <= 8.3 -> just copy longpath to shortpath
976 * longpath > 8.3 ->
977 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
978 * b) file does exist -> set the short filename.
979 * - trailing slashes are reproduced in the short name, even if the
980 * file is not a directory
981 * - the absolute/relative path of the short name is reproduced like found
982 * in the long name
983 * - longpath and shortpath may have the same adress
984 * Peter Ganten, 1999
986 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
987 DWORD shortlen )
989 DOS_FULL_NAME full_name;
990 LPSTR tmpshortpath;
991 DWORD sp = 0, lp = 0;
992 int tmplen, drive;
993 UINT flags;
995 TRACE("%s\n", debugstr_a(longpath));
997 if (!longpath) {
998 SetLastError(ERROR_INVALID_PARAMETER);
999 return 0;
1001 if (!longpath[0]) {
1002 SetLastError(ERROR_BAD_PATHNAME);
1003 return 0;
1006 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1007 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1008 return 0;
1011 /* check for drive letter */
1012 if ( longpath[1] == ':' ) {
1013 tmpshortpath[0] = longpath[0];
1014 tmpshortpath[1] = ':';
1015 sp = 2;
1018 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1019 flags = DRIVE_GetFlags ( drive );
1021 while ( longpath[lp] ) {
1023 /* check for path delimiters and reproduce them */
1024 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1025 if (!sp || tmpshortpath[sp-1]!= '\\')
1027 /* strip double "\\" */
1028 tmpshortpath[sp] = '\\';
1029 sp++;
1031 tmpshortpath[sp]=0;/*terminate string*/
1032 lp++;
1033 continue;
1036 tmplen = strcspn ( longpath + lp, "\\/" );
1037 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1039 /* Check, if the current element is a valid dos name */
1040 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1041 sp += tmplen;
1042 lp += tmplen;
1043 continue;
1046 /* Check if the file exists and use the existing file name */
1047 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1048 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1049 sp += strlen ( tmpshortpath+sp );
1050 lp += tmplen;
1051 continue;
1054 TRACE("not found!\n" );
1055 SetLastError ( ERROR_FILE_NOT_FOUND );
1056 return 0;
1058 tmpshortpath[sp] = 0;
1060 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1061 TRACE("returning %s\n", debugstr_a(shortpath) );
1062 tmplen = strlen ( tmpshortpath );
1063 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1065 return tmplen;
1069 /***********************************************************************
1070 * GetShortPathNameW (KERNEL32.272)
1072 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1073 DWORD shortlen )
1075 LPSTR longpathA, shortpathA;
1076 DWORD ret = 0;
1078 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1079 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1081 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1082 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1083 shortpath[shortlen-1] = 0;
1084 HeapFree( GetProcessHeap(), 0, longpathA );
1085 HeapFree( GetProcessHeap(), 0, shortpathA );
1087 return ret;
1091 /***********************************************************************
1092 * GetLongPathNameA (KERNEL32.xxx)
1094 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1095 DWORD longlen )
1097 DOS_FULL_NAME full_name;
1098 char *p, *r, *ll, *ss;
1100 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1101 lstrcpynA( longpath, full_name.short_name, longlen );
1103 /* Do some hackery to get the long filename. */
1105 if (longpath) {
1106 ss=longpath+strlen(longpath);
1107 ll=full_name.long_name+strlen(full_name.long_name);
1108 p=NULL;
1109 while (ss>=longpath)
1111 /* FIXME: aren't we more paranoid, than needed? */
1112 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1113 p=ss;
1114 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1115 if (ss>=longpath)
1117 /* FIXME: aren't we more paranoid, than needed? */
1118 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1119 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1120 if (ll<full_name.long_name)
1122 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1123 ,ss ,ll );
1124 return 0;
1129 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1130 if (p && p[2])
1132 p+=1;
1133 if ((p-longpath)>0) longlen -= (p-longpath);
1134 lstrcpynA( p, ll , longlen);
1136 /* Now, change all '/' to '\' */
1137 for (r=p; r<(p+longlen); r++ )
1138 if (r[0]=='/') r[0]='\\';
1139 return strlen(longpath) - strlen(p) + longlen;
1143 return strlen(longpath);
1147 /***********************************************************************
1148 * GetLongPathNameW (KERNEL32.269)
1150 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1151 DWORD longlen )
1153 DOS_FULL_NAME full_name;
1154 DWORD ret = 0;
1155 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1157 /* FIXME: is it correct to always return a fully qualified short path? */
1158 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1160 ret = strlen( full_name.short_name );
1161 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1162 longpath, longlen ))
1163 longpath[longlen-1] = 0;
1165 HeapFree( GetProcessHeap(), 0, shortpathA );
1166 return ret;
1170 /***********************************************************************
1171 * DOSFS_DoGetFullPathName
1173 * Implementation of GetFullPathNameA/W.
1175 * bon@elektron 000331:
1176 * A test for GetFullPathName with many pathological cases
1177 * now gives identical output for Wine and OSR2
1179 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1180 BOOL unicode )
1182 DWORD ret;
1183 DOS_FULL_NAME full_name;
1184 char *p,*q;
1185 const char * root;
1186 char drivecur[]="c:.";
1187 char driveletter=0;
1188 int namelen,drive=0;
1190 if ((strlen(name) >1)&& (name[1]==':'))
1191 /*drive letter given */
1193 driveletter = name[0];
1195 if ((strlen(name) >2)&& (name[1]==':') &&
1196 ((name[2]=='\\') || (name[2]=='/')))
1197 /*absolute path given */
1199 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1200 drive = (int)FILE_toupper(name[0]) - 'A';
1202 else
1204 if (driveletter)
1205 drivecur[0]=driveletter;
1206 else
1207 strcpy(drivecur,".");
1208 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1210 FIXME("internal: error getting drive/path\n");
1211 return 0;
1213 /* find path that drive letter substitutes*/
1214 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1215 root= DRIVE_GetRoot(drive);
1216 if (!root)
1218 FIXME("internal: error getting DOS Drive Root\n");
1219 return 0;
1221 if (!strcmp(root,"/"))
1223 /* we have just the last / and we need it. */
1224 p= full_name.long_name;
1226 else
1228 p= full_name.long_name +strlen(root);
1230 /* append long name (= unix name) to drive */
1231 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1232 /* append name to treat */
1233 namelen= strlen(full_name.short_name);
1234 p = (char*)name;
1235 if (driveletter)
1236 p += +2; /* skip drive name when appending */
1237 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1239 FIXME("internal error: buffer too small\n");
1240 return 0;
1242 full_name.short_name[namelen++] ='\\';
1243 full_name.short_name[namelen] = 0;
1244 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1246 /* reverse all slashes */
1247 for (p=full_name.short_name;
1248 p < full_name.short_name+strlen(full_name.short_name);
1249 p++)
1251 if ( *p == '/' )
1252 *p = '\\';
1254 /* Use memmove, as areas overlap*/
1255 /* Delete .. */
1256 while ((p = strstr(full_name.short_name,"\\..\\")))
1258 if (p > full_name.short_name+2)
1260 *p = 0;
1261 q = strrchr(full_name.short_name,'\\');
1262 memmove(q+1,p+4,strlen(p+4)+1);
1264 else
1266 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1269 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1271 /* This case istn't treated yet : c:..\test */
1272 memmove(full_name.short_name+2,full_name.short_name+4,
1273 strlen(full_name.short_name+4)+1);
1275 /* Delete . */
1276 while ((p = strstr(full_name.short_name,"\\.\\")))
1278 *(p+1) = 0;
1279 memmove(p+1,p+3,strlen(p+3)+1);
1281 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1282 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1283 namelen=strlen(full_name.short_name);
1284 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1286 /* one more starnge case: "c:\test\test1\.."
1287 return "c:\test"*/
1288 *(full_name.short_name+namelen-3)=0;
1289 q = strrchr(full_name.short_name,'\\');
1290 *q =0;
1292 if (full_name.short_name[namelen-1]=='.')
1293 full_name.short_name[(namelen--)-1] =0;
1294 if (!driveletter)
1295 if (full_name.short_name[namelen-1]=='\\')
1296 full_name.short_name[(namelen--)-1] =0;
1297 TRACE("got %s\n",full_name.short_name);
1299 /* If the lpBuffer buffer is too small, the return value is the
1300 size of the buffer, in characters, required to hold the path
1301 plus the terminating \0 (tested against win95osr, bon 001118)
1302 . */
1303 ret = strlen(full_name.short_name);
1304 if (ret >= len )
1306 /* don't touch anything when the buffer is not large enough */
1307 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1308 return ret+1;
1310 if (result)
1312 if (unicode)
1313 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1314 else
1315 lstrcpynA( result, full_name.short_name, len );
1318 TRACE("returning '%s'\n", full_name.short_name );
1319 return ret;
1323 /***********************************************************************
1324 * GetFullPathNameA (KERNEL32.272)
1325 * NOTES
1326 * if the path closed with '\', *lastpart is 0
1328 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1329 LPSTR *lastpart )
1331 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1332 if (ret && (ret<=len) && buffer && lastpart)
1334 LPSTR p = buffer + strlen(buffer);
1336 if (*p != '\\')
1338 while ((p > buffer + 2) && (*p != '\\')) p--;
1339 *lastpart = p + 1;
1341 else *lastpart = NULL;
1343 return ret;
1347 /***********************************************************************
1348 * GetFullPathNameW (KERNEL32.273)
1350 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1351 LPWSTR *lastpart )
1353 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1354 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1355 HeapFree( GetProcessHeap(), 0, nameA );
1356 if (ret && (ret<=len) && buffer && lastpart)
1358 LPWSTR p = buffer + strlenW(buffer);
1359 if (*p != (WCHAR)'\\')
1361 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1362 *lastpart = p + 1;
1364 else *lastpart = NULL;
1366 return ret;
1370 /***********************************************************************
1371 * wine_get_unix_file_name (Not a Windows API, but exported from KERNEL32)
1373 * Return the full Unix file name for a given path.
1375 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1377 BOOL ret;
1378 DOS_FULL_NAME path;
1379 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1380 return ret;
1384 /***********************************************************************
1385 * DOSFS_FindNextEx
1387 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1389 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1390 UINT flags = DRIVE_GetFlags( info->drive );
1391 char *p, buffer[MAX_PATHNAME_LEN];
1392 const char *drive_path;
1393 int drive_root;
1394 LPCSTR long_name, short_name;
1395 BY_HANDLE_FILE_INFORMATION fileinfo;
1396 char dos_name[13];
1398 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1400 if (info->cur_pos) return 0;
1401 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1402 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1403 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1404 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1405 entry->nFileSizeHigh = 0;
1406 entry->nFileSizeLow = 0;
1407 entry->dwReserved0 = 0;
1408 entry->dwReserved1 = 0;
1409 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1410 strcpy( entry->cAlternateFileName, entry->cFileName );
1411 info->cur_pos++;
1412 TRACE("returning %s (%s) as label\n",
1413 entry->cFileName, entry->cAlternateFileName);
1414 return 1;
1417 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1418 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1419 drive_root = !*drive_path;
1421 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1422 strcat( buffer, "/" );
1423 p = buffer + strlen(buffer);
1425 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1427 info->cur_pos++;
1429 /* Don't return '.' and '..' in the root of the drive */
1430 if (drive_root && (long_name[0] == '.') &&
1431 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1432 continue;
1434 /* Check the long mask */
1436 if (info->long_mask)
1438 if (!DOSFS_MatchLong( info->long_mask, long_name,
1439 flags & DRIVE_CASE_SENSITIVE )) continue;
1442 /* Check the short mask */
1444 if (info->short_mask)
1446 if (!short_name)
1448 DOSFS_Hash( long_name, dos_name, TRUE,
1449 !(flags & DRIVE_CASE_SENSITIVE) );
1450 short_name = dos_name;
1452 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1455 /* Check the file attributes */
1457 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1458 if (!FILE_Stat( buffer, &fileinfo ))
1460 WARN("can't stat %s\n", buffer);
1461 continue;
1463 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1464 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1466 static int show_dir_symlinks = -1;
1467 if (show_dir_symlinks == -1)
1468 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1469 if (!show_dir_symlinks) continue;
1472 if (fileinfo.dwFileAttributes & ~attr) continue;
1474 /* We now have a matching entry; fill the result and return */
1476 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1477 entry->ftCreationTime = fileinfo.ftCreationTime;
1478 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1479 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1480 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1481 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1483 if (short_name)
1484 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1485 else
1486 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1487 !(flags & DRIVE_CASE_SENSITIVE) );
1489 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1490 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1491 TRACE("returning %s (%s) %02lx %ld\n",
1492 entry->cFileName, entry->cAlternateFileName,
1493 entry->dwFileAttributes, entry->nFileSizeLow );
1494 return 1;
1496 return 0; /* End of directory */
1499 /***********************************************************************
1500 * DOSFS_FindNext
1502 * Find the next matching file. Return the number of entries read to find
1503 * the matching one, or 0 if no more entries.
1504 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1505 * file name mask. Either or both can be NULL.
1507 * NOTE: This is supposed to be only called by the int21 emulation
1508 * routines. Thus, we should own the Win16Mutex anyway.
1509 * Nevertheless, we explicitly enter it to ensure the static
1510 * directory cache is protected.
1512 int DOSFS_FindNext( const char *path, const char *short_mask,
1513 const char *long_mask, int drive, BYTE attr,
1514 int skip, WIN32_FIND_DATAA *entry )
1516 static FIND_FIRST_INFO info;
1517 LPCSTR short_name, long_name;
1518 int count;
1520 _EnterWin16Lock();
1522 /* Check the cached directory */
1523 if (!(info.dir && info.path == path && info.short_mask == short_mask
1524 && info.long_mask == long_mask && info.drive == drive
1525 && info.attr == attr && info.cur_pos <= skip))
1527 /* Not in the cache, open it anew */
1528 if (info.dir) DOSFS_CloseDir( info.dir );
1530 info.path = (LPSTR)path;
1531 info.long_mask = (LPSTR)long_mask;
1532 info.short_mask = (LPSTR)short_mask;
1533 info.attr = attr;
1534 info.drive = drive;
1535 info.cur_pos = 0;
1536 info.dir = DOSFS_OpenDir( info.path );
1539 /* Skip to desired position */
1540 while (info.cur_pos < skip)
1541 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1542 info.cur_pos++;
1543 else
1544 break;
1546 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1547 count = info.cur_pos - skip;
1548 else
1549 count = 0;
1551 if (!count)
1553 if (info.dir) DOSFS_CloseDir( info.dir );
1554 memset( &info, '\0', sizeof(info) );
1557 _LeaveWin16Lock();
1559 return count;
1562 /*************************************************************************
1563 * FindFirstFileExA (KERNEL32)
1565 HANDLE WINAPI FindFirstFileExA(
1566 LPCSTR lpFileName,
1567 FINDEX_INFO_LEVELS fInfoLevelId,
1568 LPVOID lpFindFileData,
1569 FINDEX_SEARCH_OPS fSearchOp,
1570 LPVOID lpSearchFilter,
1571 DWORD dwAdditionalFlags)
1573 DOS_FULL_NAME full_name;
1574 HGLOBAL handle;
1575 FIND_FIRST_INFO *info;
1577 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1579 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1580 return INVALID_HANDLE_VALUE;
1583 switch(fInfoLevelId)
1585 case FindExInfoStandard:
1587 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1588 data->dwReserved0 = data->dwReserved1 = 0x0;
1589 if (!lpFileName) return 0;
1590 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1591 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1592 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1593 info->path = HEAP_strdupA( GetProcessHeap(), 0, full_name.long_name );
1594 info->long_mask = strrchr( info->path, '/' );
1595 *(info->long_mask++) = '\0';
1596 info->short_mask = NULL;
1597 info->attr = 0xff;
1598 if (lpFileName[0] && (lpFileName[1] == ':'))
1599 info->drive = FILE_toupper(*lpFileName) - 'A';
1600 else info->drive = DRIVE_GetCurrentDrive();
1601 info->cur_pos = 0;
1603 info->dir = DOSFS_OpenDir( info->path );
1605 GlobalUnlock( handle );
1606 if (!FindNextFileA( handle, data ))
1608 FindClose( handle );
1609 SetLastError( ERROR_NO_MORE_FILES );
1610 break;
1612 return handle;
1614 break;
1615 default:
1616 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1618 return INVALID_HANDLE_VALUE;
1621 /*************************************************************************
1622 * FindFirstFileA (KERNEL32.123)
1624 HANDLE WINAPI FindFirstFileA(
1625 LPCSTR lpFileName,
1626 WIN32_FIND_DATAA *lpFindData )
1628 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1629 FindExSearchNameMatch, NULL, 0);
1632 /*************************************************************************
1633 * FindFirstFileExW (KERNEL32)
1635 HANDLE WINAPI FindFirstFileExW(
1636 LPCWSTR lpFileName,
1637 FINDEX_INFO_LEVELS fInfoLevelId,
1638 LPVOID lpFindFileData,
1639 FINDEX_SEARCH_OPS fSearchOp,
1640 LPVOID lpSearchFilter,
1641 DWORD dwAdditionalFlags)
1643 HANDLE handle;
1644 WIN32_FIND_DATAA dataA;
1645 LPVOID _lpFindFileData;
1646 LPSTR pathA;
1648 switch(fInfoLevelId)
1650 case FindExInfoStandard:
1652 _lpFindFileData = &dataA;
1654 break;
1655 default:
1656 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1657 return INVALID_HANDLE_VALUE;
1660 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1661 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1662 HeapFree( GetProcessHeap(), 0, pathA );
1663 if (handle == INVALID_HANDLE_VALUE) return handle;
1665 switch(fInfoLevelId)
1667 case FindExInfoStandard:
1669 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1670 dataW->dwFileAttributes = dataA.dwFileAttributes;
1671 dataW->ftCreationTime = dataA.ftCreationTime;
1672 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1673 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1674 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1675 dataW->nFileSizeLow = dataA.nFileSizeLow;
1676 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1677 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1678 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1679 dataW->cAlternateFileName,
1680 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1682 break;
1683 default:
1684 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1685 return INVALID_HANDLE_VALUE;
1687 return handle;
1690 /*************************************************************************
1691 * FindFirstFileW (KERNEL32.124)
1693 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1695 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1696 FindExSearchNameMatch, NULL, 0);
1699 /*************************************************************************
1700 * FindNextFileA (KERNEL32.126)
1702 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1704 FIND_FIRST_INFO *info;
1706 if ((handle == INVALID_HANDLE_VALUE) ||
1707 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1709 SetLastError( ERROR_INVALID_HANDLE );
1710 return FALSE;
1712 GlobalUnlock( handle );
1713 if (!info->path || !info->dir)
1715 SetLastError( ERROR_NO_MORE_FILES );
1716 return FALSE;
1718 if (!DOSFS_FindNextEx( info, data ))
1720 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1721 HeapFree( GetProcessHeap(), 0, info->path );
1722 info->path = info->long_mask = NULL;
1723 SetLastError( ERROR_NO_MORE_FILES );
1724 return FALSE;
1726 return TRUE;
1730 /*************************************************************************
1731 * FindNextFileW (KERNEL32.127)
1733 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1735 WIN32_FIND_DATAA dataA;
1736 if (!FindNextFileA( handle, &dataA )) return FALSE;
1737 data->dwFileAttributes = dataA.dwFileAttributes;
1738 data->ftCreationTime = dataA.ftCreationTime;
1739 data->ftLastAccessTime = dataA.ftLastAccessTime;
1740 data->ftLastWriteTime = dataA.ftLastWriteTime;
1741 data->nFileSizeHigh = dataA.nFileSizeHigh;
1742 data->nFileSizeLow = dataA.nFileSizeLow;
1743 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1744 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1745 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1746 data->cAlternateFileName,
1747 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1748 return TRUE;
1751 /*************************************************************************
1752 * FindClose (KERNEL32.119)
1754 BOOL WINAPI FindClose( HANDLE handle )
1756 FIND_FIRST_INFO *info;
1758 if ((handle == INVALID_HANDLE_VALUE) ||
1759 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1761 SetLastError( ERROR_INVALID_HANDLE );
1762 return FALSE;
1764 if (info->dir) DOSFS_CloseDir( info->dir );
1765 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1766 GlobalUnlock( handle );
1767 GlobalFree( handle );
1768 return TRUE;
1771 /***********************************************************************
1772 * DOSFS_UnixTimeToFileTime
1774 * Convert a Unix time to FILETIME format.
1775 * The FILETIME structure is a 64-bit value representing the number of
1776 * 100-nanosecond intervals since January 1, 1601, 0:00.
1777 * 'remainder' is the nonnegative number of 100-ns intervals
1778 * corresponding to the time fraction smaller than 1 second that
1779 * couldn't be stored in the time_t value.
1781 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1782 DWORD remainder )
1784 /* NOTES:
1786 CONSTANTS:
1787 The time difference between 1 January 1601, 00:00:00 and
1788 1 January 1970, 00:00:00 is 369 years, plus the leap years
1789 from 1604 to 1968, excluding 1700, 1800, 1900.
1790 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1791 of 134774 days.
1793 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1795 The time difference is 134774 * 86400 * 10000000, which can be written
1796 116444736000000000
1797 27111902 * 2^32 + 3577643008
1798 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1800 If you find that these constants are buggy, please change them in all
1801 instances in both conversion functions.
1803 VERSIONS:
1804 There are two versions, one of them uses long long variables and
1805 is presumably faster but not ISO C. The other one uses standard C
1806 data types and operations but relies on the assumption that negative
1807 numbers are stored as 2's complement (-1 is 0xffff....). If this
1808 assumption is violated, dates before 1970 will not convert correctly.
1809 This should however work on any reasonable architecture where WINE
1810 will run.
1812 DETAILS:
1814 Take care not to remove the casts. I have tested these functions
1815 (in both versions) for a lot of numbers. I would be interested in
1816 results on other compilers than GCC.
1818 The operations have been designed to account for the possibility
1819 of 64-bit time_t in future UNICES. Even the versions without
1820 internal long long numbers will work if time_t only is 64 bit.
1821 A 32-bit shift, which was necessary for that operation, turned out
1822 not to work correctly in GCC, besides giving the warning. So I
1823 used a double 16-bit shift instead. Numbers are in the ISO version
1824 represented by three limbs, the most significant with 32 bit, the
1825 other two with 16 bit each.
1827 As the modulo-operator % is not well-defined for negative numbers,
1828 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1830 There might be quicker ways to do this in C. Certainly so in
1831 assembler.
1833 Claus Fischer, fischer@iue.tuwien.ac.at
1836 #if SIZEOF_LONG_LONG >= 8
1837 # define USE_LONG_LONG 1
1838 #else
1839 # define USE_LONG_LONG 0
1840 #endif
1842 #if USE_LONG_LONG /* gcc supports long long type */
1844 long long int t = unix_time;
1845 t *= 10000000;
1846 t += 116444736000000000LL;
1847 t += remainder;
1848 filetime->dwLowDateTime = (UINT)t;
1849 filetime->dwHighDateTime = (UINT)(t >> 32);
1851 #else /* ISO version */
1853 UINT a0; /* 16 bit, low bits */
1854 UINT a1; /* 16 bit, medium bits */
1855 UINT a2; /* 32 bit, high bits */
1857 /* Copy the unix time to a2/a1/a0 */
1858 a0 = unix_time & 0xffff;
1859 a1 = (unix_time >> 16) & 0xffff;
1860 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1861 Do not replace this by >> 32, it gives a compiler warning and it does
1862 not work. */
1863 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1864 ~((~unix_time >> 16) >> 16));
1866 /* Multiply a by 10000000 (a = a2/a1/a0)
1867 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1868 a0 *= 10000;
1869 a1 = a1 * 10000 + (a0 >> 16);
1870 a2 = a2 * 10000 + (a1 >> 16);
1871 a0 &= 0xffff;
1872 a1 &= 0xffff;
1874 a0 *= 1000;
1875 a1 = a1 * 1000 + (a0 >> 16);
1876 a2 = a2 * 1000 + (a1 >> 16);
1877 a0 &= 0xffff;
1878 a1 &= 0xffff;
1880 /* Add the time difference and the remainder */
1881 a0 += 32768 + (remainder & 0xffff);
1882 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1883 a2 += 27111902 + (a1 >> 16);
1884 a0 &= 0xffff;
1885 a1 &= 0xffff;
1887 /* Set filetime */
1888 filetime->dwLowDateTime = (a1 << 16) + a0;
1889 filetime->dwHighDateTime = a2;
1890 #endif
1894 /***********************************************************************
1895 * DOSFS_FileTimeToUnixTime
1897 * Convert a FILETIME format to Unix time.
1898 * If not NULL, 'remainder' contains the fractional part of the filetime,
1899 * in the range of [0..9999999] (even if time_t is negative).
1901 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1903 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1904 #if USE_LONG_LONG
1906 long long int t = filetime->dwHighDateTime;
1907 t <<= 32;
1908 t += (UINT)filetime->dwLowDateTime;
1909 t -= 116444736000000000LL;
1910 if (t < 0)
1912 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1913 return -1 - ((-t - 1) / 10000000);
1915 else
1917 if (remainder) *remainder = t % 10000000;
1918 return t / 10000000;
1921 #else /* ISO version */
1923 UINT a0; /* 16 bit, low bits */
1924 UINT a1; /* 16 bit, medium bits */
1925 UINT a2; /* 32 bit, high bits */
1926 UINT r; /* remainder of division */
1927 unsigned int carry; /* carry bit for subtraction */
1928 int negative; /* whether a represents a negative value */
1930 /* Copy the time values to a2/a1/a0 */
1931 a2 = (UINT)filetime->dwHighDateTime;
1932 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1933 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1935 /* Subtract the time difference */
1936 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1937 else a0 += (1 << 16) - 32768 , carry = 1;
1939 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1940 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1942 a2 -= 27111902 + carry;
1944 /* If a is negative, replace a by (-1-a) */
1945 negative = (a2 >= ((UINT)1) << 31);
1946 if (negative)
1948 /* Set a to -a - 1 (a is a2/a1/a0) */
1949 a0 = 0xffff - a0;
1950 a1 = 0xffff - a1;
1951 a2 = ~a2;
1954 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1955 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1956 a1 += (a2 % 10000) << 16;
1957 a2 /= 10000;
1958 a0 += (a1 % 10000) << 16;
1959 a1 /= 10000;
1960 r = a0 % 10000;
1961 a0 /= 10000;
1963 a1 += (a2 % 1000) << 16;
1964 a2 /= 1000;
1965 a0 += (a1 % 1000) << 16;
1966 a1 /= 1000;
1967 r += (a0 % 1000) * 10000;
1968 a0 /= 1000;
1970 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1971 if (negative)
1973 /* Set a to -a - 1 (a is a2/a1/a0) */
1974 a0 = 0xffff - a0;
1975 a1 = 0xffff - a1;
1976 a2 = ~a2;
1978 r = 9999999 - r;
1981 if (remainder) *remainder = r;
1983 /* Do not replace this by << 32, it gives a compiler warning and it does
1984 not work. */
1985 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1986 #endif
1990 /***********************************************************************
1991 * MulDiv (KERNEL32.391)
1992 * RETURNS
1993 * Result of multiplication and division
1994 * -1: Overflow occurred or Divisor was 0
1996 INT WINAPI MulDiv(
1997 INT nMultiplicand,
1998 INT nMultiplier,
1999 INT nDivisor)
2001 #if SIZEOF_LONG_LONG >= 8
2002 long long ret;
2004 if (!nDivisor) return -1;
2006 /* We want to deal with a positive divisor to simplify the logic. */
2007 if (nDivisor < 0)
2009 nMultiplicand = - nMultiplicand;
2010 nDivisor = -nDivisor;
2013 /* If the result is positive, we "add" to round. else, we subtract to round. */
2014 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2015 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2016 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2017 else
2018 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2020 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2021 return ret;
2022 #else
2023 if (!nDivisor) return -1;
2025 /* We want to deal with a positive divisor to simplify the logic. */
2026 if (nDivisor < 0)
2028 nMultiplicand = - nMultiplicand;
2029 nDivisor = -nDivisor;
2032 /* If the result is positive, we "add" to round. else, we subtract to round. */
2033 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2034 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2035 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2037 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2039 #endif
2043 /***********************************************************************
2044 * DosDateTimeToFileTime (KERNEL32.76)
2046 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2048 struct tm newtm;
2050 newtm.tm_sec = (fattime & 0x1f) * 2;
2051 newtm.tm_min = (fattime >> 5) & 0x3f;
2052 newtm.tm_hour = (fattime >> 11);
2053 newtm.tm_mday = (fatdate & 0x1f);
2054 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2055 newtm.tm_year = (fatdate >> 9) + 80;
2056 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2057 return TRUE;
2061 /***********************************************************************
2062 * FileTimeToDosDateTime (KERNEL32.111)
2064 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2065 LPWORD fattime )
2067 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2068 struct tm *tm = localtime( &unixtime );
2069 if (fattime)
2070 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2071 if (fatdate)
2072 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2073 + tm->tm_mday;
2074 return TRUE;
2078 /***********************************************************************
2079 * LocalFileTimeToFileTime (KERNEL32.373)
2081 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2082 LPFILETIME utcft )
2084 struct tm *xtm;
2085 DWORD remainder;
2087 /* convert from local to UTC. Perhaps not correct. FIXME */
2088 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2089 xtm = gmtime( &unixtime );
2090 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2091 return TRUE;
2095 /***********************************************************************
2096 * FileTimeToLocalFileTime (KERNEL32.112)
2098 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2099 LPFILETIME localft )
2101 DWORD remainder;
2102 /* convert from UTC to local. Perhaps not correct. FIXME */
2103 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2104 #ifdef HAVE_TIMEGM
2105 struct tm *xtm = localtime( &unixtime );
2106 time_t localtime;
2108 localtime = timegm(xtm);
2109 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2111 #else
2112 struct tm *xtm,*gtm;
2113 time_t time1,time2;
2115 xtm = localtime( &unixtime );
2116 gtm = gmtime( &unixtime );
2117 time1 = mktime(xtm);
2118 time2 = mktime(gtm);
2119 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2120 #endif
2121 return TRUE;
2125 /***********************************************************************
2126 * FileTimeToSystemTime (KERNEL32.113)
2128 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2130 struct tm *xtm;
2131 DWORD remainder;
2132 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2133 xtm = gmtime(&xtime);
2134 syst->wYear = xtm->tm_year+1900;
2135 syst->wMonth = xtm->tm_mon + 1;
2136 syst->wDayOfWeek = xtm->tm_wday;
2137 syst->wDay = xtm->tm_mday;
2138 syst->wHour = xtm->tm_hour;
2139 syst->wMinute = xtm->tm_min;
2140 syst->wSecond = xtm->tm_sec;
2141 syst->wMilliseconds = remainder / 10000;
2142 return TRUE;
2145 /***********************************************************************
2146 * QueryDosDeviceA (KERNEL32.413)
2148 * returns array of strings terminated by \0, terminated by \0
2150 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2152 LPSTR s;
2153 char buffer[200];
2155 TRACE("(%s,...)\n", devname ? devname : "<null>");
2156 if (!devname) {
2157 /* return known MSDOS devices */
2158 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2159 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2160 return min(bufsize,sizeof(devices));
2162 strcpy(buffer,"\\DEV\\");
2163 strcat(buffer,devname);
2164 if ((s=strchr(buffer,':'))) *s='\0';
2165 lstrcpynA(target,buffer,bufsize);
2166 return strlen(buffer)+1;
2170 /***********************************************************************
2171 * QueryDosDeviceW (KERNEL32.414)
2173 * returns array of strings terminated by \0, terminated by \0
2175 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2177 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2178 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2179 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2181 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2182 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2183 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2184 return ret;
2188 /***********************************************************************
2189 * SystemTimeToFileTime (KERNEL32.526)
2191 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2193 #ifdef HAVE_TIMEGM
2194 struct tm xtm;
2195 time_t utctime;
2196 #else
2197 struct tm xtm,*local_tm,*utc_tm;
2198 time_t localtim,utctime;
2199 #endif
2201 xtm.tm_year = syst->wYear-1900;
2202 xtm.tm_mon = syst->wMonth - 1;
2203 xtm.tm_wday = syst->wDayOfWeek;
2204 xtm.tm_mday = syst->wDay;
2205 xtm.tm_hour = syst->wHour;
2206 xtm.tm_min = syst->wMinute;
2207 xtm.tm_sec = syst->wSecond; /* this is UTC */
2208 xtm.tm_isdst = -1;
2209 #ifdef HAVE_TIMEGM
2210 utctime = timegm(&xtm);
2211 DOSFS_UnixTimeToFileTime( utctime, ft,
2212 syst->wMilliseconds * 10000 );
2213 #else
2214 localtim = mktime(&xtm); /* now we've got local time */
2215 local_tm = localtime(&localtim);
2216 utc_tm = gmtime(&localtim);
2217 utctime = mktime(utc_tm);
2218 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2219 syst->wMilliseconds * 10000 );
2220 #endif
2221 return TRUE;
2224 /***********************************************************************
2225 * DefineDosDeviceA (KERNEL32.182)
2227 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2228 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2229 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2230 return FALSE;
2234 --- 16 bit functions ---
2237 /*************************************************************************
2238 * FindFirstFile16 (KERNEL.413)
2240 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2242 DOS_FULL_NAME full_name;
2243 HGLOBAL16 handle;
2244 FIND_FIRST_INFO *info;
2246 data->dwReserved0 = data->dwReserved1 = 0x0;
2247 if (!path) return 0;
2248 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2249 return INVALID_HANDLE_VALUE16;
2250 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2251 return INVALID_HANDLE_VALUE16;
2252 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2253 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
2254 info->long_mask = strrchr( info->path, '/' );
2255 if (info->long_mask )
2256 *(info->long_mask++) = '\0';
2257 info->short_mask = NULL;
2258 info->attr = 0xff;
2259 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2260 else info->drive = DRIVE_GetCurrentDrive();
2261 info->cur_pos = 0;
2263 info->dir = DOSFS_OpenDir( info->path );
2265 GlobalUnlock16( handle );
2266 if (!FindNextFile16( handle, data ))
2268 FindClose16( handle );
2269 SetLastError( ERROR_NO_MORE_FILES );
2270 return INVALID_HANDLE_VALUE16;
2272 return handle;
2275 /*************************************************************************
2276 * FindNextFile16 (KERNEL.414)
2278 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2280 FIND_FIRST_INFO *info;
2282 if ((handle == INVALID_HANDLE_VALUE16) ||
2283 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2285 SetLastError( ERROR_INVALID_HANDLE );
2286 return FALSE;
2288 GlobalUnlock16( handle );
2289 if (!info->path || !info->dir)
2291 SetLastError( ERROR_NO_MORE_FILES );
2292 return FALSE;
2294 if (!DOSFS_FindNextEx( info, data ))
2296 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2297 HeapFree( SystemHeap, 0, info->path );
2298 info->path = info->long_mask = NULL;
2299 SetLastError( ERROR_NO_MORE_FILES );
2300 return FALSE;
2302 return TRUE;
2305 /*************************************************************************
2306 * FindClose16 (KERNEL.415)
2308 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2310 FIND_FIRST_INFO *info;
2312 if ((handle == INVALID_HANDLE_VALUE16) ||
2313 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2315 SetLastError( ERROR_INVALID_HANDLE );
2316 return FALSE;
2318 if (info->dir) DOSFS_CloseDir( info->dir );
2319 if (info->path) HeapFree( SystemHeap, 0, info->path );
2320 GlobalUnlock16( handle );
2321 GlobalFree16( handle );
2322 return TRUE;