- Fixed GetSystemTimeAdjustment prototype and added it to winbase.h.
[wine.git] / files / dos_fs.c
blob7947b35cf6586b613ec3886231bd769f20164e09
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
24 #include <sys/types.h>
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
30 #endif
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <time.h>
39 #include <unistd.h>
41 #include "windef.h"
42 #include "winerror.h"
43 #include "wingdi.h"
45 #include "wine/unicode.h"
46 #include "wine/winbase16.h"
47 #include "drive.h"
48 #include "file.h"
49 #include "heap.h"
50 #include "msdos.h"
51 #include "ntddk.h"
52 #include "wine/server.h"
53 #include "msvcrt/excpt.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
58 WINE_DECLARE_DEBUG_CHANNEL(file);
60 /* Define the VFAT ioctl to get both short and long file names */
61 /* FIXME: is it possible to get this to work on other systems? */
62 #ifdef linux
63 /* We want the real kernel dirent structure, not the libc one */
64 typedef struct
66 long d_ino;
67 long d_off;
68 unsigned short d_reclen;
69 char d_name[256];
70 } KERNEL_DIRENT;
72 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
74 #else /* linux */
75 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
76 #endif /* linux */
78 /* Chars we don't want to see in DOS file names */
79 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
81 static const DOS_DEVICE DOSFS_Devices[] =
82 /* name, device flags (see Int 21/AX=0x4400) */
84 { "CON", 0xc0d3 },
85 { "PRN", 0xa0c0 },
86 { "NUL", 0x80c4 },
87 { "AUX", 0x80c0 },
88 { "LPT1", 0xa0c0 },
89 { "LPT2", 0xa0c0 },
90 { "LPT3", 0xa0c0 },
91 { "LPT4", 0xc0d3 },
92 { "COM1", 0x80c0 },
93 { "COM2", 0x80c0 },
94 { "COM3", 0x80c0 },
95 { "COM4", 0x80c0 },
96 { "SCSIMGR$", 0xc0c0 },
97 { "HPSCAN", 0xc0c0 },
98 { "EMMXXXX0", 0x0000 }
101 #define GET_DRIVE(path) \
102 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
104 /* Directory info for DOSFS_ReadDir */
105 typedef struct
107 DIR *dir;
108 #ifdef VFAT_IOCTL_READDIR_BOTH
109 int fd;
110 char short_name[12];
111 KERNEL_DIRENT dirent[2];
112 #endif
113 } DOS_DIR;
115 /* Info structure for FindFirstFile handle */
116 typedef struct
118 LPSTR path;
119 LPSTR long_mask;
120 LPSTR short_mask;
121 BYTE attr;
122 int drive;
123 int cur_pos;
124 DOS_DIR *dir;
125 } FIND_FIRST_INFO;
128 static WINE_EXCEPTION_FILTER(page_fault)
130 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
131 return EXCEPTION_EXECUTE_HANDLER;
132 return EXCEPTION_CONTINUE_SEARCH;
136 /***********************************************************************
137 * DOSFS_ValidDOSName
139 * Return 1 if Unix file 'name' is also a valid MS-DOS name
140 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
141 * File name can be terminated by '\0', '\\' or '/'.
143 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
145 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
146 const char *p = name;
147 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
148 int len = 0;
150 if (*p == '.')
152 /* Check for "." and ".." */
153 p++;
154 if (*p == '.') p++;
155 /* All other names beginning with '.' are invalid */
156 return (IS_END_OF_NAME(*p));
158 while (!IS_END_OF_NAME(*p))
160 if (strchr( invalid, *p )) return 0; /* Invalid char */
161 if (*p == '.') break; /* Start of the extension */
162 if (++len > 8) return 0; /* Name too long */
163 p++;
165 if (*p != '.') return 1; /* End of name */
166 p++;
167 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
168 len = 0;
169 while (!IS_END_OF_NAME(*p))
171 if (strchr( invalid, *p )) return 0; /* Invalid char */
172 if (*p == '.') return 0; /* Second extension not allowed */
173 if (++len > 3) return 0; /* Extension too long */
174 p++;
176 return 1;
180 /***********************************************************************
181 * DOSFS_ToDosFCBFormat
183 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
184 * expanding wild cards and converting to upper-case in the process.
185 * File name can be terminated by '\0', '\\' or '/'.
186 * Return FALSE if the name is not a valid DOS name.
187 * 'buffer' must be at least 12 characters long.
189 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
191 static const char invalid_chars[] = INVALID_DOS_CHARS;
192 const char *p = name;
193 int i;
195 /* Check for "." and ".." */
196 if (*p == '.')
198 p++;
199 strcpy( buffer, ". " );
200 if (*p == '.')
202 buffer[1] = '.';
203 p++;
205 return (!*p || (*p == '/') || (*p == '\\'));
208 for (i = 0; i < 8; i++)
210 switch(*p)
212 case '\0':
213 case '\\':
214 case '/':
215 case '.':
216 buffer[i] = ' ';
217 break;
218 case '?':
219 p++;
220 /* fall through */
221 case '*':
222 buffer[i] = '?';
223 break;
224 default:
225 if (strchr( invalid_chars, *p )) return FALSE;
226 buffer[i] = FILE_toupper(*p);
227 p++;
228 break;
232 if (*p == '*')
234 /* Skip all chars after wildcard up to first dot */
235 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
237 else
239 /* Check if name too long */
240 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
242 if (*p == '.') p++; /* Skip dot */
244 for (i = 8; i < 11; i++)
246 switch(*p)
248 case '\0':
249 case '\\':
250 case '/':
251 buffer[i] = ' ';
252 break;
253 case '.':
254 return FALSE; /* Second extension not allowed */
255 case '?':
256 p++;
257 /* fall through */
258 case '*':
259 buffer[i] = '?';
260 break;
261 default:
262 if (strchr( invalid_chars, *p )) return FALSE;
263 buffer[i] = FILE_toupper(*p);
264 p++;
265 break;
268 buffer[11] = '\0';
270 /* at most 3 character of the extension are processed
271 * is something behind this ?
273 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
274 return IS_END_OF_NAME(*p);
278 /***********************************************************************
279 * DOSFS_ToDosDTAFormat
281 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
282 * converting to upper-case in the process.
283 * File name can be terminated by '\0', '\\' or '/'.
284 * 'buffer' must be at least 13 characters long.
286 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
288 char *p;
290 memcpy( buffer, name, 8 );
291 p = buffer + 8;
292 while ((p > buffer) && (p[-1] == ' ')) p--;
293 *p++ = '.';
294 memcpy( p, name + 8, 3 );
295 p += 3;
296 while (p[-1] == ' ') p--;
297 if (p[-1] == '.') p--;
298 *p = '\0';
302 /***********************************************************************
303 * DOSFS_MatchShort
305 * Check a DOS file name against a mask (both in FCB format).
307 static int DOSFS_MatchShort( const char *mask, const char *name )
309 int i;
310 for (i = 11; i > 0; i--, mask++, name++)
311 if ((*mask != '?') && (*mask != *name)) return 0;
312 return 1;
316 /***********************************************************************
317 * DOSFS_MatchLong
319 * Check a long file name against a mask.
321 * Tests (done in W95 DOS shell - case insensitive):
322 * *.txt test1.test.txt *
323 * *st1* test1.txt *
324 * *.t??????.t* test1.ta.tornado.txt *
325 * *tornado* test1.ta.tornado.txt *
326 * t*t test1.ta.tornado.txt *
327 * ?est* test1.txt *
328 * ?est??? test1.txt -
329 * *test1.txt* test1.txt *
330 * h?l?o*t.dat hellothisisatest.dat *
332 static int DOSFS_MatchLong( const char *mask, const char *name,
333 int case_sensitive )
335 const char *lastjoker = NULL;
336 const char *next_to_retry = NULL;
338 if (!strcmp( mask, "*.*" )) return 1;
339 while (*name && *mask)
341 if (*mask == '*')
343 mask++;
344 while (*mask == '*') mask++; /* Skip consecutive '*' */
345 lastjoker = mask;
346 if (!*mask) return 1; /* end of mask is all '*', so match */
348 /* skip to the next match after the joker(s) */
349 if (case_sensitive) while (*name && (*name != *mask)) name++;
350 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
352 if (!*name) break;
353 next_to_retry = name;
355 else if (*mask != '?')
357 int mismatch = 0;
358 if (case_sensitive)
360 if (*mask != *name) mismatch = 1;
362 else
364 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
366 if (!mismatch)
368 mask++;
369 name++;
370 if (*mask == '\0')
372 if (*name == '\0')
373 return 1;
374 if (lastjoker)
375 mask = lastjoker;
378 else /* mismatch ! */
380 if (lastjoker) /* we had an '*', so we can try unlimitedly */
382 mask = lastjoker;
384 /* this scan sequence was a mismatch, so restart
385 * 1 char after the first char we checked last time */
386 next_to_retry++;
387 name = next_to_retry;
389 else
390 return 0; /* bad luck */
393 else /* '?' */
395 mask++;
396 name++;
399 while ((*mask == '.') || (*mask == '*'))
400 mask++; /* Ignore trailing '.' or '*' in mask */
401 return (!*name && !*mask);
405 /***********************************************************************
406 * DOSFS_OpenDir
408 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
410 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
411 if (!dir)
413 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
414 return NULL;
417 /* Treat empty path as root directory. This simplifies path split into
418 directory and mask in several other places */
419 if (!*path) path = "/";
421 #ifdef VFAT_IOCTL_READDIR_BOTH
423 /* Check if the VFAT ioctl is supported on this directory */
425 if ((dir->fd = open( path, O_RDONLY )) != -1)
427 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
429 close( dir->fd );
430 dir->fd = -1;
432 else
434 /* Set the file pointer back at the start of the directory */
435 lseek( dir->fd, 0, SEEK_SET );
436 dir->dir = NULL;
437 return dir;
440 #endif /* VFAT_IOCTL_READDIR_BOTH */
442 /* Now use the standard opendir/readdir interface */
444 if (!(dir->dir = opendir( path )))
446 HeapFree( GetProcessHeap(), 0, dir );
447 return NULL;
449 return dir;
453 /***********************************************************************
454 * DOSFS_CloseDir
456 static void DOSFS_CloseDir( DOS_DIR *dir )
458 #ifdef VFAT_IOCTL_READDIR_BOTH
459 if (dir->fd != -1) close( dir->fd );
460 #endif /* VFAT_IOCTL_READDIR_BOTH */
461 if (dir->dir) closedir( dir->dir );
462 HeapFree( GetProcessHeap(), 0, dir );
466 /***********************************************************************
467 * DOSFS_ReadDir
469 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
470 LPCSTR *short_name )
472 struct dirent *dirent;
474 #ifdef VFAT_IOCTL_READDIR_BOTH
475 if (dir->fd != -1)
477 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
478 if (!dir->dirent[0].d_reclen) return FALSE;
479 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
480 dir->short_name[0] = '\0';
481 *short_name = dir->short_name;
482 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
483 else *long_name = dir->dirent[0].d_name;
484 return TRUE;
487 #endif /* VFAT_IOCTL_READDIR_BOTH */
489 if (!(dirent = readdir( dir->dir ))) return FALSE;
490 *long_name = dirent->d_name;
491 *short_name = NULL;
492 return TRUE;
496 /***********************************************************************
497 * DOSFS_Hash
499 * Transform a Unix file name into a hashed DOS name. If the name is a valid
500 * DOS name, it is converted to upper-case; otherwise it is replaced by a
501 * hashed version that fits in 8.3 format.
502 * File name can be terminated by '\0', '\\' or '/'.
503 * 'buffer' must be at least 13 characters long.
505 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
506 BOOL ignore_case )
508 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
509 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
511 const char *p, *ext;
512 char *dst;
513 unsigned short hash;
514 int i;
516 if (dir_format) strcpy( buffer, " " );
518 if (DOSFS_ValidDOSName( name, ignore_case ))
520 /* Check for '.' and '..' */
521 if (*name == '.')
523 buffer[0] = '.';
524 if (!dir_format) buffer[1] = buffer[2] = '\0';
525 if (name[1] == '.') buffer[1] = '.';
526 return;
529 /* Simply copy the name, converting to uppercase */
531 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
532 *dst++ = FILE_toupper(*name);
533 if (*name == '.')
535 if (dir_format) dst = buffer + 8;
536 else *dst++ = '.';
537 for (name++; !IS_END_OF_NAME(*name); name++)
538 *dst++ = FILE_toupper(*name);
540 if (!dir_format) *dst = '\0';
541 return;
544 /* Compute the hash code of the file name */
545 /* If you know something about hash functions, feel free to */
546 /* insert a better algorithm here... */
547 if (ignore_case)
549 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
550 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
551 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
553 else
555 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
556 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
557 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
560 /* Find last dot for start of the extension */
561 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
562 if (*p == '.') ext = p;
563 if (ext && IS_END_OF_NAME(ext[1]))
564 ext = NULL; /* Empty extension ignored */
566 /* Copy first 4 chars, replacing invalid chars with '_' */
567 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
569 if (IS_END_OF_NAME(*p) || (p == ext)) break;
570 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
572 /* Pad to 5 chars with '~' */
573 while (i-- >= 0) *dst++ = '~';
575 /* Insert hash code converted to 3 ASCII chars */
576 *dst++ = hash_chars[(hash >> 10) & 0x1f];
577 *dst++ = hash_chars[(hash >> 5) & 0x1f];
578 *dst++ = hash_chars[hash & 0x1f];
580 /* Copy the first 3 chars of the extension (if any) */
581 if (ext)
583 if (!dir_format) *dst++ = '.';
584 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
585 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
587 if (!dir_format) *dst = '\0';
591 /***********************************************************************
592 * DOSFS_FindUnixName
594 * Find the Unix file name in a given directory that corresponds to
595 * a file name (either in Unix or DOS format).
596 * File name can be terminated by '\0', '\\' or '/'.
597 * Return TRUE if OK, FALSE if no file name matches.
599 * 'long_buf' must be at least 'long_len' characters long. If the long name
600 * turns out to be larger than that, the function returns FALSE.
601 * 'short_buf' must be at least 13 characters long.
603 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
604 INT long_len, LPSTR short_buf, BOOL ignore_case)
606 DOS_DIR *dir;
607 LPCSTR long_name, short_name;
608 char dos_name[12], tmp_buf[13];
609 BOOL ret;
611 const char *p = strchr( name, '/' );
612 int len = p ? (int)(p - name) : strlen(name);
613 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
614 /* Ignore trailing dots and spaces */
615 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
616 if (long_len < len + 1) return FALSE;
618 TRACE("%s,%s\n", path, name );
620 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
622 if (!(dir = DOSFS_OpenDir( path )))
624 WARN("(%s,%s): can't open dir: %s\n",
625 path, name, strerror(errno) );
626 return FALSE;
629 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
631 /* Check against Unix name */
632 if (len == strlen(long_name))
634 if (!ignore_case)
636 if (!strncmp( long_name, name, len )) break;
638 else
640 if (!FILE_strncasecmp( long_name, name, len )) break;
643 if (dos_name[0])
645 /* Check against hashed DOS name */
646 if (!short_name)
648 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
649 short_name = tmp_buf;
651 if (!strcmp( dos_name, short_name )) break;
654 if (ret)
656 if (long_buf) strcpy( long_buf, long_name );
657 if (short_buf)
659 if (short_name)
660 DOSFS_ToDosDTAFormat( short_name, short_buf );
661 else
662 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
664 TRACE("(%s,%s) -> %s (%s)\n",
665 path, name, long_name, short_buf ? short_buf : "***");
667 else
668 WARN("'%s' not found in '%s'\n", name, path);
669 DOSFS_CloseDir( dir );
670 return ret;
674 /***********************************************************************
675 * DOSFS_GetDevice
677 * Check if a DOS file name represents a DOS device and return the device.
679 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
681 int i;
682 const char *p;
684 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
685 if (name[0] && (name[1] == ':')) name += 2;
686 if ((p = strrchr( name, '/' ))) name = p + 1;
687 if ((p = strrchr( name, '\\' ))) name = p + 1;
688 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
690 const char *dev = DOSFS_Devices[i].name;
691 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
693 p = name + strlen( dev );
694 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
697 return NULL;
701 /***********************************************************************
702 * DOSFS_GetDeviceByHandle
704 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
706 const DOS_DEVICE *ret = NULL;
707 SERVER_START_REQ( get_file_info )
709 req->handle = hFile;
710 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
712 if ((reply->attr >= 0) &&
713 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
714 ret = &DOSFS_Devices[reply->attr];
717 SERVER_END_REQ;
718 return ret;
722 /**************************************************************************
723 * DOSFS_CreateCommPort
725 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
727 HANDLE ret;
728 char devname[40];
730 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
732 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
733 if(!devname[0])
734 return 0;
736 TRACE("opening %s as %s\n", devname, name);
738 SERVER_START_REQ( create_serial )
740 req->access = access;
741 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
742 req->attributes = attributes;
743 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
744 wine_server_add_data( req, devname, strlen(devname) );
745 SetLastError(0);
746 wine_server_call_err( req );
747 ret = reply->handle;
749 SERVER_END_REQ;
751 if(!ret)
752 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
753 else
754 TRACE("return %08X\n", ret );
755 return ret;
758 /***********************************************************************
759 * DOSFS_OpenDevice
761 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
762 * Returns 0 on failure.
764 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
766 int i;
767 const char *p;
768 HANDLE handle;
770 if (name[0] && (name[1] == ':')) name += 2;
771 if ((p = strrchr( name, '/' ))) name = p + 1;
772 if ((p = strrchr( name, '\\' ))) name = p + 1;
773 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
775 const char *dev = DOSFS_Devices[i].name;
776 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
778 p = name + strlen( dev );
779 if (!*p || (*p == '.') || (*p == ':')) {
780 /* got it */
781 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
782 return FILE_CreateFile( "/dev/null", access,
783 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
784 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
785 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
786 HANDLE to_dup;
787 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
788 case GENERIC_READ:
789 to_dup = GetStdHandle( STD_INPUT_HANDLE );
790 break;
791 case GENERIC_WRITE:
792 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
793 break;
794 default:
795 FIXME("can't open CON read/write\n");
796 return 0;
798 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
799 &handle, 0,
800 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
801 DUPLICATE_SAME_ACCESS ))
802 handle = 0;
803 return handle;
805 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
806 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
807 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
809 return FILE_CreateDevice( i, access, sa );
812 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
813 return handle;
814 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
815 return 0;
819 return 0;
823 /***********************************************************************
824 * DOSFS_GetPathDrive
826 * Get the drive specified by a given path name (DOS or Unix format).
828 static int DOSFS_GetPathDrive( const char **name )
830 int drive;
831 const char *p = *name;
833 if (*p && (p[1] == ':'))
835 drive = FILE_toupper(*p) - 'A';
836 *name += 2;
838 else if (*p == '/') /* Absolute Unix path? */
840 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
842 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
843 /* Assume it really was a DOS name */
844 drive = DRIVE_GetCurrentDrive();
847 else drive = DRIVE_GetCurrentDrive();
849 if (!DRIVE_IsValid(drive))
851 SetLastError( ERROR_INVALID_DRIVE );
852 return -1;
854 return drive;
858 /***********************************************************************
859 * DOSFS_GetFullName
861 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
862 * Unix name / short DOS name pair.
863 * Return FALSE if one of the path components does not exist. The last path
864 * component is only checked if 'check_last' is non-zero.
865 * The buffers pointed to by 'long_buf' and 'short_buf' must be
866 * at least MAX_PATHNAME_LEN long.
868 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
870 BOOL unixabsolute = *name == '/';
871 BOOL found;
872 UINT flags;
873 char *p_l, *p_s, *root;
875 TRACE("%s (last=%d)\n", name, check_last );
877 if ((!*name) || (*name=='\n'))
878 { /* error code for Win98 */
879 SetLastError(ERROR_BAD_PATHNAME);
880 return FALSE;
883 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
884 flags = DRIVE_GetFlags( full->drive );
886 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
887 sizeof(full->long_name) );
888 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
889 else root = full->long_name; /* root directory */
891 strcpy( full->short_name, "A:\\" );
892 full->short_name[0] += full->drive;
894 if ((*name == '\\') || (*name == '/')) /* Absolute path */
896 while ((*name == '\\') || (*name == '/')) name++;
898 else if (!unixabsolute) /* Relative path */
900 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
901 sizeof(full->long_name) - (root - full->long_name) - 1 );
902 if (root[1]) *root = '/';
903 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
904 sizeof(full->short_name) - 3 );
907 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
908 : full->long_name;
909 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
910 : full->short_name + 2;
911 found = TRUE;
913 while (*name && found)
915 /* Check for '.' and '..' */
917 if (*name == '.')
919 if (IS_END_OF_NAME(name[1]))
921 name++;
922 while ((*name == '\\') || (*name == '/')) name++;
923 continue;
925 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
927 name += 2;
928 while ((*name == '\\') || (*name == '/')) name++;
929 while ((p_l > root) && (*p_l != '/')) p_l--;
930 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
931 *p_l = *p_s = '\0'; /* Remove trailing separator */
932 continue;
936 /* Make sure buffers are large enough */
938 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
939 (p_l >= full->long_name + sizeof(full->long_name) - 1))
941 SetLastError( ERROR_PATH_NOT_FOUND );
942 return FALSE;
945 /* Get the long and short name matching the file name */
947 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
948 sizeof(full->long_name) - (p_l - full->long_name) - 1,
949 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
951 *p_l++ = '/';
952 p_l += strlen(p_l);
953 *p_s++ = '\\';
954 p_s += strlen(p_s);
955 while (!IS_END_OF_NAME(*name)) name++;
957 else if (!check_last)
959 *p_l++ = '/';
960 *p_s++ = '\\';
961 while (!IS_END_OF_NAME(*name) &&
962 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
963 (p_l < full->long_name + sizeof(full->long_name) - 1))
965 *p_s++ = FILE_tolower(*name);
966 /* If the drive is case-sensitive we want to create new */
967 /* files in lower-case otherwise we can't reopen them */
968 /* under the same short name. */
969 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
970 else *p_l++ = *name;
971 name++;
973 /* Ignore trailing dots and spaces */
974 while(p_l[-1] == '.' || p_l[-1] == ' ') {
975 --p_l;
976 --p_s;
978 *p_l = *p_s = '\0';
980 while ((*name == '\\') || (*name == '/')) name++;
983 if (!found)
985 if (check_last)
987 SetLastError( ERROR_FILE_NOT_FOUND );
988 return FALSE;
990 if (*name) /* Not last */
992 SetLastError( ERROR_PATH_NOT_FOUND );
993 return FALSE;
996 if (!full->long_name[0]) strcpy( full->long_name, "/" );
997 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
998 TRACE("returning %s = %s\n", full->long_name, full->short_name );
999 return TRUE;
1003 /***********************************************************************
1004 * GetShortPathNameA (KERNEL32.@)
1006 * NOTES
1007 * observed:
1008 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1009 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1011 * more observations ( with NT 3.51 (WinDD) ):
1012 * longpath <= 8.3 -> just copy longpath to shortpath
1013 * longpath > 8.3 ->
1014 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1015 * b) file does exist -> set the short filename.
1016 * - trailing slashes are reproduced in the short name, even if the
1017 * file is not a directory
1018 * - the absolute/relative path of the short name is reproduced like found
1019 * in the long name
1020 * - longpath and shortpath may have the same address
1021 * Peter Ganten, 1999
1023 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1024 DWORD shortlen )
1026 DOS_FULL_NAME full_name;
1027 LPSTR tmpshortpath;
1028 DWORD sp = 0, lp = 0;
1029 int tmplen, drive;
1030 UINT flags;
1031 BOOL unixabsolute = *longpath == '/';
1033 TRACE("%s\n", debugstr_a(longpath));
1035 if (!longpath) {
1036 SetLastError(ERROR_INVALID_PARAMETER);
1037 return 0;
1039 if (!longpath[0]) {
1040 SetLastError(ERROR_BAD_PATHNAME);
1041 return 0;
1044 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1045 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1046 return 0;
1049 /* check for drive letter */
1050 if ( longpath[1] == ':' ) {
1051 tmpshortpath[0] = longpath[0];
1052 tmpshortpath[1] = ':';
1053 sp = 2;
1056 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1057 flags = DRIVE_GetFlags ( drive );
1059 if ( unixabsolute ) {
1060 tmpshortpath[0] = drive + 'A';
1061 tmpshortpath[1] = ':';
1062 tmpshortpath[2] = '\\';
1063 sp = 3;
1066 while ( longpath[lp] ) {
1068 /* check for path delimiters and reproduce them */
1069 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1070 if (!sp || tmpshortpath[sp-1]!= '\\')
1072 /* strip double "\\" */
1073 tmpshortpath[sp] = '\\';
1074 sp++;
1076 tmpshortpath[sp]=0;/*terminate string*/
1077 lp++;
1078 continue;
1081 tmplen = strcspn ( longpath + lp, "\\/" );
1082 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1084 /* Check, if the current element is a valid dos name */
1085 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1086 sp += tmplen;
1087 lp += tmplen;
1088 continue;
1091 /* Check if the file exists and use the existing file name */
1092 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1093 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1094 sp += strlen ( tmpshortpath+sp );
1095 lp += tmplen;
1096 continue;
1099 TRACE("not found!\n" );
1100 SetLastError ( ERROR_FILE_NOT_FOUND );
1101 return 0;
1103 tmpshortpath[sp] = 0;
1105 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1106 TRACE("returning %s\n", debugstr_a(shortpath) );
1107 tmplen = strlen ( tmpshortpath );
1108 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1110 return tmplen;
1114 /***********************************************************************
1115 * GetShortPathNameW (KERNEL32.@)
1117 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1118 DWORD shortlen )
1120 LPSTR longpathA, shortpathA;
1121 DWORD ret = 0;
1123 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1124 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1126 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1127 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1128 shortpath[shortlen-1] = 0;
1129 HeapFree( GetProcessHeap(), 0, longpathA );
1130 HeapFree( GetProcessHeap(), 0, shortpathA );
1132 return ret;
1136 /***********************************************************************
1137 * GetLongPathNameA (KERNEL32.@)
1139 * NOTES
1140 * observed (Win2000):
1141 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1142 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1144 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1145 DWORD longlen )
1147 DOS_FULL_NAME full_name;
1148 char *p, *r, *ll, *ss;
1150 if (!shortpath) {
1151 SetLastError(ERROR_INVALID_PARAMETER);
1152 return 0;
1154 if (!shortpath[0]) {
1155 SetLastError(ERROR_PATH_NOT_FOUND);
1156 return 0;
1159 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1160 lstrcpynA( longpath, full_name.short_name, longlen );
1162 /* Do some hackery to get the long filename. */
1164 if (longpath) {
1165 ss=longpath+strlen(longpath);
1166 ll=full_name.long_name+strlen(full_name.long_name);
1167 p=NULL;
1168 while (ss>=longpath)
1170 /* FIXME: aren't we more paranoid, than needed? */
1171 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1172 p=ss;
1173 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1174 if (ss>=longpath)
1176 /* FIXME: aren't we more paranoid, than needed? */
1177 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1178 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1179 if (ll<full_name.long_name)
1181 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1182 ,ss ,ll );
1183 return 0;
1188 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1189 if (p && p[2])
1191 p+=1;
1192 if ((p-longpath)>0) longlen -= (p-longpath);
1193 lstrcpynA( p, ll , longlen);
1195 /* Now, change all '/' to '\' */
1196 for (r=p; r<(p+longlen); r++ )
1197 if (r[0]=='/') r[0]='\\';
1198 return strlen(longpath) - strlen(p) + longlen;
1202 return strlen(longpath);
1206 /***********************************************************************
1207 * GetLongPathNameW (KERNEL32.@)
1209 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1210 DWORD longlen )
1212 DOS_FULL_NAME full_name;
1213 DWORD ret = 0;
1214 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1216 /* FIXME: is it correct to always return a fully qualified short path? */
1217 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1219 ret = strlen( full_name.short_name );
1220 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1221 longpath, longlen ))
1222 longpath[longlen-1] = 0;
1224 HeapFree( GetProcessHeap(), 0, shortpathA );
1225 return ret;
1229 /***********************************************************************
1230 * DOSFS_DoGetFullPathName
1232 * Implementation of GetFullPathNameA/W.
1234 * bon@elektron 000331:
1235 * A test for GetFullPathName with many pathological cases
1236 * now gives identical output for Wine and OSR2
1238 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1239 BOOL unicode )
1241 DWORD ret;
1242 DOS_FULL_NAME full_name;
1243 char *p,*q;
1244 const char * root;
1245 char drivecur[]="c:.";
1246 char driveletter=0;
1247 int namelen,drive=0;
1249 if (!name[0]) return 0;
1251 TRACE("passed '%s'\n", name);
1253 if (name[1]==':')
1254 /*drive letter given */
1256 driveletter = name[0];
1258 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1259 /*absolute path given */
1261 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1262 drive = (int)FILE_toupper(name[0]) - 'A';
1264 else
1266 if (driveletter)
1267 drivecur[0]=driveletter;
1268 else if ((name[0]=='\\') || (name[0]=='/'))
1269 strcpy(drivecur,"\\");
1270 else
1271 strcpy(drivecur,".");
1273 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1275 FIXME("internal: error getting drive/path\n");
1276 return 0;
1278 /* find path that drive letter substitutes*/
1279 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1280 root= DRIVE_GetRoot(drive);
1281 if (!root)
1283 FIXME("internal: error getting DOS Drive Root\n");
1284 return 0;
1286 if (!strcmp(root,"/"))
1288 /* we have just the last / and we need it. */
1289 p= full_name.long_name;
1291 else
1293 p= full_name.long_name +strlen(root);
1295 /* append long name (= unix name) to drive */
1296 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1297 /* append name to treat */
1298 namelen= strlen(full_name.short_name);
1299 p = (char*)name;
1300 if (driveletter)
1301 p += +2; /* skip drive name when appending */
1302 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1304 FIXME("internal error: buffer too small\n");
1305 return 0;
1307 full_name.short_name[namelen++] ='\\';
1308 full_name.short_name[namelen] = 0;
1309 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1311 /* reverse all slashes */
1312 for (p=full_name.short_name;
1313 p < full_name.short_name+strlen(full_name.short_name);
1314 p++)
1316 if ( *p == '/' )
1317 *p = '\\';
1319 /* Use memmove, as areas overlap */
1320 /* Delete .. */
1321 while ((p = strstr(full_name.short_name,"\\..\\")))
1323 if (p > full_name.short_name+2)
1325 *p = 0;
1326 q = strrchr(full_name.short_name,'\\');
1327 memmove(q+1,p+4,strlen(p+4)+1);
1329 else
1331 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1334 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1336 /* This case istn't treated yet : c:..\test */
1337 memmove(full_name.short_name+2,full_name.short_name+4,
1338 strlen(full_name.short_name+4)+1);
1340 /* Delete . */
1341 while ((p = strstr(full_name.short_name,"\\.\\")))
1343 *(p+1) = 0;
1344 memmove(p+1,p+3,strlen(p+3)+1);
1346 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1347 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1348 namelen=strlen(full_name.short_name);
1349 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1351 /* one more strange case: "c:\test\test1\.."
1352 return "c:\test" */
1353 *(full_name.short_name+namelen-3)=0;
1354 q = strrchr(full_name.short_name,'\\');
1355 *q =0;
1357 if (full_name.short_name[namelen-1]=='.')
1358 full_name.short_name[(namelen--)-1] =0;
1359 if (!driveletter)
1360 if (full_name.short_name[namelen-1]=='\\')
1361 full_name.short_name[(namelen--)-1] =0;
1362 TRACE("got %s\n",full_name.short_name);
1364 /* If the lpBuffer buffer is too small, the return value is the
1365 size of the buffer, in characters, required to hold the path
1366 plus the terminating \0 (tested against win95osr2, bon 001118)
1367 . */
1368 ret = strlen(full_name.short_name);
1369 if (ret >= len )
1371 /* don't touch anything when the buffer is not large enough */
1372 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1373 return ret+1;
1375 if (result)
1377 if (unicode)
1378 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1379 else
1380 lstrcpynA( result, full_name.short_name, len );
1383 TRACE("returning '%s'\n", full_name.short_name );
1384 return ret;
1388 /***********************************************************************
1389 * GetFullPathNameA (KERNEL32.@)
1390 * NOTES
1391 * if the path closed with '\', *lastpart is 0
1393 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1394 LPSTR *lastpart )
1396 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1397 if (ret && (ret<=len) && buffer && lastpart)
1399 LPSTR p = buffer + strlen(buffer);
1401 if (*p != '\\')
1403 while ((p > buffer + 2) && (*p != '\\')) p--;
1404 *lastpart = p + 1;
1406 else *lastpart = NULL;
1408 return ret;
1412 /***********************************************************************
1413 * GetFullPathNameW (KERNEL32.@)
1415 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1416 LPWSTR *lastpart )
1418 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1419 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1420 HeapFree( GetProcessHeap(), 0, nameA );
1421 if (ret && (ret<=len) && buffer && lastpart)
1423 LPWSTR p = buffer + strlenW(buffer);
1424 if (*p != (WCHAR)'\\')
1426 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1427 *lastpart = p + 1;
1429 else *lastpart = NULL;
1431 return ret;
1435 /***********************************************************************
1436 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1438 * Return the full Unix file name for a given path.
1440 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1442 BOOL ret;
1443 DOS_FULL_NAME path;
1444 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1445 return ret;
1449 /***********************************************************************
1450 * DOSFS_FindNextEx
1452 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1454 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1455 UINT flags = DRIVE_GetFlags( info->drive );
1456 char *p, buffer[MAX_PATHNAME_LEN];
1457 const char *drive_path;
1458 int drive_root;
1459 LPCSTR long_name, short_name;
1460 BY_HANDLE_FILE_INFORMATION fileinfo;
1461 char dos_name[13];
1463 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1465 if (info->cur_pos) return 0;
1466 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1467 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1468 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1469 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1470 entry->nFileSizeHigh = 0;
1471 entry->nFileSizeLow = 0;
1472 entry->dwReserved0 = 0;
1473 entry->dwReserved1 = 0;
1474 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1475 strcpy( entry->cAlternateFileName, entry->cFileName );
1476 info->cur_pos++;
1477 TRACE("returning %s (%s) as label\n",
1478 entry->cFileName, entry->cAlternateFileName);
1479 return 1;
1482 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1483 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1484 drive_root = !*drive_path;
1486 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1487 strcat( buffer, "/" );
1488 p = buffer + strlen(buffer);
1490 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1492 info->cur_pos++;
1494 /* Don't return '.' and '..' in the root of the drive */
1495 if (drive_root && (long_name[0] == '.') &&
1496 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1497 continue;
1499 /* Check the long mask */
1501 if (info->long_mask)
1503 if (!DOSFS_MatchLong( info->long_mask, long_name,
1504 flags & DRIVE_CASE_SENSITIVE )) continue;
1507 /* Check the short mask */
1509 if (info->short_mask)
1511 if (!short_name)
1513 DOSFS_Hash( long_name, dos_name, TRUE,
1514 !(flags & DRIVE_CASE_SENSITIVE) );
1515 short_name = dos_name;
1517 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1520 /* Check the file attributes */
1522 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1523 if (!FILE_Stat( buffer, &fileinfo ))
1525 WARN("can't stat %s\n", buffer);
1526 continue;
1528 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1529 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1531 static int show_dir_symlinks = -1;
1532 if (show_dir_symlinks == -1)
1533 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1534 if (!show_dir_symlinks) continue;
1537 if (fileinfo.dwFileAttributes & ~attr) continue;
1539 /* We now have a matching entry; fill the result and return */
1541 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1542 entry->ftCreationTime = fileinfo.ftCreationTime;
1543 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1544 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1545 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1546 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1548 if (short_name)
1549 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1550 else
1551 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1552 !(flags & DRIVE_CASE_SENSITIVE) );
1554 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1555 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1556 TRACE("returning %s (%s) %02lx %ld\n",
1557 entry->cFileName, entry->cAlternateFileName,
1558 entry->dwFileAttributes, entry->nFileSizeLow );
1559 return 1;
1561 return 0; /* End of directory */
1564 /***********************************************************************
1565 * DOSFS_FindNext
1567 * Find the next matching file. Return the number of entries read to find
1568 * the matching one, or 0 if no more entries.
1569 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1570 * file name mask. Either or both can be NULL.
1572 * NOTE: This is supposed to be only called by the int21 emulation
1573 * routines. Thus, we should own the Win16Mutex anyway.
1574 * Nevertheless, we explicitly enter it to ensure the static
1575 * directory cache is protected.
1577 int DOSFS_FindNext( const char *path, const char *short_mask,
1578 const char *long_mask, int drive, BYTE attr,
1579 int skip, WIN32_FIND_DATAA *entry )
1581 static FIND_FIRST_INFO info;
1582 LPCSTR short_name, long_name;
1583 int count;
1585 _EnterWin16Lock();
1587 /* Check the cached directory */
1588 if (!(info.dir && info.path == path && info.short_mask == short_mask
1589 && info.long_mask == long_mask && info.drive == drive
1590 && info.attr == attr && info.cur_pos <= skip))
1592 /* Not in the cache, open it anew */
1593 if (info.dir) DOSFS_CloseDir( info.dir );
1595 info.path = (LPSTR)path;
1596 info.long_mask = (LPSTR)long_mask;
1597 info.short_mask = (LPSTR)short_mask;
1598 info.attr = attr;
1599 info.drive = drive;
1600 info.cur_pos = 0;
1601 info.dir = DOSFS_OpenDir( info.path );
1604 /* Skip to desired position */
1605 while (info.cur_pos < skip)
1606 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1607 info.cur_pos++;
1608 else
1609 break;
1611 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1612 count = info.cur_pos - skip;
1613 else
1614 count = 0;
1616 if (!count)
1618 if (info.dir) DOSFS_CloseDir( info.dir );
1619 memset( &info, '\0', sizeof(info) );
1622 _LeaveWin16Lock();
1624 return count;
1627 /*************************************************************************
1628 * FindFirstFileExA (KERNEL32.@)
1630 HANDLE WINAPI FindFirstFileExA(
1631 LPCSTR lpFileName,
1632 FINDEX_INFO_LEVELS fInfoLevelId,
1633 LPVOID lpFindFileData,
1634 FINDEX_SEARCH_OPS fSearchOp,
1635 LPVOID lpSearchFilter,
1636 DWORD dwAdditionalFlags)
1638 DOS_FULL_NAME full_name;
1639 HGLOBAL handle;
1640 FIND_FIRST_INFO *info;
1642 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1644 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1645 return INVALID_HANDLE_VALUE;
1648 switch(fInfoLevelId)
1650 case FindExInfoStandard:
1652 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1653 data->dwReserved0 = data->dwReserved1 = 0x0;
1654 if (!lpFileName) return 0;
1655 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1656 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1657 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1658 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1659 strcpy( info->path, full_name.long_name );
1660 info->long_mask = strrchr( info->path, '/' );
1661 *(info->long_mask++) = '\0';
1662 info->short_mask = NULL;
1663 info->attr = 0xff;
1664 if (lpFileName[0] && (lpFileName[1] == ':'))
1665 info->drive = FILE_toupper(*lpFileName) - 'A';
1666 else info->drive = DRIVE_GetCurrentDrive();
1667 info->cur_pos = 0;
1669 info->dir = DOSFS_OpenDir( info->path );
1671 GlobalUnlock( handle );
1672 if (!FindNextFileA( handle, data ))
1674 FindClose( handle );
1675 SetLastError( ERROR_NO_MORE_FILES );
1676 break;
1678 return handle;
1680 break;
1681 default:
1682 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1684 return INVALID_HANDLE_VALUE;
1687 /*************************************************************************
1688 * FindFirstFileA (KERNEL32.@)
1690 HANDLE WINAPI FindFirstFileA(
1691 LPCSTR lpFileName,
1692 WIN32_FIND_DATAA *lpFindData )
1694 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1695 FindExSearchNameMatch, NULL, 0);
1698 /*************************************************************************
1699 * FindFirstFileExW (KERNEL32.@)
1701 HANDLE WINAPI FindFirstFileExW(
1702 LPCWSTR lpFileName,
1703 FINDEX_INFO_LEVELS fInfoLevelId,
1704 LPVOID lpFindFileData,
1705 FINDEX_SEARCH_OPS fSearchOp,
1706 LPVOID lpSearchFilter,
1707 DWORD dwAdditionalFlags)
1709 HANDLE handle;
1710 WIN32_FIND_DATAA dataA;
1711 LPVOID _lpFindFileData;
1712 LPSTR pathA;
1714 switch(fInfoLevelId)
1716 case FindExInfoStandard:
1718 _lpFindFileData = &dataA;
1720 break;
1721 default:
1722 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1723 return INVALID_HANDLE_VALUE;
1726 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1727 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1728 HeapFree( GetProcessHeap(), 0, pathA );
1729 if (handle == INVALID_HANDLE_VALUE) return handle;
1731 switch(fInfoLevelId)
1733 case FindExInfoStandard:
1735 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1736 dataW->dwFileAttributes = dataA.dwFileAttributes;
1737 dataW->ftCreationTime = dataA.ftCreationTime;
1738 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1739 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1740 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1741 dataW->nFileSizeLow = dataA.nFileSizeLow;
1742 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1743 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1744 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1745 dataW->cAlternateFileName,
1746 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1748 break;
1749 default:
1750 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1751 return INVALID_HANDLE_VALUE;
1753 return handle;
1756 /*************************************************************************
1757 * FindFirstFileW (KERNEL32.@)
1759 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1761 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1762 FindExSearchNameMatch, NULL, 0);
1765 /*************************************************************************
1766 * FindNextFileA (KERNEL32.@)
1768 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1770 FIND_FIRST_INFO *info;
1772 if ((handle == INVALID_HANDLE_VALUE) ||
1773 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1775 SetLastError( ERROR_INVALID_HANDLE );
1776 return FALSE;
1778 GlobalUnlock( handle );
1779 if (!info->path || !info->dir)
1781 SetLastError( ERROR_NO_MORE_FILES );
1782 return FALSE;
1784 if (!DOSFS_FindNextEx( info, data ))
1786 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1787 HeapFree( GetProcessHeap(), 0, info->path );
1788 info->path = info->long_mask = NULL;
1789 SetLastError( ERROR_NO_MORE_FILES );
1790 return FALSE;
1792 return TRUE;
1796 /*************************************************************************
1797 * FindNextFileW (KERNEL32.@)
1799 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1801 WIN32_FIND_DATAA dataA;
1802 if (!FindNextFileA( handle, &dataA )) return FALSE;
1803 data->dwFileAttributes = dataA.dwFileAttributes;
1804 data->ftCreationTime = dataA.ftCreationTime;
1805 data->ftLastAccessTime = dataA.ftLastAccessTime;
1806 data->ftLastWriteTime = dataA.ftLastWriteTime;
1807 data->nFileSizeHigh = dataA.nFileSizeHigh;
1808 data->nFileSizeLow = dataA.nFileSizeLow;
1809 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1810 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1811 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1812 data->cAlternateFileName,
1813 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1814 return TRUE;
1817 /*************************************************************************
1818 * FindClose (KERNEL32.@)
1820 BOOL WINAPI FindClose( HANDLE handle )
1822 FIND_FIRST_INFO *info;
1824 if (handle == INVALID_HANDLE_VALUE) goto error;
1826 __TRY
1828 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1830 if (info->dir) DOSFS_CloseDir( info->dir );
1831 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1834 __EXCEPT(page_fault)
1836 WARN("Illegal handle %x\n", handle);
1837 SetLastError( ERROR_INVALID_HANDLE );
1838 return FALSE;
1840 __ENDTRY
1841 if (!info) goto error;
1842 GlobalUnlock( handle );
1843 GlobalFree( handle );
1844 return TRUE;
1846 error:
1847 SetLastError( ERROR_INVALID_HANDLE );
1848 return FALSE;
1851 /***********************************************************************
1852 * DOSFS_UnixTimeToFileTime
1854 * Convert a Unix time to FILETIME format.
1855 * The FILETIME structure is a 64-bit value representing the number of
1856 * 100-nanosecond intervals since January 1, 1601, 0:00.
1857 * 'remainder' is the nonnegative number of 100-ns intervals
1858 * corresponding to the time fraction smaller than 1 second that
1859 * couldn't be stored in the time_t value.
1861 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1862 DWORD remainder )
1864 /* NOTES:
1866 CONSTANTS:
1867 The time difference between 1 January 1601, 00:00:00 and
1868 1 January 1970, 00:00:00 is 369 years, plus the leap years
1869 from 1604 to 1968, excluding 1700, 1800, 1900.
1870 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1871 of 134774 days.
1873 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1875 The time difference is 134774 * 86400 * 10000000, which can be written
1876 116444736000000000
1877 27111902 * 2^32 + 3577643008
1878 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1880 If you find that these constants are buggy, please change them in all
1881 instances in both conversion functions.
1883 VERSIONS:
1884 There are two versions, one of them uses long long variables and
1885 is presumably faster but not ISO C. The other one uses standard C
1886 data types and operations but relies on the assumption that negative
1887 numbers are stored as 2's complement (-1 is 0xffff....). If this
1888 assumption is violated, dates before 1970 will not convert correctly.
1889 This should however work on any reasonable architecture where WINE
1890 will run.
1892 DETAILS:
1894 Take care not to remove the casts. I have tested these functions
1895 (in both versions) for a lot of numbers. I would be interested in
1896 results on other compilers than GCC.
1898 The operations have been designed to account for the possibility
1899 of 64-bit time_t in future UNICES. Even the versions without
1900 internal long long numbers will work if time_t only is 64 bit.
1901 A 32-bit shift, which was necessary for that operation, turned out
1902 not to work correctly in GCC, besides giving the warning. So I
1903 used a double 16-bit shift instead. Numbers are in the ISO version
1904 represented by three limbs, the most significant with 32 bit, the
1905 other two with 16 bit each.
1907 As the modulo-operator % is not well-defined for negative numbers,
1908 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1910 There might be quicker ways to do this in C. Certainly so in
1911 assembler.
1913 Claus Fischer, fischer@iue.tuwien.ac.at
1916 #if SIZEOF_LONG_LONG >= 8
1917 # define USE_LONG_LONG 1
1918 #else
1919 # define USE_LONG_LONG 0
1920 #endif
1922 #if USE_LONG_LONG /* gcc supports long long type */
1924 long long int t = unix_time;
1925 t *= 10000000;
1926 t += 116444736000000000LL;
1927 t += remainder;
1928 filetime->dwLowDateTime = (UINT)t;
1929 filetime->dwHighDateTime = (UINT)(t >> 32);
1931 #else /* ISO version */
1933 UINT a0; /* 16 bit, low bits */
1934 UINT a1; /* 16 bit, medium bits */
1935 UINT a2; /* 32 bit, high bits */
1937 /* Copy the unix time to a2/a1/a0 */
1938 a0 = unix_time & 0xffff;
1939 a1 = (unix_time >> 16) & 0xffff;
1940 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1941 Do not replace this by >> 32, it gives a compiler warning and it does
1942 not work. */
1943 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1944 ~((~unix_time >> 16) >> 16));
1946 /* Multiply a by 10000000 (a = a2/a1/a0)
1947 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1948 a0 *= 10000;
1949 a1 = a1 * 10000 + (a0 >> 16);
1950 a2 = a2 * 10000 + (a1 >> 16);
1951 a0 &= 0xffff;
1952 a1 &= 0xffff;
1954 a0 *= 1000;
1955 a1 = a1 * 1000 + (a0 >> 16);
1956 a2 = a2 * 1000 + (a1 >> 16);
1957 a0 &= 0xffff;
1958 a1 &= 0xffff;
1960 /* Add the time difference and the remainder */
1961 a0 += 32768 + (remainder & 0xffff);
1962 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1963 a2 += 27111902 + (a1 >> 16);
1964 a0 &= 0xffff;
1965 a1 &= 0xffff;
1967 /* Set filetime */
1968 filetime->dwLowDateTime = (a1 << 16) + a0;
1969 filetime->dwHighDateTime = a2;
1970 #endif
1974 /***********************************************************************
1975 * DOSFS_FileTimeToUnixTime
1977 * Convert a FILETIME format to Unix time.
1978 * If not NULL, 'remainder' contains the fractional part of the filetime,
1979 * in the range of [0..9999999] (even if time_t is negative).
1981 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1983 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1984 #if USE_LONG_LONG
1986 long long int t = filetime->dwHighDateTime;
1987 t <<= 32;
1988 t += (UINT)filetime->dwLowDateTime;
1989 t -= 116444736000000000LL;
1990 if (t < 0)
1992 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1993 return -1 - ((-t - 1) / 10000000);
1995 else
1997 if (remainder) *remainder = t % 10000000;
1998 return t / 10000000;
2001 #else /* ISO version */
2003 UINT a0; /* 16 bit, low bits */
2004 UINT a1; /* 16 bit, medium bits */
2005 UINT a2; /* 32 bit, high bits */
2006 UINT r; /* remainder of division */
2007 unsigned int carry; /* carry bit for subtraction */
2008 int negative; /* whether a represents a negative value */
2010 /* Copy the time values to a2/a1/a0 */
2011 a2 = (UINT)filetime->dwHighDateTime;
2012 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2013 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2015 /* Subtract the time difference */
2016 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2017 else a0 += (1 << 16) - 32768 , carry = 1;
2019 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2020 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2022 a2 -= 27111902 + carry;
2024 /* If a is negative, replace a by (-1-a) */
2025 negative = (a2 >= ((UINT)1) << 31);
2026 if (negative)
2028 /* Set a to -a - 1 (a is a2/a1/a0) */
2029 a0 = 0xffff - a0;
2030 a1 = 0xffff - a1;
2031 a2 = ~a2;
2034 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2035 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2036 a1 += (a2 % 10000) << 16;
2037 a2 /= 10000;
2038 a0 += (a1 % 10000) << 16;
2039 a1 /= 10000;
2040 r = a0 % 10000;
2041 a0 /= 10000;
2043 a1 += (a2 % 1000) << 16;
2044 a2 /= 1000;
2045 a0 += (a1 % 1000) << 16;
2046 a1 /= 1000;
2047 r += (a0 % 1000) * 10000;
2048 a0 /= 1000;
2050 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2051 if (negative)
2053 /* Set a to -a - 1 (a is a2/a1/a0) */
2054 a0 = 0xffff - a0;
2055 a1 = 0xffff - a1;
2056 a2 = ~a2;
2058 r = 9999999 - r;
2061 if (remainder) *remainder = r;
2063 /* Do not replace this by << 32, it gives a compiler warning and it does
2064 not work. */
2065 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2066 #endif
2070 /***********************************************************************
2071 * MulDiv (KERNEL32.@)
2072 * RETURNS
2073 * Result of multiplication and division
2074 * -1: Overflow occurred or Divisor was 0
2076 INT WINAPI MulDiv(
2077 INT nMultiplicand,
2078 INT nMultiplier,
2079 INT nDivisor)
2081 #if SIZEOF_LONG_LONG >= 8
2082 long long ret;
2084 if (!nDivisor) return -1;
2086 /* We want to deal with a positive divisor to simplify the logic. */
2087 if (nDivisor < 0)
2089 nMultiplicand = - nMultiplicand;
2090 nDivisor = -nDivisor;
2093 /* If the result is positive, we "add" to round. else, we subtract to round. */
2094 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2095 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2096 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2097 else
2098 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2100 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2101 return ret;
2102 #else
2103 if (!nDivisor) return -1;
2105 /* We want to deal with a positive divisor to simplify the logic. */
2106 if (nDivisor < 0)
2108 nMultiplicand = - nMultiplicand;
2109 nDivisor = -nDivisor;
2112 /* If the result is positive, we "add" to round. else, we subtract to round. */
2113 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2114 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2115 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2117 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2119 #endif
2123 /***********************************************************************
2124 * DosDateTimeToFileTime (KERNEL32.@)
2126 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2128 struct tm newtm;
2129 #ifndef HAVE_TIMEGM
2130 struct tm *gtm;
2131 time_t time1, time2;
2132 #endif
2134 newtm.tm_sec = (fattime & 0x1f) * 2;
2135 newtm.tm_min = (fattime >> 5) & 0x3f;
2136 newtm.tm_hour = (fattime >> 11);
2137 newtm.tm_mday = (fatdate & 0x1f);
2138 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2139 newtm.tm_year = (fatdate >> 9) + 80;
2140 #ifdef HAVE_TIMEGM
2141 RtlSecondsSince1970ToTime( timegm(&newtm), ft );
2142 #else
2143 time1 = mktime(&newtm);
2144 gtm = gmtime(&time1);
2145 time2 = mktime(gtm);
2146 RtlSecondsSince1970ToTime( 2*time1-time2, ft );
2147 #endif
2148 return TRUE;
2152 /***********************************************************************
2153 * FileTimeToDosDateTime (KERNEL32.@)
2155 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2156 LPWORD fattime )
2158 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2159 struct tm *tm = gmtime( &unixtime );
2160 if (fattime)
2161 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2162 if (fatdate)
2163 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2164 + tm->tm_mday;
2165 return TRUE;
2169 /***********************************************************************
2170 * LocalFileTimeToFileTime (KERNEL32.@)
2172 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2173 LPFILETIME utcft )
2175 struct tm *xtm;
2176 DWORD remainder;
2177 time_t utctime;
2179 /* Converts from local to UTC. */
2180 time_t localtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2181 xtm = gmtime( &localtime );
2182 utctime = mktime(xtm);
2183 if(xtm->tm_isdst > 0) utctime-=3600;
2184 DOSFS_UnixTimeToFileTime( utctime, utcft, remainder );
2185 return TRUE;
2189 /***********************************************************************
2190 * FileTimeToLocalFileTime (KERNEL32.@)
2192 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2193 LPFILETIME localft )
2195 DWORD remainder;
2196 /* Converts from UTC to local. */
2197 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2198 #ifdef HAVE_TIMEGM
2199 struct tm *xtm = localtime( &unixtime );
2200 time_t localtime;
2202 localtime = timegm(xtm);
2203 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2205 #else
2206 struct tm *xtm;
2207 time_t time;
2209 xtm = gmtime( &unixtime );
2210 time = mktime(xtm);
2211 if(xtm->tm_isdst > 0) time-=3600;
2212 DOSFS_UnixTimeToFileTime( 2*unixtime-time, localft, remainder );
2213 #endif
2214 return TRUE;
2218 /***********************************************************************
2219 * FileTimeToSystemTime (KERNEL32.@)
2221 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2223 struct tm *xtm;
2224 DWORD remainder;
2225 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2226 xtm = gmtime(&xtime);
2227 syst->wYear = xtm->tm_year+1900;
2228 syst->wMonth = xtm->tm_mon + 1;
2229 syst->wDayOfWeek = xtm->tm_wday;
2230 syst->wDay = xtm->tm_mday;
2231 syst->wHour = xtm->tm_hour;
2232 syst->wMinute = xtm->tm_min;
2233 syst->wSecond = xtm->tm_sec;
2234 syst->wMilliseconds = remainder / 10000;
2235 return TRUE;
2238 /***********************************************************************
2239 * QueryDosDeviceA (KERNEL32.@)
2241 * returns array of strings terminated by \0, terminated by \0
2243 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2245 LPSTR s;
2246 char buffer[200];
2248 TRACE("(%s,...)\n", devname ? devname : "<null>");
2249 if (!devname) {
2250 /* return known MSDOS devices */
2251 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2252 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2253 return min(bufsize,sizeof(devices));
2255 /* In theory all that are possible and have been defined.
2256 * Now just those below, since mirc uses it to check for special files.
2258 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2259 * but currently we just ignore that.)
2261 #define CHECK(x) (strstr(devname,#x)==devname)
2262 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2263 strcpy(buffer,"\\DEV\\");
2264 strcat(buffer,devname);
2265 if ((s=strchr(buffer,':'))) *s='\0';
2266 lstrcpynA(target,buffer,bufsize);
2267 return strlen(buffer)+1;
2268 } else {
2269 if (strchr(devname,':') || devname[0]=='\\') {
2270 /* This might be a DOS device we do not handle yet ... */
2271 FIXME("(%s) not detected as DOS device!\n",devname);
2273 SetLastError(ERROR_DEV_NOT_EXIST);
2274 return 0;
2280 /***********************************************************************
2281 * QueryDosDeviceW (KERNEL32.@)
2283 * returns array of strings terminated by \0, terminated by \0
2285 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2287 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2288 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2289 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2291 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2292 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2293 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2294 return ret;
2298 /***********************************************************************
2299 * SystemTimeToFileTime (KERNEL32.@)
2301 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2303 #ifdef HAVE_TIMEGM
2304 struct tm xtm;
2305 time_t utctime;
2306 #else
2307 struct tm xtm,*utc_tm;
2308 time_t localtim,utctime;
2309 #endif
2311 xtm.tm_year = syst->wYear-1900;
2312 xtm.tm_mon = syst->wMonth - 1;
2313 xtm.tm_wday = syst->wDayOfWeek;
2314 xtm.tm_mday = syst->wDay;
2315 xtm.tm_hour = syst->wHour;
2316 xtm.tm_min = syst->wMinute;
2317 xtm.tm_sec = syst->wSecond; /* this is UTC */
2318 xtm.tm_isdst = -1;
2319 #ifdef HAVE_TIMEGM
2320 utctime = timegm(&xtm);
2321 DOSFS_UnixTimeToFileTime( utctime, ft,
2322 syst->wMilliseconds * 10000 );
2323 #else
2324 localtim = mktime(&xtm); /* now we've got local time */
2325 utc_tm = gmtime(&localtim);
2326 utctime = mktime(utc_tm);
2327 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2328 syst->wMilliseconds * 10000 );
2329 #endif
2330 return TRUE;
2333 /***********************************************************************
2334 * DefineDosDeviceA (KERNEL32.@)
2336 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2337 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2338 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2339 return FALSE;
2343 --- 16 bit functions ---
2346 /*************************************************************************
2347 * FindFirstFile (KERNEL.413)
2349 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2351 DOS_FULL_NAME full_name;
2352 HGLOBAL16 handle;
2353 FIND_FIRST_INFO *info;
2355 data->dwReserved0 = data->dwReserved1 = 0x0;
2356 if (!path) return 0;
2357 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2358 return INVALID_HANDLE_VALUE16;
2359 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2360 return INVALID_HANDLE_VALUE16;
2361 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2362 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2363 strcpy( info->path, full_name.long_name );
2364 info->long_mask = strrchr( info->path, '/' );
2365 if (info->long_mask )
2366 *(info->long_mask++) = '\0';
2367 info->short_mask = NULL;
2368 info->attr = 0xff;
2369 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2370 else info->drive = DRIVE_GetCurrentDrive();
2371 info->cur_pos = 0;
2373 info->dir = DOSFS_OpenDir( info->path );
2375 GlobalUnlock16( handle );
2376 if (!FindNextFile16( handle, data ))
2378 FindClose16( handle );
2379 SetLastError( ERROR_NO_MORE_FILES );
2380 return INVALID_HANDLE_VALUE16;
2382 return handle;
2385 /*************************************************************************
2386 * FindNextFile (KERNEL.414)
2388 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2390 FIND_FIRST_INFO *info;
2392 if ((handle == INVALID_HANDLE_VALUE16) ||
2393 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2395 SetLastError( ERROR_INVALID_HANDLE );
2396 return FALSE;
2398 GlobalUnlock16( handle );
2399 if (!info->path || !info->dir)
2401 SetLastError( ERROR_NO_MORE_FILES );
2402 return FALSE;
2404 if (!DOSFS_FindNextEx( info, data ))
2406 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2407 HeapFree( GetProcessHeap(), 0, info->path );
2408 info->path = info->long_mask = NULL;
2409 SetLastError( ERROR_NO_MORE_FILES );
2410 return FALSE;
2412 return TRUE;
2415 /*************************************************************************
2416 * FindClose (KERNEL.415)
2418 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2420 FIND_FIRST_INFO *info;
2422 if ((handle == INVALID_HANDLE_VALUE16) ||
2423 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2425 SetLastError( ERROR_INVALID_HANDLE );
2426 return FALSE;
2428 if (info->dir) DOSFS_CloseDir( info->dir );
2429 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2430 GlobalUnlock16( handle );
2431 GlobalFree16( handle );
2432 return TRUE;