Fixed regression caused by previous change.
[wine/multimedia.git] / files / dos_fs.c
blob85021ff4a5f8d1dbdb30fc16a751a08e619bf5dd
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 found;
871 UINT flags;
872 char *p_l, *p_s, *root;
874 TRACE("%s (last=%d)\n", name, check_last );
876 if ((!*name) || (*name=='\n'))
877 { /* error code for Win98 */
878 SetLastError(ERROR_BAD_PATHNAME);
879 return FALSE;
882 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
883 flags = DRIVE_GetFlags( full->drive );
885 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
886 sizeof(full->long_name) );
887 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
888 else root = full->long_name; /* root directory */
890 strcpy( full->short_name, "A:\\" );
891 full->short_name[0] += full->drive;
893 if ((*name == '\\') || (*name == '/')) /* Absolute path */
895 while ((*name == '\\') || (*name == '/')) name++;
897 else /* Relative path */
899 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
900 sizeof(full->long_name) - (root - full->long_name) - 1 );
901 if (root[1]) *root = '/';
902 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
903 sizeof(full->short_name) - 3 );
906 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
907 : full->long_name;
908 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
909 : full->short_name + 2;
910 found = TRUE;
912 while (*name && found)
914 /* Check for '.' and '..' */
916 if (*name == '.')
918 if (IS_END_OF_NAME(name[1]))
920 name++;
921 while ((*name == '\\') || (*name == '/')) name++;
922 continue;
924 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
926 name += 2;
927 while ((*name == '\\') || (*name == '/')) name++;
928 while ((p_l > root) && (*p_l != '/')) p_l--;
929 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
930 *p_l = *p_s = '\0'; /* Remove trailing separator */
931 continue;
935 /* Make sure buffers are large enough */
937 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
938 (p_l >= full->long_name + sizeof(full->long_name) - 1))
940 SetLastError( ERROR_PATH_NOT_FOUND );
941 return FALSE;
944 /* Get the long and short name matching the file name */
946 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
947 sizeof(full->long_name) - (p_l - full->long_name) - 1,
948 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
950 *p_l++ = '/';
951 p_l += strlen(p_l);
952 *p_s++ = '\\';
953 p_s += strlen(p_s);
954 while (!IS_END_OF_NAME(*name)) name++;
956 else if (!check_last)
958 *p_l++ = '/';
959 *p_s++ = '\\';
960 while (!IS_END_OF_NAME(*name) &&
961 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
962 (p_l < full->long_name + sizeof(full->long_name) - 1))
964 *p_s++ = FILE_tolower(*name);
965 /* If the drive is case-sensitive we want to create new */
966 /* files in lower-case otherwise we can't reopen them */
967 /* under the same short name. */
968 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
969 else *p_l++ = *name;
970 name++;
972 /* Ignore trailing dots and spaces */
973 while(p_l[-1] == '.' || p_l[-1] == ' ') {
974 --p_l;
975 --p_s;
977 *p_l = *p_s = '\0';
979 while ((*name == '\\') || (*name == '/')) name++;
982 if (!found)
984 if (check_last)
986 SetLastError( ERROR_FILE_NOT_FOUND );
987 return FALSE;
989 if (*name) /* Not last */
991 SetLastError( ERROR_PATH_NOT_FOUND );
992 return FALSE;
995 if (!full->long_name[0]) strcpy( full->long_name, "/" );
996 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
997 TRACE("returning %s = %s\n", full->long_name, full->short_name );
998 return TRUE;
1002 /***********************************************************************
1003 * GetShortPathNameA (KERNEL32.@)
1005 * NOTES
1006 * observed:
1007 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1008 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1010 * more observations ( with NT 3.51 (WinDD) ):
1011 * longpath <= 8.3 -> just copy longpath to shortpath
1012 * longpath > 8.3 ->
1013 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1014 * b) file does exist -> set the short filename.
1015 * - trailing slashes are reproduced in the short name, even if the
1016 * file is not a directory
1017 * - the absolute/relative path of the short name is reproduced like found
1018 * in the long name
1019 * - longpath and shortpath may have the same address
1020 * Peter Ganten, 1999
1022 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1023 DWORD shortlen )
1025 DOS_FULL_NAME full_name;
1026 LPSTR tmpshortpath;
1027 DWORD sp = 0, lp = 0;
1028 int tmplen, drive;
1029 UINT flags;
1030 BOOL unixabsolute = *longpath == '/';
1032 TRACE("%s\n", debugstr_a(longpath));
1034 if (!longpath) {
1035 SetLastError(ERROR_INVALID_PARAMETER);
1036 return 0;
1038 if (!longpath[0]) {
1039 SetLastError(ERROR_BAD_PATHNAME);
1040 return 0;
1043 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1044 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1045 return 0;
1048 /* check for drive letter */
1049 if (!unixabsolute && longpath[1] == ':' ) {
1050 tmpshortpath[0] = longpath[0];
1051 tmpshortpath[1] = ':';
1052 sp = 2;
1055 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1056 flags = DRIVE_GetFlags ( drive );
1058 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1059 tmpshortpath[0] = drive + 'A';
1060 tmpshortpath[1] = ':';
1061 sp = 2;
1064 while ( longpath[lp] ) {
1066 /* check for path delimiters and reproduce them */
1067 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1068 if (!sp || tmpshortpath[sp-1]!= '\\')
1070 /* strip double "\\" */
1071 tmpshortpath[sp] = '\\';
1072 sp++;
1074 tmpshortpath[sp]=0;/*terminate string*/
1075 lp++;
1076 continue;
1079 tmplen = strcspn ( longpath + lp, "\\/" );
1080 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1082 /* Check, if the current element is a valid dos name */
1083 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1084 sp += tmplen;
1085 lp += tmplen;
1086 continue;
1089 /* Check if the file exists and use the existing file name */
1090 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1091 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1092 sp += strlen ( tmpshortpath+sp );
1093 lp += tmplen;
1094 continue;
1097 TRACE("not found!\n" );
1098 SetLastError ( ERROR_FILE_NOT_FOUND );
1099 return 0;
1101 tmpshortpath[sp] = 0;
1103 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1104 TRACE("returning %s\n", debugstr_a(shortpath) );
1105 tmplen = strlen ( tmpshortpath );
1106 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1108 return tmplen;
1112 /***********************************************************************
1113 * GetShortPathNameW (KERNEL32.@)
1115 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1116 DWORD shortlen )
1118 LPSTR longpathA, shortpathA;
1119 DWORD ret = 0;
1121 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1122 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1124 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1125 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1126 shortpath[shortlen-1] = 0;
1127 HeapFree( GetProcessHeap(), 0, longpathA );
1128 HeapFree( GetProcessHeap(), 0, shortpathA );
1130 return ret;
1134 /***********************************************************************
1135 * GetLongPathNameA (KERNEL32.@)
1137 * NOTES
1138 * observed (Win2000):
1139 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1140 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1142 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1143 DWORD longlen )
1145 DOS_FULL_NAME full_name;
1146 char *p, *r, *ll, *ss;
1148 if (!shortpath) {
1149 SetLastError(ERROR_INVALID_PARAMETER);
1150 return 0;
1152 if (!shortpath[0]) {
1153 SetLastError(ERROR_PATH_NOT_FOUND);
1154 return 0;
1157 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1158 lstrcpynA( longpath, full_name.short_name, longlen );
1160 /* Do some hackery to get the long filename. */
1162 if (longpath) {
1163 ss=longpath+strlen(longpath);
1164 ll=full_name.long_name+strlen(full_name.long_name);
1165 p=NULL;
1166 while (ss>=longpath)
1168 /* FIXME: aren't we more paranoid, than needed? */
1169 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1170 p=ss;
1171 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1172 if (ss>=longpath)
1174 /* FIXME: aren't we more paranoid, than needed? */
1175 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1176 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1177 if (ll<full_name.long_name)
1179 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1180 ,ss ,ll );
1181 return 0;
1186 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1187 if (p && p[2])
1189 p+=1;
1190 if ((p-longpath)>0) longlen -= (p-longpath);
1191 lstrcpynA( p, ll , longlen);
1193 /* Now, change all '/' to '\' */
1194 for (r=p; r<(p+longlen); r++ )
1195 if (r[0]=='/') r[0]='\\';
1196 return strlen(longpath) - strlen(p) + longlen;
1200 return strlen(longpath);
1204 /***********************************************************************
1205 * GetLongPathNameW (KERNEL32.@)
1207 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1208 DWORD longlen )
1210 DOS_FULL_NAME full_name;
1211 DWORD ret = 0;
1212 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1214 /* FIXME: is it correct to always return a fully qualified short path? */
1215 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1217 ret = strlen( full_name.short_name );
1218 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1219 longpath, longlen ))
1220 longpath[longlen-1] = 0;
1222 HeapFree( GetProcessHeap(), 0, shortpathA );
1223 return ret;
1227 /***********************************************************************
1228 * DOSFS_DoGetFullPathName
1230 * Implementation of GetFullPathNameA/W.
1232 * bon@elektron 000331:
1233 * A test for GetFullPathName with many pathological cases
1234 * now gives identical output for Wine and OSR2
1236 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1237 BOOL unicode )
1239 DWORD ret;
1240 DOS_FULL_NAME full_name;
1241 char *p,*q;
1242 const char * root;
1243 char drivecur[]="c:.";
1244 char driveletter=0;
1245 int namelen,drive=0;
1247 if (!name[0]) return 0;
1249 TRACE("passed '%s'\n", name);
1251 if (name[1]==':')
1252 /*drive letter given */
1254 driveletter = name[0];
1256 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1257 /*absolute path given */
1259 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1260 drive = (int)FILE_toupper(name[0]) - 'A';
1262 else
1264 if (driveletter)
1265 drivecur[0]=driveletter;
1266 else if ((name[0]=='\\') || (name[0]=='/'))
1267 strcpy(drivecur,"\\");
1268 else
1269 strcpy(drivecur,".");
1271 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1273 FIXME("internal: error getting drive/path\n");
1274 return 0;
1276 /* find path that drive letter substitutes*/
1277 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1278 root= DRIVE_GetRoot(drive);
1279 if (!root)
1281 FIXME("internal: error getting DOS Drive Root\n");
1282 return 0;
1284 if (!strcmp(root,"/"))
1286 /* we have just the last / and we need it. */
1287 p= full_name.long_name;
1289 else
1291 p= full_name.long_name +strlen(root);
1293 /* append long name (= unix name) to drive */
1294 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1295 /* append name to treat */
1296 namelen= strlen(full_name.short_name);
1297 p = (char*)name;
1298 if (driveletter)
1299 p += +2; /* skip drive name when appending */
1300 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1302 FIXME("internal error: buffer too small\n");
1303 return 0;
1305 full_name.short_name[namelen++] ='\\';
1306 full_name.short_name[namelen] = 0;
1307 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1309 /* reverse all slashes */
1310 for (p=full_name.short_name;
1311 p < full_name.short_name+strlen(full_name.short_name);
1312 p++)
1314 if ( *p == '/' )
1315 *p = '\\';
1317 /* Use memmove, as areas overlap */
1318 /* Delete .. */
1319 while ((p = strstr(full_name.short_name,"\\..\\")))
1321 if (p > full_name.short_name+2)
1323 *p = 0;
1324 q = strrchr(full_name.short_name,'\\');
1325 memmove(q+1,p+4,strlen(p+4)+1);
1327 else
1329 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1332 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1334 /* This case istn't treated yet : c:..\test */
1335 memmove(full_name.short_name+2,full_name.short_name+4,
1336 strlen(full_name.short_name+4)+1);
1338 /* Delete . */
1339 while ((p = strstr(full_name.short_name,"\\.\\")))
1341 *(p+1) = 0;
1342 memmove(p+1,p+3,strlen(p+3)+1);
1344 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1345 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1346 namelen=strlen(full_name.short_name);
1347 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1349 /* one more strange case: "c:\test\test1\.."
1350 return "c:\test" */
1351 *(full_name.short_name+namelen-3)=0;
1352 q = strrchr(full_name.short_name,'\\');
1353 *q =0;
1355 if (full_name.short_name[namelen-1]=='.')
1356 full_name.short_name[(namelen--)-1] =0;
1357 if (!driveletter)
1358 if (full_name.short_name[namelen-1]=='\\')
1359 full_name.short_name[(namelen--)-1] =0;
1360 TRACE("got %s\n",full_name.short_name);
1362 /* If the lpBuffer buffer is too small, the return value is the
1363 size of the buffer, in characters, required to hold the path
1364 plus the terminating \0 (tested against win95osr2, bon 001118)
1365 . */
1366 ret = strlen(full_name.short_name);
1367 if (ret >= len )
1369 /* don't touch anything when the buffer is not large enough */
1370 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1371 return ret+1;
1373 if (result)
1375 if (unicode)
1376 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1377 else
1378 lstrcpynA( result, full_name.short_name, len );
1381 TRACE("returning '%s'\n", full_name.short_name );
1382 return ret;
1386 /***********************************************************************
1387 * GetFullPathNameA (KERNEL32.@)
1388 * NOTES
1389 * if the path closed with '\', *lastpart is 0
1391 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1392 LPSTR *lastpart )
1394 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1395 if (ret && (ret<=len) && buffer && lastpart)
1397 LPSTR p = buffer + strlen(buffer);
1399 if (*p != '\\')
1401 while ((p > buffer + 2) && (*p != '\\')) p--;
1402 *lastpart = p + 1;
1404 else *lastpart = NULL;
1406 return ret;
1410 /***********************************************************************
1411 * GetFullPathNameW (KERNEL32.@)
1413 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1414 LPWSTR *lastpart )
1416 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1417 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1418 HeapFree( GetProcessHeap(), 0, nameA );
1419 if (ret && (ret<=len) && buffer && lastpart)
1421 LPWSTR p = buffer + strlenW(buffer);
1422 if (*p != (WCHAR)'\\')
1424 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1425 *lastpart = p + 1;
1427 else *lastpart = NULL;
1429 return ret;
1433 /***********************************************************************
1434 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1436 * Return the full Unix file name for a given path.
1438 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1440 BOOL ret;
1441 DOS_FULL_NAME path;
1442 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1443 return ret;
1447 /***********************************************************************
1448 * DOSFS_FindNextEx
1450 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1452 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1453 UINT flags = DRIVE_GetFlags( info->drive );
1454 char *p, buffer[MAX_PATHNAME_LEN];
1455 const char *drive_path;
1456 int drive_root;
1457 LPCSTR long_name, short_name;
1458 BY_HANDLE_FILE_INFORMATION fileinfo;
1459 char dos_name[13];
1461 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1463 if (info->cur_pos) return 0;
1464 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1465 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1466 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1467 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1468 entry->nFileSizeHigh = 0;
1469 entry->nFileSizeLow = 0;
1470 entry->dwReserved0 = 0;
1471 entry->dwReserved1 = 0;
1472 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1473 strcpy( entry->cAlternateFileName, entry->cFileName );
1474 info->cur_pos++;
1475 TRACE("returning %s (%s) as label\n",
1476 entry->cFileName, entry->cAlternateFileName);
1477 return 1;
1480 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1481 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1482 drive_root = !*drive_path;
1484 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1485 strcat( buffer, "/" );
1486 p = buffer + strlen(buffer);
1488 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1490 info->cur_pos++;
1492 /* Don't return '.' and '..' in the root of the drive */
1493 if (drive_root && (long_name[0] == '.') &&
1494 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1495 continue;
1497 /* Check the long mask */
1499 if (info->long_mask)
1501 if (!DOSFS_MatchLong( info->long_mask, long_name,
1502 flags & DRIVE_CASE_SENSITIVE )) continue;
1505 /* Check the short mask */
1507 if (info->short_mask)
1509 if (!short_name)
1511 DOSFS_Hash( long_name, dos_name, TRUE,
1512 !(flags & DRIVE_CASE_SENSITIVE) );
1513 short_name = dos_name;
1515 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1518 /* Check the file attributes */
1520 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1521 if (!FILE_Stat( buffer, &fileinfo ))
1523 WARN("can't stat %s\n", buffer);
1524 continue;
1526 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1527 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1529 static int show_dir_symlinks = -1;
1530 if (show_dir_symlinks == -1)
1531 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1532 if (!show_dir_symlinks) continue;
1535 if (fileinfo.dwFileAttributes & ~attr) continue;
1537 /* We now have a matching entry; fill the result and return */
1539 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1540 entry->ftCreationTime = fileinfo.ftCreationTime;
1541 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1542 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1543 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1544 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1546 if (short_name)
1547 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1548 else
1549 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1550 !(flags & DRIVE_CASE_SENSITIVE) );
1552 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1553 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1554 TRACE("returning %s (%s) %02lx %ld\n",
1555 entry->cFileName, entry->cAlternateFileName,
1556 entry->dwFileAttributes, entry->nFileSizeLow );
1557 return 1;
1559 return 0; /* End of directory */
1562 /***********************************************************************
1563 * DOSFS_FindNext
1565 * Find the next matching file. Return the number of entries read to find
1566 * the matching one, or 0 if no more entries.
1567 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1568 * file name mask. Either or both can be NULL.
1570 * NOTE: This is supposed to be only called by the int21 emulation
1571 * routines. Thus, we should own the Win16Mutex anyway.
1572 * Nevertheless, we explicitly enter it to ensure the static
1573 * directory cache is protected.
1575 int DOSFS_FindNext( const char *path, const char *short_mask,
1576 const char *long_mask, int drive, BYTE attr,
1577 int skip, WIN32_FIND_DATAA *entry )
1579 static FIND_FIRST_INFO info;
1580 LPCSTR short_name, long_name;
1581 int count;
1583 _EnterWin16Lock();
1585 /* Check the cached directory */
1586 if (!(info.dir && info.path == path && info.short_mask == short_mask
1587 && info.long_mask == long_mask && info.drive == drive
1588 && info.attr == attr && info.cur_pos <= skip))
1590 /* Not in the cache, open it anew */
1591 if (info.dir) DOSFS_CloseDir( info.dir );
1593 info.path = (LPSTR)path;
1594 info.long_mask = (LPSTR)long_mask;
1595 info.short_mask = (LPSTR)short_mask;
1596 info.attr = attr;
1597 info.drive = drive;
1598 info.cur_pos = 0;
1599 info.dir = DOSFS_OpenDir( info.path );
1602 /* Skip to desired position */
1603 while (info.cur_pos < skip)
1604 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1605 info.cur_pos++;
1606 else
1607 break;
1609 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1610 count = info.cur_pos - skip;
1611 else
1612 count = 0;
1614 if (!count)
1616 if (info.dir) DOSFS_CloseDir( info.dir );
1617 memset( &info, '\0', sizeof(info) );
1620 _LeaveWin16Lock();
1622 return count;
1625 /*************************************************************************
1626 * FindFirstFileExA (KERNEL32.@)
1628 HANDLE WINAPI FindFirstFileExA(
1629 LPCSTR lpFileName,
1630 FINDEX_INFO_LEVELS fInfoLevelId,
1631 LPVOID lpFindFileData,
1632 FINDEX_SEARCH_OPS fSearchOp,
1633 LPVOID lpSearchFilter,
1634 DWORD dwAdditionalFlags)
1636 DOS_FULL_NAME full_name;
1637 HGLOBAL handle;
1638 FIND_FIRST_INFO *info;
1640 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1642 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1643 return INVALID_HANDLE_VALUE;
1646 switch(fInfoLevelId)
1648 case FindExInfoStandard:
1650 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1651 data->dwReserved0 = data->dwReserved1 = 0x0;
1652 if (!lpFileName) return 0;
1653 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1654 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1655 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1656 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1657 strcpy( info->path, full_name.long_name );
1658 info->long_mask = strrchr( info->path, '/' );
1659 *(info->long_mask++) = '\0';
1660 info->short_mask = NULL;
1661 info->attr = 0xff;
1662 if (lpFileName[0] && (lpFileName[1] == ':'))
1663 info->drive = FILE_toupper(*lpFileName) - 'A';
1664 else info->drive = DRIVE_GetCurrentDrive();
1665 info->cur_pos = 0;
1667 info->dir = DOSFS_OpenDir( info->path );
1669 GlobalUnlock( handle );
1670 if (!FindNextFileA( handle, data ))
1672 FindClose( handle );
1673 SetLastError( ERROR_NO_MORE_FILES );
1674 break;
1676 return handle;
1678 break;
1679 default:
1680 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1682 return INVALID_HANDLE_VALUE;
1685 /*************************************************************************
1686 * FindFirstFileA (KERNEL32.@)
1688 HANDLE WINAPI FindFirstFileA(
1689 LPCSTR lpFileName,
1690 WIN32_FIND_DATAA *lpFindData )
1692 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1693 FindExSearchNameMatch, NULL, 0);
1696 /*************************************************************************
1697 * FindFirstFileExW (KERNEL32.@)
1699 HANDLE WINAPI FindFirstFileExW(
1700 LPCWSTR lpFileName,
1701 FINDEX_INFO_LEVELS fInfoLevelId,
1702 LPVOID lpFindFileData,
1703 FINDEX_SEARCH_OPS fSearchOp,
1704 LPVOID lpSearchFilter,
1705 DWORD dwAdditionalFlags)
1707 HANDLE handle;
1708 WIN32_FIND_DATAA dataA;
1709 LPVOID _lpFindFileData;
1710 LPSTR pathA;
1712 switch(fInfoLevelId)
1714 case FindExInfoStandard:
1716 _lpFindFileData = &dataA;
1718 break;
1719 default:
1720 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1721 return INVALID_HANDLE_VALUE;
1724 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1725 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1726 HeapFree( GetProcessHeap(), 0, pathA );
1727 if (handle == INVALID_HANDLE_VALUE) return handle;
1729 switch(fInfoLevelId)
1731 case FindExInfoStandard:
1733 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1734 dataW->dwFileAttributes = dataA.dwFileAttributes;
1735 dataW->ftCreationTime = dataA.ftCreationTime;
1736 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1737 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1738 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1739 dataW->nFileSizeLow = dataA.nFileSizeLow;
1740 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1741 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1742 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1743 dataW->cAlternateFileName,
1744 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1746 break;
1747 default:
1748 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1749 return INVALID_HANDLE_VALUE;
1751 return handle;
1754 /*************************************************************************
1755 * FindFirstFileW (KERNEL32.@)
1757 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1759 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1760 FindExSearchNameMatch, NULL, 0);
1763 /*************************************************************************
1764 * FindNextFileA (KERNEL32.@)
1766 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1768 FIND_FIRST_INFO *info;
1770 if ((handle == INVALID_HANDLE_VALUE) ||
1771 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1773 SetLastError( ERROR_INVALID_HANDLE );
1774 return FALSE;
1776 GlobalUnlock( handle );
1777 if (!info->path || !info->dir)
1779 SetLastError( ERROR_NO_MORE_FILES );
1780 return FALSE;
1782 if (!DOSFS_FindNextEx( info, data ))
1784 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1785 HeapFree( GetProcessHeap(), 0, info->path );
1786 info->path = info->long_mask = NULL;
1787 SetLastError( ERROR_NO_MORE_FILES );
1788 return FALSE;
1790 return TRUE;
1794 /*************************************************************************
1795 * FindNextFileW (KERNEL32.@)
1797 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1799 WIN32_FIND_DATAA dataA;
1800 if (!FindNextFileA( handle, &dataA )) return FALSE;
1801 data->dwFileAttributes = dataA.dwFileAttributes;
1802 data->ftCreationTime = dataA.ftCreationTime;
1803 data->ftLastAccessTime = dataA.ftLastAccessTime;
1804 data->ftLastWriteTime = dataA.ftLastWriteTime;
1805 data->nFileSizeHigh = dataA.nFileSizeHigh;
1806 data->nFileSizeLow = dataA.nFileSizeLow;
1807 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1808 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1809 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1810 data->cAlternateFileName,
1811 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1812 return TRUE;
1815 /*************************************************************************
1816 * FindClose (KERNEL32.@)
1818 BOOL WINAPI FindClose( HANDLE handle )
1820 FIND_FIRST_INFO *info;
1822 if (handle == INVALID_HANDLE_VALUE) goto error;
1824 __TRY
1826 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1828 if (info->dir) DOSFS_CloseDir( info->dir );
1829 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1832 __EXCEPT(page_fault)
1834 WARN("Illegal handle %x\n", handle);
1835 SetLastError( ERROR_INVALID_HANDLE );
1836 return FALSE;
1838 __ENDTRY
1839 if (!info) goto error;
1840 GlobalUnlock( handle );
1841 GlobalFree( handle );
1842 return TRUE;
1844 error:
1845 SetLastError( ERROR_INVALID_HANDLE );
1846 return FALSE;
1849 /***********************************************************************
1850 * DOSFS_UnixTimeToFileTime
1852 * Convert a Unix time to FILETIME format.
1853 * The FILETIME structure is a 64-bit value representing the number of
1854 * 100-nanosecond intervals since January 1, 1601, 0:00.
1855 * 'remainder' is the nonnegative number of 100-ns intervals
1856 * corresponding to the time fraction smaller than 1 second that
1857 * couldn't be stored in the time_t value.
1859 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1860 DWORD remainder )
1862 /* NOTES:
1864 CONSTANTS:
1865 The time difference between 1 January 1601, 00:00:00 and
1866 1 January 1970, 00:00:00 is 369 years, plus the leap years
1867 from 1604 to 1968, excluding 1700, 1800, 1900.
1868 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1869 of 134774 days.
1871 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1873 The time difference is 134774 * 86400 * 10000000, which can be written
1874 116444736000000000
1875 27111902 * 2^32 + 3577643008
1876 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1878 If you find that these constants are buggy, please change them in all
1879 instances in both conversion functions.
1881 VERSIONS:
1882 There are two versions, one of them uses long long variables and
1883 is presumably faster but not ISO C. The other one uses standard C
1884 data types and operations but relies on the assumption that negative
1885 numbers are stored as 2's complement (-1 is 0xffff....). If this
1886 assumption is violated, dates before 1970 will not convert correctly.
1887 This should however work on any reasonable architecture where WINE
1888 will run.
1890 DETAILS:
1892 Take care not to remove the casts. I have tested these functions
1893 (in both versions) for a lot of numbers. I would be interested in
1894 results on other compilers than GCC.
1896 The operations have been designed to account for the possibility
1897 of 64-bit time_t in future UNICES. Even the versions without
1898 internal long long numbers will work if time_t only is 64 bit.
1899 A 32-bit shift, which was necessary for that operation, turned out
1900 not to work correctly in GCC, besides giving the warning. So I
1901 used a double 16-bit shift instead. Numbers are in the ISO version
1902 represented by three limbs, the most significant with 32 bit, the
1903 other two with 16 bit each.
1905 As the modulo-operator % is not well-defined for negative numbers,
1906 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1908 There might be quicker ways to do this in C. Certainly so in
1909 assembler.
1911 Claus Fischer, fischer@iue.tuwien.ac.at
1914 #if SIZEOF_LONG_LONG >= 8
1915 # define USE_LONG_LONG 1
1916 #else
1917 # define USE_LONG_LONG 0
1918 #endif
1920 #if USE_LONG_LONG /* gcc supports long long type */
1922 long long int t = unix_time;
1923 t *= 10000000;
1924 t += 116444736000000000LL;
1925 t += remainder;
1926 filetime->dwLowDateTime = (UINT)t;
1927 filetime->dwHighDateTime = (UINT)(t >> 32);
1929 #else /* ISO version */
1931 UINT a0; /* 16 bit, low bits */
1932 UINT a1; /* 16 bit, medium bits */
1933 UINT a2; /* 32 bit, high bits */
1935 /* Copy the unix time to a2/a1/a0 */
1936 a0 = unix_time & 0xffff;
1937 a1 = (unix_time >> 16) & 0xffff;
1938 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1939 Do not replace this by >> 32, it gives a compiler warning and it does
1940 not work. */
1941 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1942 ~((~unix_time >> 16) >> 16));
1944 /* Multiply a by 10000000 (a = a2/a1/a0)
1945 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1946 a0 *= 10000;
1947 a1 = a1 * 10000 + (a0 >> 16);
1948 a2 = a2 * 10000 + (a1 >> 16);
1949 a0 &= 0xffff;
1950 a1 &= 0xffff;
1952 a0 *= 1000;
1953 a1 = a1 * 1000 + (a0 >> 16);
1954 a2 = a2 * 1000 + (a1 >> 16);
1955 a0 &= 0xffff;
1956 a1 &= 0xffff;
1958 /* Add the time difference and the remainder */
1959 a0 += 32768 + (remainder & 0xffff);
1960 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1961 a2 += 27111902 + (a1 >> 16);
1962 a0 &= 0xffff;
1963 a1 &= 0xffff;
1965 /* Set filetime */
1966 filetime->dwLowDateTime = (a1 << 16) + a0;
1967 filetime->dwHighDateTime = a2;
1968 #endif
1972 /***********************************************************************
1973 * DOSFS_FileTimeToUnixTime
1975 * Convert a FILETIME format to Unix time.
1976 * If not NULL, 'remainder' contains the fractional part of the filetime,
1977 * in the range of [0..9999999] (even if time_t is negative).
1979 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1981 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1982 #if USE_LONG_LONG
1984 long long int t = filetime->dwHighDateTime;
1985 t <<= 32;
1986 t += (UINT)filetime->dwLowDateTime;
1987 t -= 116444736000000000LL;
1988 if (t < 0)
1990 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1991 return -1 - ((-t - 1) / 10000000);
1993 else
1995 if (remainder) *remainder = t % 10000000;
1996 return t / 10000000;
1999 #else /* ISO version */
2001 UINT a0; /* 16 bit, low bits */
2002 UINT a1; /* 16 bit, medium bits */
2003 UINT a2; /* 32 bit, high bits */
2004 UINT r; /* remainder of division */
2005 unsigned int carry; /* carry bit for subtraction */
2006 int negative; /* whether a represents a negative value */
2008 /* Copy the time values to a2/a1/a0 */
2009 a2 = (UINT)filetime->dwHighDateTime;
2010 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2011 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2013 /* Subtract the time difference */
2014 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2015 else a0 += (1 << 16) - 32768 , carry = 1;
2017 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2018 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2020 a2 -= 27111902 + carry;
2022 /* If a is negative, replace a by (-1-a) */
2023 negative = (a2 >= ((UINT)1) << 31);
2024 if (negative)
2026 /* Set a to -a - 1 (a is a2/a1/a0) */
2027 a0 = 0xffff - a0;
2028 a1 = 0xffff - a1;
2029 a2 = ~a2;
2032 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2033 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2034 a1 += (a2 % 10000) << 16;
2035 a2 /= 10000;
2036 a0 += (a1 % 10000) << 16;
2037 a1 /= 10000;
2038 r = a0 % 10000;
2039 a0 /= 10000;
2041 a1 += (a2 % 1000) << 16;
2042 a2 /= 1000;
2043 a0 += (a1 % 1000) << 16;
2044 a1 /= 1000;
2045 r += (a0 % 1000) * 10000;
2046 a0 /= 1000;
2048 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2049 if (negative)
2051 /* Set a to -a - 1 (a is a2/a1/a0) */
2052 a0 = 0xffff - a0;
2053 a1 = 0xffff - a1;
2054 a2 = ~a2;
2056 r = 9999999 - r;
2059 if (remainder) *remainder = r;
2061 /* Do not replace this by << 32, it gives a compiler warning and it does
2062 not work. */
2063 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2064 #endif
2068 /***********************************************************************
2069 * MulDiv (KERNEL32.@)
2070 * RETURNS
2071 * Result of multiplication and division
2072 * -1: Overflow occurred or Divisor was 0
2074 INT WINAPI MulDiv(
2075 INT nMultiplicand,
2076 INT nMultiplier,
2077 INT nDivisor)
2079 #if SIZEOF_LONG_LONG >= 8
2080 long long ret;
2082 if (!nDivisor) return -1;
2084 /* We want to deal with a positive divisor to simplify the logic. */
2085 if (nDivisor < 0)
2087 nMultiplicand = - nMultiplicand;
2088 nDivisor = -nDivisor;
2091 /* If the result is positive, we "add" to round. else, we subtract to round. */
2092 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2093 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2094 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2095 else
2096 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2098 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2099 return ret;
2100 #else
2101 if (!nDivisor) return -1;
2103 /* We want to deal with a positive divisor to simplify the logic. */
2104 if (nDivisor < 0)
2106 nMultiplicand = - nMultiplicand;
2107 nDivisor = -nDivisor;
2110 /* If the result is positive, we "add" to round. else, we subtract to round. */
2111 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2112 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2113 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2115 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2117 #endif
2121 /***********************************************************************
2122 * DosDateTimeToFileTime (KERNEL32.@)
2124 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2126 struct tm newtm;
2127 #ifndef HAVE_TIMEGM
2128 struct tm *gtm;
2129 time_t time1, time2;
2130 #endif
2132 newtm.tm_sec = (fattime & 0x1f) * 2;
2133 newtm.tm_min = (fattime >> 5) & 0x3f;
2134 newtm.tm_hour = (fattime >> 11);
2135 newtm.tm_mday = (fatdate & 0x1f);
2136 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2137 newtm.tm_year = (fatdate >> 9) + 80;
2138 #ifdef HAVE_TIMEGM
2139 RtlSecondsSince1970ToTime( timegm(&newtm), ft );
2140 #else
2141 time1 = mktime(&newtm);
2142 gtm = gmtime(&time1);
2143 time2 = mktime(gtm);
2144 RtlSecondsSince1970ToTime( 2*time1-time2, ft );
2145 #endif
2146 return TRUE;
2150 /***********************************************************************
2151 * FileTimeToDosDateTime (KERNEL32.@)
2153 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2154 LPWORD fattime )
2156 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2157 struct tm *tm = gmtime( &unixtime );
2158 if (fattime)
2159 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2160 if (fatdate)
2161 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2162 + tm->tm_mday;
2163 return TRUE;
2167 /***********************************************************************
2168 * LocalFileTimeToFileTime (KERNEL32.@)
2170 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2171 LPFILETIME utcft )
2173 struct tm *xtm;
2174 DWORD remainder;
2175 time_t utctime;
2177 /* Converts from local to UTC. */
2178 time_t localtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2179 xtm = gmtime( &localtime );
2180 utctime = mktime(xtm);
2181 if(xtm->tm_isdst > 0) utctime-=3600;
2182 DOSFS_UnixTimeToFileTime( utctime, utcft, remainder );
2183 return TRUE;
2187 /***********************************************************************
2188 * FileTimeToLocalFileTime (KERNEL32.@)
2190 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2191 LPFILETIME localft )
2193 DWORD remainder;
2194 /* Converts from UTC to local. */
2195 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2196 #ifdef HAVE_TIMEGM
2197 struct tm *xtm = localtime( &unixtime );
2198 time_t localtime;
2200 localtime = timegm(xtm);
2201 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2203 #else
2204 struct tm *xtm;
2205 time_t time;
2207 xtm = gmtime( &unixtime );
2208 time = mktime(xtm);
2209 if(xtm->tm_isdst > 0) time-=3600;
2210 DOSFS_UnixTimeToFileTime( 2*unixtime-time, localft, remainder );
2211 #endif
2212 return TRUE;
2216 /***********************************************************************
2217 * FileTimeToSystemTime (KERNEL32.@)
2219 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2221 struct tm *xtm;
2222 DWORD remainder;
2223 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2224 xtm = gmtime(&xtime);
2225 syst->wYear = xtm->tm_year+1900;
2226 syst->wMonth = xtm->tm_mon + 1;
2227 syst->wDayOfWeek = xtm->tm_wday;
2228 syst->wDay = xtm->tm_mday;
2229 syst->wHour = xtm->tm_hour;
2230 syst->wMinute = xtm->tm_min;
2231 syst->wSecond = xtm->tm_sec;
2232 syst->wMilliseconds = remainder / 10000;
2233 return TRUE;
2236 /***********************************************************************
2237 * QueryDosDeviceA (KERNEL32.@)
2239 * returns array of strings terminated by \0, terminated by \0
2241 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2243 LPSTR s;
2244 char buffer[200];
2246 TRACE("(%s,...)\n", devname ? devname : "<null>");
2247 if (!devname) {
2248 /* return known MSDOS devices */
2249 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2250 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2251 return min(bufsize,sizeof(devices));
2253 /* In theory all that are possible and have been defined.
2254 * Now just those below, since mirc uses it to check for special files.
2256 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2257 * but currently we just ignore that.)
2259 #define CHECK(x) (strstr(devname,#x)==devname)
2260 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2261 strcpy(buffer,"\\DEV\\");
2262 strcat(buffer,devname);
2263 if ((s=strchr(buffer,':'))) *s='\0';
2264 lstrcpynA(target,buffer,bufsize);
2265 return strlen(buffer)+1;
2266 } else {
2267 if (strchr(devname,':') || devname[0]=='\\') {
2268 /* This might be a DOS device we do not handle yet ... */
2269 FIXME("(%s) not detected as DOS device!\n",devname);
2271 SetLastError(ERROR_DEV_NOT_EXIST);
2272 return 0;
2278 /***********************************************************************
2279 * QueryDosDeviceW (KERNEL32.@)
2281 * returns array of strings terminated by \0, terminated by \0
2283 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2285 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2286 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2287 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2289 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2290 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2291 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2292 return ret;
2296 /***********************************************************************
2297 * SystemTimeToFileTime (KERNEL32.@)
2299 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2301 #ifdef HAVE_TIMEGM
2302 struct tm xtm;
2303 time_t utctime;
2304 #else
2305 struct tm xtm,*utc_tm;
2306 time_t localtim,utctime;
2307 #endif
2309 xtm.tm_year = syst->wYear-1900;
2310 xtm.tm_mon = syst->wMonth - 1;
2311 xtm.tm_wday = syst->wDayOfWeek;
2312 xtm.tm_mday = syst->wDay;
2313 xtm.tm_hour = syst->wHour;
2314 xtm.tm_min = syst->wMinute;
2315 xtm.tm_sec = syst->wSecond; /* this is UTC */
2316 xtm.tm_isdst = -1;
2317 #ifdef HAVE_TIMEGM
2318 utctime = timegm(&xtm);
2319 DOSFS_UnixTimeToFileTime( utctime, ft,
2320 syst->wMilliseconds * 10000 );
2321 #else
2322 localtim = mktime(&xtm); /* now we've got local time */
2323 utc_tm = gmtime(&localtim);
2324 utctime = mktime(utc_tm);
2325 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2326 syst->wMilliseconds * 10000 );
2327 #endif
2328 return TRUE;
2331 /***********************************************************************
2332 * DefineDosDeviceA (KERNEL32.@)
2334 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2335 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2336 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2337 return FALSE;
2341 --- 16 bit functions ---
2344 /*************************************************************************
2345 * FindFirstFile (KERNEL.413)
2347 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2349 DOS_FULL_NAME full_name;
2350 HGLOBAL16 handle;
2351 FIND_FIRST_INFO *info;
2353 data->dwReserved0 = data->dwReserved1 = 0x0;
2354 if (!path) return 0;
2355 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2356 return INVALID_HANDLE_VALUE16;
2357 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2358 return INVALID_HANDLE_VALUE16;
2359 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2360 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2361 strcpy( info->path, full_name.long_name );
2362 info->long_mask = strrchr( info->path, '/' );
2363 if (info->long_mask )
2364 *(info->long_mask++) = '\0';
2365 info->short_mask = NULL;
2366 info->attr = 0xff;
2367 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2368 else info->drive = DRIVE_GetCurrentDrive();
2369 info->cur_pos = 0;
2371 info->dir = DOSFS_OpenDir( info->path );
2373 GlobalUnlock16( handle );
2374 if (!FindNextFile16( handle, data ))
2376 FindClose16( handle );
2377 SetLastError( ERROR_NO_MORE_FILES );
2378 return INVALID_HANDLE_VALUE16;
2380 return handle;
2383 /*************************************************************************
2384 * FindNextFile (KERNEL.414)
2386 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2388 FIND_FIRST_INFO *info;
2390 if ((handle == INVALID_HANDLE_VALUE16) ||
2391 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2393 SetLastError( ERROR_INVALID_HANDLE );
2394 return FALSE;
2396 GlobalUnlock16( handle );
2397 if (!info->path || !info->dir)
2399 SetLastError( ERROR_NO_MORE_FILES );
2400 return FALSE;
2402 if (!DOSFS_FindNextEx( info, data ))
2404 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2405 HeapFree( GetProcessHeap(), 0, info->path );
2406 info->path = info->long_mask = NULL;
2407 SetLastError( ERROR_NO_MORE_FILES );
2408 return FALSE;
2410 return TRUE;
2413 /*************************************************************************
2414 * FindClose (KERNEL.415)
2416 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2418 FIND_FIRST_INFO *info;
2420 if ((handle == INVALID_HANDLE_VALUE16) ||
2421 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2423 SetLastError( ERROR_INVALID_HANDLE );
2424 return FALSE;
2426 if (info->dir) DOSFS_CloseDir( info->dir );
2427 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2428 GlobalUnlock16( handle );
2429 GlobalFree16( handle );
2430 return TRUE;