Fixed make uninstall.
[wine/gsoc_dplay.git] / files / dos_fs.c
blobff756fce34eb0f0232e51c77a291c06c8a20cb17
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #ifdef HAVE_SYS_ERRNO_H
15 #include <sys/errno.h>
16 #endif
17 #include <fcntl.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <sys/stat.h>
21 #include <sys/ioctl.h>
22 #include <time.h>
23 #include <unistd.h>
25 #include "windef.h"
26 #include "winerror.h"
27 #include "wingdi.h"
29 #include "wine/unicode.h"
30 #include "wine/winbase16.h"
31 #include "drive.h"
32 #include "file.h"
33 #include "heap.h"
34 #include "msdos.h"
35 #include "ntddk.h"
36 #include "options.h"
37 #include "wine/server.h"
38 #include "msvcrt/excpt.h"
40 #include "debugtools.h"
42 DEFAULT_DEBUG_CHANNEL(dosfs);
43 DECLARE_DEBUG_CHANNEL(file);
45 /* Define the VFAT ioctl to get both short and long file names */
46 /* FIXME: is it possible to get this to work on other systems? */
47 #ifdef linux
48 /* We want the real kernel dirent structure, not the libc one */
49 typedef struct
51 long d_ino;
52 long d_off;
53 unsigned short d_reclen;
54 char d_name[256];
55 } KERNEL_DIRENT;
57 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
59 #else /* linux */
60 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
61 #endif /* linux */
63 /* Chars we don't want to see in DOS file names */
64 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
66 static const DOS_DEVICE DOSFS_Devices[] =
67 /* name, device flags (see Int 21/AX=0x4400) */
69 { "CON", 0xc0d3 },
70 { "PRN", 0xa0c0 },
71 { "NUL", 0x80c4 },
72 { "AUX", 0x80c0 },
73 { "LPT1", 0xa0c0 },
74 { "LPT2", 0xa0c0 },
75 { "LPT3", 0xa0c0 },
76 { "LPT4", 0xc0d3 },
77 { "COM1", 0x80c0 },
78 { "COM2", 0x80c0 },
79 { "COM3", 0x80c0 },
80 { "COM4", 0x80c0 },
81 { "SCSIMGR$", 0xc0c0 },
82 { "HPSCAN", 0xc0c0 },
83 { "EMMXXXX0", 0x0000 }
86 #define GET_DRIVE(path) \
87 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
89 /* Directory info for DOSFS_ReadDir */
90 typedef struct
92 DIR *dir;
93 #ifdef VFAT_IOCTL_READDIR_BOTH
94 int fd;
95 char short_name[12];
96 KERNEL_DIRENT dirent[2];
97 #endif
98 } DOS_DIR;
100 /* Info structure for FindFirstFile handle */
101 typedef struct
103 LPSTR path;
104 LPSTR long_mask;
105 LPSTR short_mask;
106 BYTE attr;
107 int drive;
108 int cur_pos;
109 DOS_DIR *dir;
110 } FIND_FIRST_INFO;
113 static WINE_EXCEPTION_FILTER(page_fault)
115 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
116 return EXCEPTION_EXECUTE_HANDLER;
117 return EXCEPTION_CONTINUE_SEARCH;
121 /***********************************************************************
122 * DOSFS_ValidDOSName
124 * Return 1 if Unix file 'name' is also a valid MS-DOS name
125 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
126 * File name can be terminated by '\0', '\\' or '/'.
128 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
130 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
131 const char *p = name;
132 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
133 int len = 0;
135 if (*p == '.')
137 /* Check for "." and ".." */
138 p++;
139 if (*p == '.') p++;
140 /* All other names beginning with '.' are invalid */
141 return (IS_END_OF_NAME(*p));
143 while (!IS_END_OF_NAME(*p))
145 if (strchr( invalid, *p )) return 0; /* Invalid char */
146 if (*p == '.') break; /* Start of the extension */
147 if (++len > 8) return 0; /* Name too long */
148 p++;
150 if (*p != '.') return 1; /* End of name */
151 p++;
152 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
153 len = 0;
154 while (!IS_END_OF_NAME(*p))
156 if (strchr( invalid, *p )) return 0; /* Invalid char */
157 if (*p == '.') return 0; /* Second extension not allowed */
158 if (++len > 3) return 0; /* Extension too long */
159 p++;
161 return 1;
165 /***********************************************************************
166 * DOSFS_ToDosFCBFormat
168 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
169 * expanding wild cards and converting to upper-case in the process.
170 * File name can be terminated by '\0', '\\' or '/'.
171 * Return FALSE if the name is not a valid DOS name.
172 * 'buffer' must be at least 12 characters long.
174 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
176 static const char invalid_chars[] = INVALID_DOS_CHARS;
177 const char *p = name;
178 int i;
180 /* Check for "." and ".." */
181 if (*p == '.')
183 p++;
184 strcpy( buffer, ". " );
185 if (*p == '.')
187 buffer[1] = '.';
188 p++;
190 return (!*p || (*p == '/') || (*p == '\\'));
193 for (i = 0; i < 8; i++)
195 switch(*p)
197 case '\0':
198 case '\\':
199 case '/':
200 case '.':
201 buffer[i] = ' ';
202 break;
203 case '?':
204 p++;
205 /* fall through */
206 case '*':
207 buffer[i] = '?';
208 break;
209 default:
210 if (strchr( invalid_chars, *p )) return FALSE;
211 buffer[i] = FILE_toupper(*p);
212 p++;
213 break;
217 if (*p == '*')
219 /* Skip all chars after wildcard up to first dot */
220 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
222 else
224 /* Check if name too long */
225 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
227 if (*p == '.') p++; /* Skip dot */
229 for (i = 8; i < 11; i++)
231 switch(*p)
233 case '\0':
234 case '\\':
235 case '/':
236 buffer[i] = ' ';
237 break;
238 case '.':
239 return FALSE; /* Second extension not allowed */
240 case '?':
241 p++;
242 /* fall through */
243 case '*':
244 buffer[i] = '?';
245 break;
246 default:
247 if (strchr( invalid_chars, *p )) return FALSE;
248 buffer[i] = FILE_toupper(*p);
249 p++;
250 break;
253 buffer[11] = '\0';
255 /* at most 3 character of the extension are processed
256 * is something behind this ?
258 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
259 return IS_END_OF_NAME(*p);
263 /***********************************************************************
264 * DOSFS_ToDosDTAFormat
266 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
267 * converting to upper-case in the process.
268 * File name can be terminated by '\0', '\\' or '/'.
269 * 'buffer' must be at least 13 characters long.
271 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
273 char *p;
275 memcpy( buffer, name, 8 );
276 p = buffer + 8;
277 while ((p > buffer) && (p[-1] == ' ')) p--;
278 *p++ = '.';
279 memcpy( p, name + 8, 3 );
280 p += 3;
281 while (p[-1] == ' ') p--;
282 if (p[-1] == '.') p--;
283 *p = '\0';
287 /***********************************************************************
288 * DOSFS_MatchShort
290 * Check a DOS file name against a mask (both in FCB format).
292 static int DOSFS_MatchShort( const char *mask, const char *name )
294 int i;
295 for (i = 11; i > 0; i--, mask++, name++)
296 if ((*mask != '?') && (*mask != *name)) return 0;
297 return 1;
301 /***********************************************************************
302 * DOSFS_MatchLong
304 * Check a long file name against a mask.
306 * Tests (done in W95 DOS shell - case insensitive):
307 * *.txt test1.test.txt *
308 * *st1* test1.txt *
309 * *.t??????.t* test1.ta.tornado.txt *
310 * *tornado* test1.ta.tornado.txt *
311 * t*t test1.ta.tornado.txt *
312 * ?est* test1.txt *
313 * ?est??? test1.txt -
314 * *test1.txt* test1.txt *
315 * h?l?o*t.dat hellothisisatest.dat *
317 static int DOSFS_MatchLong( const char *mask, const char *name,
318 int case_sensitive )
320 const char *lastjoker = NULL;
321 const char *next_to_retry = NULL;
323 if (!strcmp( mask, "*.*" )) return 1;
324 while (*name && *mask)
326 if (*mask == '*')
328 mask++;
329 while (*mask == '*') mask++; /* Skip consecutive '*' */
330 lastjoker = mask;
331 if (!*mask) return 1; /* end of mask is all '*', so match */
333 /* skip to the next match after the joker(s) */
334 if (case_sensitive) while (*name && (*name != *mask)) name++;
335 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
337 if (!*name) break;
338 next_to_retry = name;
340 else if (*mask != '?')
342 int mismatch = 0;
343 if (case_sensitive)
345 if (*mask != *name) mismatch = 1;
347 else
349 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
351 if (!mismatch)
353 mask++;
354 name++;
355 if (*mask == '\0')
357 if (*name == '\0')
358 return 1;
359 if (lastjoker)
360 mask = lastjoker;
363 else /* mismatch ! */
365 if (lastjoker) /* we had an '*', so we can try unlimitedly */
367 mask = lastjoker;
369 /* this scan sequence was a mismatch, so restart
370 * 1 char after the first char we checked last time */
371 next_to_retry++;
372 name = next_to_retry;
374 else
375 return 0; /* bad luck */
378 else /* '?' */
380 mask++;
381 name++;
384 while ((*mask == '.') || (*mask == '*'))
385 mask++; /* Ignore trailing '.' or '*' in mask */
386 return (!*name && !*mask);
390 /***********************************************************************
391 * DOSFS_OpenDir
393 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
395 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
396 if (!dir)
398 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
399 return NULL;
402 /* Treat empty path as root directory. This simplifies path split into
403 directory and mask in several other places */
404 if (!*path) path = "/";
406 #ifdef VFAT_IOCTL_READDIR_BOTH
408 /* Check if the VFAT ioctl is supported on this directory */
410 if ((dir->fd = open( path, O_RDONLY )) != -1)
412 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
414 close( dir->fd );
415 dir->fd = -1;
417 else
419 /* Set the file pointer back at the start of the directory */
420 lseek( dir->fd, 0, SEEK_SET );
421 dir->dir = NULL;
422 return dir;
425 #endif /* VFAT_IOCTL_READDIR_BOTH */
427 /* Now use the standard opendir/readdir interface */
429 if (!(dir->dir = opendir( path )))
431 HeapFree( GetProcessHeap(), 0, dir );
432 return NULL;
434 return dir;
438 /***********************************************************************
439 * DOSFS_CloseDir
441 static void DOSFS_CloseDir( DOS_DIR *dir )
443 #ifdef VFAT_IOCTL_READDIR_BOTH
444 if (dir->fd != -1) close( dir->fd );
445 #endif /* VFAT_IOCTL_READDIR_BOTH */
446 if (dir->dir) closedir( dir->dir );
447 HeapFree( GetProcessHeap(), 0, dir );
451 /***********************************************************************
452 * DOSFS_ReadDir
454 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
455 LPCSTR *short_name )
457 struct dirent *dirent;
459 #ifdef VFAT_IOCTL_READDIR_BOTH
460 if (dir->fd != -1)
462 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
463 if (!dir->dirent[0].d_reclen) return FALSE;
464 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
465 dir->short_name[0] = '\0';
466 *short_name = dir->short_name;
467 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
468 else *long_name = dir->dirent[0].d_name;
469 return TRUE;
472 #endif /* VFAT_IOCTL_READDIR_BOTH */
474 if (!(dirent = readdir( dir->dir ))) return FALSE;
475 *long_name = dirent->d_name;
476 *short_name = NULL;
477 return TRUE;
481 /***********************************************************************
482 * DOSFS_Hash
484 * Transform a Unix file name into a hashed DOS name. If the name is a valid
485 * DOS name, it is converted to upper-case; otherwise it is replaced by a
486 * hashed version that fits in 8.3 format.
487 * File name can be terminated by '\0', '\\' or '/'.
488 * 'buffer' must be at least 13 characters long.
490 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
491 BOOL ignore_case )
493 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
494 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
496 const char *p, *ext;
497 char *dst;
498 unsigned short hash;
499 int i;
501 if (dir_format) strcpy( buffer, " " );
503 if (DOSFS_ValidDOSName( name, ignore_case ))
505 /* Check for '.' and '..' */
506 if (*name == '.')
508 buffer[0] = '.';
509 if (!dir_format) buffer[1] = buffer[2] = '\0';
510 if (name[1] == '.') buffer[1] = '.';
511 return;
514 /* Simply copy the name, converting to uppercase */
516 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
517 *dst++ = FILE_toupper(*name);
518 if (*name == '.')
520 if (dir_format) dst = buffer + 8;
521 else *dst++ = '.';
522 for (name++; !IS_END_OF_NAME(*name); name++)
523 *dst++ = FILE_toupper(*name);
525 if (!dir_format) *dst = '\0';
526 return;
529 /* Compute the hash code of the file name */
530 /* If you know something about hash functions, feel free to */
531 /* insert a better algorithm here... */
532 if (ignore_case)
534 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
535 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
536 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
538 else
540 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
541 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
542 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
545 /* Find last dot for start of the extension */
546 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
547 if (*p == '.') ext = p;
548 if (ext && IS_END_OF_NAME(ext[1]))
549 ext = NULL; /* Empty extension ignored */
551 /* Copy first 4 chars, replacing invalid chars with '_' */
552 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
554 if (IS_END_OF_NAME(*p) || (p == ext)) break;
555 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
557 /* Pad to 5 chars with '~' */
558 while (i-- >= 0) *dst++ = '~';
560 /* Insert hash code converted to 3 ASCII chars */
561 *dst++ = hash_chars[(hash >> 10) & 0x1f];
562 *dst++ = hash_chars[(hash >> 5) & 0x1f];
563 *dst++ = hash_chars[hash & 0x1f];
565 /* Copy the first 3 chars of the extension (if any) */
566 if (ext)
568 if (!dir_format) *dst++ = '.';
569 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
570 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
572 if (!dir_format) *dst = '\0';
576 /***********************************************************************
577 * DOSFS_FindUnixName
579 * Find the Unix file name in a given directory that corresponds to
580 * a file name (either in Unix or DOS format).
581 * File name can be terminated by '\0', '\\' or '/'.
582 * Return TRUE if OK, FALSE if no file name matches.
584 * 'long_buf' must be at least 'long_len' characters long. If the long name
585 * turns out to be larger than that, the function returns FALSE.
586 * 'short_buf' must be at least 13 characters long.
588 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
589 INT long_len, LPSTR short_buf, BOOL ignore_case)
591 DOS_DIR *dir;
592 LPCSTR long_name, short_name;
593 char dos_name[12], tmp_buf[13];
594 BOOL ret;
596 const char *p = strchr( name, '/' );
597 int len = p ? (int)(p - name) : strlen(name);
598 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
599 /* Ignore trailing dots and spaces */
600 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
601 if (long_len < len + 1) return FALSE;
603 TRACE("%s,%s\n", path, name );
605 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
607 if (!(dir = DOSFS_OpenDir( path )))
609 WARN("(%s,%s): can't open dir: %s\n",
610 path, name, strerror(errno) );
611 return FALSE;
614 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
616 /* Check against Unix name */
617 if (len == strlen(long_name))
619 if (!ignore_case)
621 if (!strncmp( long_name, name, len )) break;
623 else
625 if (!FILE_strncasecmp( long_name, name, len )) break;
628 if (dos_name[0])
630 /* Check against hashed DOS name */
631 if (!short_name)
633 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
634 short_name = tmp_buf;
636 if (!strcmp( dos_name, short_name )) break;
639 if (ret)
641 if (long_buf) strcpy( long_buf, long_name );
642 if (short_buf)
644 if (short_name)
645 DOSFS_ToDosDTAFormat( short_name, short_buf );
646 else
647 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
649 TRACE("(%s,%s) -> %s (%s)\n",
650 path, name, long_name, short_buf ? short_buf : "***");
652 else
653 WARN("'%s' not found in '%s'\n", name, path);
654 DOSFS_CloseDir( dir );
655 return ret;
659 /***********************************************************************
660 * DOSFS_GetDevice
662 * Check if a DOS file name represents a DOS device and return the device.
664 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
666 int i;
667 const char *p;
669 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
670 if (name[0] && (name[1] == ':')) name += 2;
671 if ((p = strrchr( name, '/' ))) name = p + 1;
672 if ((p = strrchr( name, '\\' ))) name = p + 1;
673 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
675 const char *dev = DOSFS_Devices[i].name;
676 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
678 p = name + strlen( dev );
679 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
682 return NULL;
686 /***********************************************************************
687 * DOSFS_GetDeviceByHandle
689 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
691 const DOS_DEVICE *ret = NULL;
692 SERVER_START_REQ( get_file_info )
694 req->handle = hFile;
695 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
697 if ((reply->attr >= 0) &&
698 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
699 ret = &DOSFS_Devices[reply->attr];
702 SERVER_END_REQ;
703 return ret;
707 /**************************************************************************
708 * DOSFS_CreateCommPort
710 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
712 HANDLE ret;
713 char devname[40];
715 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
717 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
718 if(!devname[0])
719 return 0;
721 TRACE("opening %s as %s\n", devname, name);
723 SERVER_START_REQ( create_serial )
725 req->access = access;
726 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
727 req->attributes = attributes;
728 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
729 wine_server_add_data( req, devname, strlen(devname) );
730 SetLastError(0);
731 wine_server_call_err( req );
732 ret = reply->handle;
734 SERVER_END_REQ;
736 if(!ret)
737 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
738 else
739 TRACE("return %08X\n", ret );
740 return ret;
743 /***********************************************************************
744 * DOSFS_OpenDevice
746 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
747 * Returns 0 on failure.
749 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
751 int i;
752 const char *p;
753 HANDLE handle;
755 if (name[0] && (name[1] == ':')) name += 2;
756 if ((p = strrchr( name, '/' ))) name = p + 1;
757 if ((p = strrchr( name, '\\' ))) name = p + 1;
758 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
760 const char *dev = DOSFS_Devices[i].name;
761 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
763 p = name + strlen( dev );
764 if (!*p || (*p == '.') || (*p == ':')) {
765 /* got it */
766 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
767 return FILE_CreateFile( "/dev/null", access,
768 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
769 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
770 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
771 HANDLE to_dup;
772 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
773 case GENERIC_READ:
774 to_dup = GetStdHandle( STD_INPUT_HANDLE );
775 break;
776 case GENERIC_WRITE:
777 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
778 break;
779 default:
780 FIXME("can't open CON read/write\n");
781 return 0;
783 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
784 &handle, 0,
785 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
786 DUPLICATE_SAME_ACCESS ))
787 handle = 0;
788 return handle;
790 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
791 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
792 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
794 return FILE_CreateDevice( i, access, sa );
797 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
798 return handle;
799 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
800 return 0;
804 return 0;
808 /***********************************************************************
809 * DOSFS_GetPathDrive
811 * Get the drive specified by a given path name (DOS or Unix format).
813 static int DOSFS_GetPathDrive( const char **name )
815 int drive;
816 const char *p = *name;
818 if (*p && (p[1] == ':'))
820 drive = FILE_toupper(*p) - 'A';
821 *name += 2;
823 else if (*p == '/') /* Absolute Unix path? */
825 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
827 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
828 /* Assume it really was a DOS name */
829 drive = DRIVE_GetCurrentDrive();
832 else drive = DRIVE_GetCurrentDrive();
834 if (!DRIVE_IsValid(drive))
836 SetLastError( ERROR_INVALID_DRIVE );
837 return -1;
839 return drive;
843 /***********************************************************************
844 * DOSFS_GetFullName
846 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
847 * Unix name / short DOS name pair.
848 * Return FALSE if one of the path components does not exist. The last path
849 * component is only checked if 'check_last' is non-zero.
850 * The buffers pointed to by 'long_buf' and 'short_buf' must be
851 * at least MAX_PATHNAME_LEN long.
853 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
855 BOOL found;
856 UINT flags;
857 char *p_l, *p_s, *root;
859 TRACE("%s (last=%d)\n", name, check_last );
861 if ((!*name) || (*name=='\n'))
862 { /* error code for Win98 */
863 SetLastError(ERROR_BAD_PATHNAME);
864 return FALSE;
867 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
868 flags = DRIVE_GetFlags( full->drive );
870 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
871 sizeof(full->long_name) );
872 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
873 else root = full->long_name; /* root directory */
875 strcpy( full->short_name, "A:\\" );
876 full->short_name[0] += full->drive;
878 if ((*name == '\\') || (*name == '/')) /* Absolute path */
880 while ((*name == '\\') || (*name == '/')) name++;
882 else /* Relative path */
884 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
885 sizeof(full->long_name) - (root - full->long_name) - 1 );
886 if (root[1]) *root = '/';
887 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
888 sizeof(full->short_name) - 3 );
891 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
892 : full->long_name;
893 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
894 : full->short_name + 2;
895 found = TRUE;
897 while (*name && found)
899 /* Check for '.' and '..' */
901 if (*name == '.')
903 if (IS_END_OF_NAME(name[1]))
905 name++;
906 while ((*name == '\\') || (*name == '/')) name++;
907 continue;
909 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
911 name += 2;
912 while ((*name == '\\') || (*name == '/')) name++;
913 while ((p_l > root) && (*p_l != '/')) p_l--;
914 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
915 *p_l = *p_s = '\0'; /* Remove trailing separator */
916 continue;
920 /* Make sure buffers are large enough */
922 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
923 (p_l >= full->long_name + sizeof(full->long_name) - 1))
925 SetLastError( ERROR_PATH_NOT_FOUND );
926 return FALSE;
929 /* Get the long and short name matching the file name */
931 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
932 sizeof(full->long_name) - (p_l - full->long_name) - 1,
933 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
935 *p_l++ = '/';
936 p_l += strlen(p_l);
937 *p_s++ = '\\';
938 p_s += strlen(p_s);
939 while (!IS_END_OF_NAME(*name)) name++;
941 else if (!check_last)
943 *p_l++ = '/';
944 *p_s++ = '\\';
945 while (!IS_END_OF_NAME(*name) &&
946 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
947 (p_l < full->long_name + sizeof(full->long_name) - 1))
949 *p_s++ = FILE_tolower(*name);
950 /* If the drive is case-sensitive we want to create new */
951 /* files in lower-case otherwise we can't reopen them */
952 /* under the same short name. */
953 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
954 else *p_l++ = *name;
955 name++;
957 /* Ignore trailing dots and spaces */
958 while(p_l[-1] == '.' || p_l[-1] == ' ') {
959 --p_l;
960 --p_s;
962 *p_l = *p_s = '\0';
964 while ((*name == '\\') || (*name == '/')) name++;
967 if (!found)
969 if (check_last)
971 SetLastError( ERROR_FILE_NOT_FOUND );
972 return FALSE;
974 if (*name) /* Not last */
976 SetLastError( ERROR_PATH_NOT_FOUND );
977 return FALSE;
980 if (!full->long_name[0]) strcpy( full->long_name, "/" );
981 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
982 TRACE("returning %s = %s\n", full->long_name, full->short_name );
983 return TRUE;
987 /***********************************************************************
988 * GetShortPathNameA (KERNEL32.@)
990 * NOTES
991 * observed:
992 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
993 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
995 * more observations ( with NT 3.51 (WinDD) ):
996 * longpath <= 8.3 -> just copy longpath to shortpath
997 * longpath > 8.3 ->
998 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
999 * b) file does exist -> set the short filename.
1000 * - trailing slashes are reproduced in the short name, even if the
1001 * file is not a directory
1002 * - the absolute/relative path of the short name is reproduced like found
1003 * in the long name
1004 * - longpath and shortpath may have the same address
1005 * Peter Ganten, 1999
1007 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1008 DWORD shortlen )
1010 DOS_FULL_NAME full_name;
1011 LPSTR tmpshortpath;
1012 DWORD sp = 0, lp = 0;
1013 int tmplen, drive;
1014 UINT flags;
1016 TRACE("%s\n", debugstr_a(longpath));
1018 if (!longpath) {
1019 SetLastError(ERROR_INVALID_PARAMETER);
1020 return 0;
1022 if (!longpath[0]) {
1023 SetLastError(ERROR_BAD_PATHNAME);
1024 return 0;
1027 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1028 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1029 return 0;
1032 /* check for drive letter */
1033 if ( longpath[1] == ':' ) {
1034 tmpshortpath[0] = longpath[0];
1035 tmpshortpath[1] = ':';
1036 sp = 2;
1039 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1040 flags = DRIVE_GetFlags ( drive );
1042 while ( longpath[lp] ) {
1044 /* check for path delimiters and reproduce them */
1045 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1046 if (!sp || tmpshortpath[sp-1]!= '\\')
1048 /* strip double "\\" */
1049 tmpshortpath[sp] = '\\';
1050 sp++;
1052 tmpshortpath[sp]=0;/*terminate string*/
1053 lp++;
1054 continue;
1057 tmplen = strcspn ( longpath + lp, "\\/" );
1058 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1060 /* Check, if the current element is a valid dos name */
1061 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1062 sp += tmplen;
1063 lp += tmplen;
1064 continue;
1067 /* Check if the file exists and use the existing file name */
1068 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1069 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1070 sp += strlen ( tmpshortpath+sp );
1071 lp += tmplen;
1072 continue;
1075 TRACE("not found!\n" );
1076 SetLastError ( ERROR_FILE_NOT_FOUND );
1077 return 0;
1079 tmpshortpath[sp] = 0;
1081 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1082 TRACE("returning %s\n", debugstr_a(shortpath) );
1083 tmplen = strlen ( tmpshortpath );
1084 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1086 return tmplen;
1090 /***********************************************************************
1091 * GetShortPathNameW (KERNEL32.@)
1093 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1094 DWORD shortlen )
1096 LPSTR longpathA, shortpathA;
1097 DWORD ret = 0;
1099 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1100 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1102 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1103 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1104 shortpath[shortlen-1] = 0;
1105 HeapFree( GetProcessHeap(), 0, longpathA );
1106 HeapFree( GetProcessHeap(), 0, shortpathA );
1108 return ret;
1112 /***********************************************************************
1113 * GetLongPathNameA (KERNEL32.@)
1115 * NOTES
1116 * observed (Win2000):
1117 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1118 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1120 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1121 DWORD longlen )
1123 DOS_FULL_NAME full_name;
1124 char *p, *r, *ll, *ss;
1126 if (!shortpath) {
1127 SetLastError(ERROR_INVALID_PARAMETER);
1128 return 0;
1130 if (!shortpath[0]) {
1131 SetLastError(ERROR_PATH_NOT_FOUND);
1132 return 0;
1135 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1136 lstrcpynA( longpath, full_name.short_name, longlen );
1138 /* Do some hackery to get the long filename. */
1140 if (longpath) {
1141 ss=longpath+strlen(longpath);
1142 ll=full_name.long_name+strlen(full_name.long_name);
1143 p=NULL;
1144 while (ss>=longpath)
1146 /* FIXME: aren't we more paranoid, than needed? */
1147 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1148 p=ss;
1149 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1150 if (ss>=longpath)
1152 /* FIXME: aren't we more paranoid, than needed? */
1153 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1154 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1155 if (ll<full_name.long_name)
1157 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1158 ,ss ,ll );
1159 return 0;
1164 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1165 if (p && p[2])
1167 p+=1;
1168 if ((p-longpath)>0) longlen -= (p-longpath);
1169 lstrcpynA( p, ll , longlen);
1171 /* Now, change all '/' to '\' */
1172 for (r=p; r<(p+longlen); r++ )
1173 if (r[0]=='/') r[0]='\\';
1174 return strlen(longpath) - strlen(p) + longlen;
1178 return strlen(longpath);
1182 /***********************************************************************
1183 * GetLongPathNameW (KERNEL32.@)
1185 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1186 DWORD longlen )
1188 DOS_FULL_NAME full_name;
1189 DWORD ret = 0;
1190 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1192 /* FIXME: is it correct to always return a fully qualified short path? */
1193 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1195 ret = strlen( full_name.short_name );
1196 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1197 longpath, longlen ))
1198 longpath[longlen-1] = 0;
1200 HeapFree( GetProcessHeap(), 0, shortpathA );
1201 return ret;
1205 /***********************************************************************
1206 * DOSFS_DoGetFullPathName
1208 * Implementation of GetFullPathNameA/W.
1210 * bon@elektron 000331:
1211 * A test for GetFullPathName with many pathological cases
1212 * now gives identical output for Wine and OSR2
1214 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1215 BOOL unicode )
1217 DWORD ret;
1218 DOS_FULL_NAME full_name;
1219 char *p,*q;
1220 const char * root;
1221 char drivecur[]="c:.";
1222 char driveletter=0;
1223 int namelen,drive=0;
1225 if ((strlen(name) >1)&& (name[1]==':'))
1226 /* drive letter given */
1228 driveletter = name[0];
1230 if ((strlen(name) >2)&& (name[1]==':') &&
1231 ((name[2]=='\\') || (name[2]=='/')))
1232 /* absolute path given */
1234 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1235 drive = (int)FILE_toupper(name[0]) - 'A';
1237 else
1239 if (driveletter)
1240 drivecur[0]=driveletter;
1241 else
1242 strcpy(drivecur,".");
1243 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1245 FIXME("internal: error getting drive/path\n");
1246 return 0;
1248 /* find path that drive letter substitutes*/
1249 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1250 root= DRIVE_GetRoot(drive);
1251 if (!root)
1253 FIXME("internal: error getting DOS Drive Root\n");
1254 return 0;
1256 if (!strcmp(root,"/"))
1258 /* we have just the last / and we need it. */
1259 p= full_name.long_name;
1261 else
1263 p= full_name.long_name +strlen(root);
1265 /* append long name (= unix name) to drive */
1266 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1267 /* append name to treat */
1268 namelen= strlen(full_name.short_name);
1269 p = (char*)name;
1270 if (driveletter)
1271 p += +2; /* skip drive name when appending */
1272 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1274 FIXME("internal error: buffer too small\n");
1275 return 0;
1277 full_name.short_name[namelen++] ='\\';
1278 full_name.short_name[namelen] = 0;
1279 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1281 /* reverse all slashes */
1282 for (p=full_name.short_name;
1283 p < full_name.short_name+strlen(full_name.short_name);
1284 p++)
1286 if ( *p == '/' )
1287 *p = '\\';
1289 /* Use memmove, as areas overlap */
1290 /* Delete .. */
1291 while ((p = strstr(full_name.short_name,"\\..\\")))
1293 if (p > full_name.short_name+2)
1295 *p = 0;
1296 q = strrchr(full_name.short_name,'\\');
1297 memmove(q+1,p+4,strlen(p+4)+1);
1299 else
1301 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1304 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1306 /* This case istn't treated yet : c:..\test */
1307 memmove(full_name.short_name+2,full_name.short_name+4,
1308 strlen(full_name.short_name+4)+1);
1310 /* Delete . */
1311 while ((p = strstr(full_name.short_name,"\\.\\")))
1313 *(p+1) = 0;
1314 memmove(p+1,p+3,strlen(p+3)+1);
1316 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1317 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1318 namelen=strlen(full_name.short_name);
1319 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1321 /* one more strange case: "c:\test\test1\.."
1322 return "c:\test" */
1323 *(full_name.short_name+namelen-3)=0;
1324 q = strrchr(full_name.short_name,'\\');
1325 *q =0;
1327 if (full_name.short_name[namelen-1]=='.')
1328 full_name.short_name[(namelen--)-1] =0;
1329 if (!driveletter)
1330 if (full_name.short_name[namelen-1]=='\\')
1331 full_name.short_name[(namelen--)-1] =0;
1332 TRACE("got %s\n",full_name.short_name);
1334 /* If the lpBuffer buffer is too small, the return value is the
1335 size of the buffer, in characters, required to hold the path
1336 plus the terminating \0 (tested against win95osr2, bon 001118)
1337 . */
1338 ret = strlen(full_name.short_name);
1339 if (ret >= len )
1341 /* don't touch anything when the buffer is not large enough */
1342 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1343 return ret+1;
1345 if (result)
1347 if (unicode)
1348 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1349 else
1350 lstrcpynA( result, full_name.short_name, len );
1353 TRACE("returning '%s'\n", full_name.short_name );
1354 return ret;
1358 /***********************************************************************
1359 * GetFullPathNameA (KERNEL32.@)
1360 * NOTES
1361 * if the path closed with '\', *lastpart is 0
1363 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1364 LPSTR *lastpart )
1366 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1367 if (ret && (ret<=len) && buffer && lastpart)
1369 LPSTR p = buffer + strlen(buffer);
1371 if (*p != '\\')
1373 while ((p > buffer + 2) && (*p != '\\')) p--;
1374 *lastpart = p + 1;
1376 else *lastpart = NULL;
1378 return ret;
1382 /***********************************************************************
1383 * GetFullPathNameW (KERNEL32.@)
1385 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1386 LPWSTR *lastpart )
1388 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1389 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1390 HeapFree( GetProcessHeap(), 0, nameA );
1391 if (ret && (ret<=len) && buffer && lastpart)
1393 LPWSTR p = buffer + strlenW(buffer);
1394 if (*p != (WCHAR)'\\')
1396 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1397 *lastpart = p + 1;
1399 else *lastpart = NULL;
1401 return ret;
1405 /***********************************************************************
1406 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1408 * Return the full Unix file name for a given path.
1410 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1412 BOOL ret;
1413 DOS_FULL_NAME path;
1414 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1415 return ret;
1419 /***********************************************************************
1420 * DOSFS_FindNextEx
1422 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1424 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1425 UINT flags = DRIVE_GetFlags( info->drive );
1426 char *p, buffer[MAX_PATHNAME_LEN];
1427 const char *drive_path;
1428 int drive_root;
1429 LPCSTR long_name, short_name;
1430 BY_HANDLE_FILE_INFORMATION fileinfo;
1431 char dos_name[13];
1433 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1435 if (info->cur_pos) return 0;
1436 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1437 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1438 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1439 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1440 entry->nFileSizeHigh = 0;
1441 entry->nFileSizeLow = 0;
1442 entry->dwReserved0 = 0;
1443 entry->dwReserved1 = 0;
1444 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1445 strcpy( entry->cAlternateFileName, entry->cFileName );
1446 info->cur_pos++;
1447 TRACE("returning %s (%s) as label\n",
1448 entry->cFileName, entry->cAlternateFileName);
1449 return 1;
1452 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1453 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1454 drive_root = !*drive_path;
1456 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1457 strcat( buffer, "/" );
1458 p = buffer + strlen(buffer);
1460 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1462 info->cur_pos++;
1464 /* Don't return '.' and '..' in the root of the drive */
1465 if (drive_root && (long_name[0] == '.') &&
1466 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1467 continue;
1469 /* Check the long mask */
1471 if (info->long_mask)
1473 if (!DOSFS_MatchLong( info->long_mask, long_name,
1474 flags & DRIVE_CASE_SENSITIVE )) continue;
1477 /* Check the short mask */
1479 if (info->short_mask)
1481 if (!short_name)
1483 DOSFS_Hash( long_name, dos_name, TRUE,
1484 !(flags & DRIVE_CASE_SENSITIVE) );
1485 short_name = dos_name;
1487 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1490 /* Check the file attributes */
1492 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1493 if (!FILE_Stat( buffer, &fileinfo ))
1495 WARN("can't stat %s\n", buffer);
1496 continue;
1498 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1499 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1501 static int show_dir_symlinks = -1;
1502 if (show_dir_symlinks == -1)
1503 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1504 if (!show_dir_symlinks) continue;
1507 if (fileinfo.dwFileAttributes & ~attr) continue;
1509 /* We now have a matching entry; fill the result and return */
1511 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1512 entry->ftCreationTime = fileinfo.ftCreationTime;
1513 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1514 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1515 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1516 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1518 if (short_name)
1519 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1520 else
1521 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1522 !(flags & DRIVE_CASE_SENSITIVE) );
1524 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1525 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1526 TRACE("returning %s (%s) %02lx %ld\n",
1527 entry->cFileName, entry->cAlternateFileName,
1528 entry->dwFileAttributes, entry->nFileSizeLow );
1529 return 1;
1531 return 0; /* End of directory */
1534 /***********************************************************************
1535 * DOSFS_FindNext
1537 * Find the next matching file. Return the number of entries read to find
1538 * the matching one, or 0 if no more entries.
1539 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1540 * file name mask. Either or both can be NULL.
1542 * NOTE: This is supposed to be only called by the int21 emulation
1543 * routines. Thus, we should own the Win16Mutex anyway.
1544 * Nevertheless, we explicitly enter it to ensure the static
1545 * directory cache is protected.
1547 int DOSFS_FindNext( const char *path, const char *short_mask,
1548 const char *long_mask, int drive, BYTE attr,
1549 int skip, WIN32_FIND_DATAA *entry )
1551 static FIND_FIRST_INFO info;
1552 LPCSTR short_name, long_name;
1553 int count;
1555 _EnterWin16Lock();
1557 /* Check the cached directory */
1558 if (!(info.dir && info.path == path && info.short_mask == short_mask
1559 && info.long_mask == long_mask && info.drive == drive
1560 && info.attr == attr && info.cur_pos <= skip))
1562 /* Not in the cache, open it anew */
1563 if (info.dir) DOSFS_CloseDir( info.dir );
1565 info.path = (LPSTR)path;
1566 info.long_mask = (LPSTR)long_mask;
1567 info.short_mask = (LPSTR)short_mask;
1568 info.attr = attr;
1569 info.drive = drive;
1570 info.cur_pos = 0;
1571 info.dir = DOSFS_OpenDir( info.path );
1574 /* Skip to desired position */
1575 while (info.cur_pos < skip)
1576 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1577 info.cur_pos++;
1578 else
1579 break;
1581 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1582 count = info.cur_pos - skip;
1583 else
1584 count = 0;
1586 if (!count)
1588 if (info.dir) DOSFS_CloseDir( info.dir );
1589 memset( &info, '\0', sizeof(info) );
1592 _LeaveWin16Lock();
1594 return count;
1597 /*************************************************************************
1598 * FindFirstFileExA (KERNEL32.@)
1600 HANDLE WINAPI FindFirstFileExA(
1601 LPCSTR lpFileName,
1602 FINDEX_INFO_LEVELS fInfoLevelId,
1603 LPVOID lpFindFileData,
1604 FINDEX_SEARCH_OPS fSearchOp,
1605 LPVOID lpSearchFilter,
1606 DWORD dwAdditionalFlags)
1608 DOS_FULL_NAME full_name;
1609 HGLOBAL handle;
1610 FIND_FIRST_INFO *info;
1612 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1614 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1615 return INVALID_HANDLE_VALUE;
1618 switch(fInfoLevelId)
1620 case FindExInfoStandard:
1622 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1623 data->dwReserved0 = data->dwReserved1 = 0x0;
1624 if (!lpFileName) return 0;
1625 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1626 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1627 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1628 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1629 strcpy( info->path, full_name.long_name );
1630 info->long_mask = strrchr( info->path, '/' );
1631 *(info->long_mask++) = '\0';
1632 info->short_mask = NULL;
1633 info->attr = 0xff;
1634 if (lpFileName[0] && (lpFileName[1] == ':'))
1635 info->drive = FILE_toupper(*lpFileName) - 'A';
1636 else info->drive = DRIVE_GetCurrentDrive();
1637 info->cur_pos = 0;
1639 info->dir = DOSFS_OpenDir( info->path );
1641 GlobalUnlock( handle );
1642 if (!FindNextFileA( handle, data ))
1644 FindClose( handle );
1645 SetLastError( ERROR_NO_MORE_FILES );
1646 break;
1648 return handle;
1650 break;
1651 default:
1652 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1654 return INVALID_HANDLE_VALUE;
1657 /*************************************************************************
1658 * FindFirstFileA (KERNEL32.@)
1660 HANDLE WINAPI FindFirstFileA(
1661 LPCSTR lpFileName,
1662 WIN32_FIND_DATAA *lpFindData )
1664 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1665 FindExSearchNameMatch, NULL, 0);
1668 /*************************************************************************
1669 * FindFirstFileExW (KERNEL32.@)
1671 HANDLE WINAPI FindFirstFileExW(
1672 LPCWSTR lpFileName,
1673 FINDEX_INFO_LEVELS fInfoLevelId,
1674 LPVOID lpFindFileData,
1675 FINDEX_SEARCH_OPS fSearchOp,
1676 LPVOID lpSearchFilter,
1677 DWORD dwAdditionalFlags)
1679 HANDLE handle;
1680 WIN32_FIND_DATAA dataA;
1681 LPVOID _lpFindFileData;
1682 LPSTR pathA;
1684 switch(fInfoLevelId)
1686 case FindExInfoStandard:
1688 _lpFindFileData = &dataA;
1690 break;
1691 default:
1692 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1693 return INVALID_HANDLE_VALUE;
1696 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1697 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1698 HeapFree( GetProcessHeap(), 0, pathA );
1699 if (handle == INVALID_HANDLE_VALUE) return handle;
1701 switch(fInfoLevelId)
1703 case FindExInfoStandard:
1705 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1706 dataW->dwFileAttributes = dataA.dwFileAttributes;
1707 dataW->ftCreationTime = dataA.ftCreationTime;
1708 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1709 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1710 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1711 dataW->nFileSizeLow = dataA.nFileSizeLow;
1712 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1713 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1714 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1715 dataW->cAlternateFileName,
1716 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1718 break;
1719 default:
1720 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1721 return INVALID_HANDLE_VALUE;
1723 return handle;
1726 /*************************************************************************
1727 * FindFirstFileW (KERNEL32.@)
1729 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1731 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1732 FindExSearchNameMatch, NULL, 0);
1735 /*************************************************************************
1736 * FindNextFileA (KERNEL32.@)
1738 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1740 FIND_FIRST_INFO *info;
1742 if ((handle == INVALID_HANDLE_VALUE) ||
1743 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1745 SetLastError( ERROR_INVALID_HANDLE );
1746 return FALSE;
1748 GlobalUnlock( handle );
1749 if (!info->path || !info->dir)
1751 SetLastError( ERROR_NO_MORE_FILES );
1752 return FALSE;
1754 if (!DOSFS_FindNextEx( info, data ))
1756 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1757 HeapFree( GetProcessHeap(), 0, info->path );
1758 info->path = info->long_mask = NULL;
1759 SetLastError( ERROR_NO_MORE_FILES );
1760 return FALSE;
1762 return TRUE;
1766 /*************************************************************************
1767 * FindNextFileW (KERNEL32.@)
1769 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1771 WIN32_FIND_DATAA dataA;
1772 if (!FindNextFileA( handle, &dataA )) return FALSE;
1773 data->dwFileAttributes = dataA.dwFileAttributes;
1774 data->ftCreationTime = dataA.ftCreationTime;
1775 data->ftLastAccessTime = dataA.ftLastAccessTime;
1776 data->ftLastWriteTime = dataA.ftLastWriteTime;
1777 data->nFileSizeHigh = dataA.nFileSizeHigh;
1778 data->nFileSizeLow = dataA.nFileSizeLow;
1779 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1780 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1781 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1782 data->cAlternateFileName,
1783 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1784 return TRUE;
1787 /*************************************************************************
1788 * FindClose (KERNEL32.@)
1790 BOOL WINAPI FindClose( HANDLE handle )
1792 FIND_FIRST_INFO *info;
1794 if ((handle == INVALID_HANDLE_VALUE) ||
1795 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1797 SetLastError( ERROR_INVALID_HANDLE );
1798 return FALSE;
1800 __TRY
1802 if (info->dir) DOSFS_CloseDir( info->dir );
1803 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1805 __EXCEPT(page_fault)
1807 WARN("Illegal handle %x\n", handle);
1808 SetLastError( ERROR_INVALID_HANDLE );
1809 return FALSE;
1811 __ENDTRY
1812 GlobalUnlock( handle );
1813 GlobalFree( handle );
1814 return TRUE;
1817 /***********************************************************************
1818 * DOSFS_UnixTimeToFileTime
1820 * Convert a Unix time to FILETIME format.
1821 * The FILETIME structure is a 64-bit value representing the number of
1822 * 100-nanosecond intervals since January 1, 1601, 0:00.
1823 * 'remainder' is the nonnegative number of 100-ns intervals
1824 * corresponding to the time fraction smaller than 1 second that
1825 * couldn't be stored in the time_t value.
1827 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1828 DWORD remainder )
1830 /* NOTES:
1832 CONSTANTS:
1833 The time difference between 1 January 1601, 00:00:00 and
1834 1 January 1970, 00:00:00 is 369 years, plus the leap years
1835 from 1604 to 1968, excluding 1700, 1800, 1900.
1836 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1837 of 134774 days.
1839 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1841 The time difference is 134774 * 86400 * 10000000, which can be written
1842 116444736000000000
1843 27111902 * 2^32 + 3577643008
1844 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1846 If you find that these constants are buggy, please change them in all
1847 instances in both conversion functions.
1849 VERSIONS:
1850 There are two versions, one of them uses long long variables and
1851 is presumably faster but not ISO C. The other one uses standard C
1852 data types and operations but relies on the assumption that negative
1853 numbers are stored as 2's complement (-1 is 0xffff....). If this
1854 assumption is violated, dates before 1970 will not convert correctly.
1855 This should however work on any reasonable architecture where WINE
1856 will run.
1858 DETAILS:
1860 Take care not to remove the casts. I have tested these functions
1861 (in both versions) for a lot of numbers. I would be interested in
1862 results on other compilers than GCC.
1864 The operations have been designed to account for the possibility
1865 of 64-bit time_t in future UNICES. Even the versions without
1866 internal long long numbers will work if time_t only is 64 bit.
1867 A 32-bit shift, which was necessary for that operation, turned out
1868 not to work correctly in GCC, besides giving the warning. So I
1869 used a double 16-bit shift instead. Numbers are in the ISO version
1870 represented by three limbs, the most significant with 32 bit, the
1871 other two with 16 bit each.
1873 As the modulo-operator % is not well-defined for negative numbers,
1874 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1876 There might be quicker ways to do this in C. Certainly so in
1877 assembler.
1879 Claus Fischer, fischer@iue.tuwien.ac.at
1882 #if SIZEOF_LONG_LONG >= 8
1883 # define USE_LONG_LONG 1
1884 #else
1885 # define USE_LONG_LONG 0
1886 #endif
1888 #if USE_LONG_LONG /* gcc supports long long type */
1890 long long int t = unix_time;
1891 t *= 10000000;
1892 t += 116444736000000000LL;
1893 t += remainder;
1894 filetime->dwLowDateTime = (UINT)t;
1895 filetime->dwHighDateTime = (UINT)(t >> 32);
1897 #else /* ISO version */
1899 UINT a0; /* 16 bit, low bits */
1900 UINT a1; /* 16 bit, medium bits */
1901 UINT a2; /* 32 bit, high bits */
1903 /* Copy the unix time to a2/a1/a0 */
1904 a0 = unix_time & 0xffff;
1905 a1 = (unix_time >> 16) & 0xffff;
1906 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1907 Do not replace this by >> 32, it gives a compiler warning and it does
1908 not work. */
1909 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1910 ~((~unix_time >> 16) >> 16));
1912 /* Multiply a by 10000000 (a = a2/a1/a0)
1913 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1914 a0 *= 10000;
1915 a1 = a1 * 10000 + (a0 >> 16);
1916 a2 = a2 * 10000 + (a1 >> 16);
1917 a0 &= 0xffff;
1918 a1 &= 0xffff;
1920 a0 *= 1000;
1921 a1 = a1 * 1000 + (a0 >> 16);
1922 a2 = a2 * 1000 + (a1 >> 16);
1923 a0 &= 0xffff;
1924 a1 &= 0xffff;
1926 /* Add the time difference and the remainder */
1927 a0 += 32768 + (remainder & 0xffff);
1928 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1929 a2 += 27111902 + (a1 >> 16);
1930 a0 &= 0xffff;
1931 a1 &= 0xffff;
1933 /* Set filetime */
1934 filetime->dwLowDateTime = (a1 << 16) + a0;
1935 filetime->dwHighDateTime = a2;
1936 #endif
1940 /***********************************************************************
1941 * DOSFS_FileTimeToUnixTime
1943 * Convert a FILETIME format to Unix time.
1944 * If not NULL, 'remainder' contains the fractional part of the filetime,
1945 * in the range of [0..9999999] (even if time_t is negative).
1947 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1949 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1950 #if USE_LONG_LONG
1952 long long int t = filetime->dwHighDateTime;
1953 t <<= 32;
1954 t += (UINT)filetime->dwLowDateTime;
1955 t -= 116444736000000000LL;
1956 if (t < 0)
1958 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1959 return -1 - ((-t - 1) / 10000000);
1961 else
1963 if (remainder) *remainder = t % 10000000;
1964 return t / 10000000;
1967 #else /* ISO version */
1969 UINT a0; /* 16 bit, low bits */
1970 UINT a1; /* 16 bit, medium bits */
1971 UINT a2; /* 32 bit, high bits */
1972 UINT r; /* remainder of division */
1973 unsigned int carry; /* carry bit for subtraction */
1974 int negative; /* whether a represents a negative value */
1976 /* Copy the time values to a2/a1/a0 */
1977 a2 = (UINT)filetime->dwHighDateTime;
1978 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1979 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1981 /* Subtract the time difference */
1982 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1983 else a0 += (1 << 16) - 32768 , carry = 1;
1985 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1986 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1988 a2 -= 27111902 + carry;
1990 /* If a is negative, replace a by (-1-a) */
1991 negative = (a2 >= ((UINT)1) << 31);
1992 if (negative)
1994 /* Set a to -a - 1 (a is a2/a1/a0) */
1995 a0 = 0xffff - a0;
1996 a1 = 0xffff - a1;
1997 a2 = ~a2;
2000 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2001 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2002 a1 += (a2 % 10000) << 16;
2003 a2 /= 10000;
2004 a0 += (a1 % 10000) << 16;
2005 a1 /= 10000;
2006 r = a0 % 10000;
2007 a0 /= 10000;
2009 a1 += (a2 % 1000) << 16;
2010 a2 /= 1000;
2011 a0 += (a1 % 1000) << 16;
2012 a1 /= 1000;
2013 r += (a0 % 1000) * 10000;
2014 a0 /= 1000;
2016 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2017 if (negative)
2019 /* Set a to -a - 1 (a is a2/a1/a0) */
2020 a0 = 0xffff - a0;
2021 a1 = 0xffff - a1;
2022 a2 = ~a2;
2024 r = 9999999 - r;
2027 if (remainder) *remainder = r;
2029 /* Do not replace this by << 32, it gives a compiler warning and it does
2030 not work. */
2031 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2032 #endif
2036 /***********************************************************************
2037 * MulDiv (KERNEL32.@)
2038 * RETURNS
2039 * Result of multiplication and division
2040 * -1: Overflow occurred or Divisor was 0
2042 INT WINAPI MulDiv(
2043 INT nMultiplicand,
2044 INT nMultiplier,
2045 INT nDivisor)
2047 #if SIZEOF_LONG_LONG >= 8
2048 long long ret;
2050 if (!nDivisor) return -1;
2052 /* We want to deal with a positive divisor to simplify the logic. */
2053 if (nDivisor < 0)
2055 nMultiplicand = - nMultiplicand;
2056 nDivisor = -nDivisor;
2059 /* If the result is positive, we "add" to round. else, we subtract to round. */
2060 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2061 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2062 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2063 else
2064 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2066 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2067 return ret;
2068 #else
2069 if (!nDivisor) return -1;
2071 /* We want to deal with a positive divisor to simplify the logic. */
2072 if (nDivisor < 0)
2074 nMultiplicand = - nMultiplicand;
2075 nDivisor = -nDivisor;
2078 /* If the result is positive, we "add" to round. else, we subtract to round. */
2079 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2080 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2081 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2083 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2085 #endif
2089 /***********************************************************************
2090 * DosDateTimeToFileTime (KERNEL32.@)
2092 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2094 struct tm newtm;
2096 newtm.tm_sec = (fattime & 0x1f) * 2;
2097 newtm.tm_min = (fattime >> 5) & 0x3f;
2098 newtm.tm_hour = (fattime >> 11);
2099 newtm.tm_mday = (fatdate & 0x1f);
2100 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2101 newtm.tm_year = (fatdate >> 9) + 80;
2102 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2103 return TRUE;
2107 /***********************************************************************
2108 * FileTimeToDosDateTime (KERNEL32.@)
2110 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2111 LPWORD fattime )
2113 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2114 struct tm *tm = localtime( &unixtime );
2115 if (fattime)
2116 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2117 if (fatdate)
2118 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2119 + tm->tm_mday;
2120 return TRUE;
2124 /***********************************************************************
2125 * LocalFileTimeToFileTime (KERNEL32.@)
2127 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2128 LPFILETIME utcft )
2130 struct tm *xtm;
2131 DWORD remainder;
2133 /* convert from local to UTC. Perhaps not correct. FIXME */
2134 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2135 xtm = gmtime( &unixtime );
2136 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2137 return TRUE;
2141 /***********************************************************************
2142 * FileTimeToLocalFileTime (KERNEL32.@)
2144 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2145 LPFILETIME localft )
2147 DWORD remainder;
2148 /* convert from UTC to local. Perhaps not correct. FIXME */
2149 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2150 #ifdef HAVE_TIMEGM
2151 struct tm *xtm = localtime( &unixtime );
2152 time_t localtime;
2154 localtime = timegm(xtm);
2155 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2157 #else
2158 struct tm *xtm,*gtm;
2159 time_t time1,time2;
2161 xtm = localtime( &unixtime );
2162 gtm = gmtime( &unixtime );
2163 time1 = mktime(xtm);
2164 time2 = mktime(gtm);
2165 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2166 #endif
2167 return TRUE;
2171 /***********************************************************************
2172 * FileTimeToSystemTime (KERNEL32.@)
2174 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2176 struct tm *xtm;
2177 DWORD remainder;
2178 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2179 xtm = gmtime(&xtime);
2180 syst->wYear = xtm->tm_year+1900;
2181 syst->wMonth = xtm->tm_mon + 1;
2182 syst->wDayOfWeek = xtm->tm_wday;
2183 syst->wDay = xtm->tm_mday;
2184 syst->wHour = xtm->tm_hour;
2185 syst->wMinute = xtm->tm_min;
2186 syst->wSecond = xtm->tm_sec;
2187 syst->wMilliseconds = remainder / 10000;
2188 return TRUE;
2191 /***********************************************************************
2192 * QueryDosDeviceA (KERNEL32.@)
2194 * returns array of strings terminated by \0, terminated by \0
2196 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2198 LPSTR s;
2199 char buffer[200];
2201 TRACE("(%s,...)\n", devname ? devname : "<null>");
2202 if (!devname) {
2203 /* return known MSDOS devices */
2204 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2205 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2206 return min(bufsize,sizeof(devices));
2208 /* In theory all that are possible and have been defined.
2209 * Now just those below, since mirc uses it to check for special files.
2211 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2212 * but currently we just ignore that.)
2214 #define CHECK(x) (strstr(devname,#x)==devname)
2215 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2216 strcpy(buffer,"\\DEV\\");
2217 strcat(buffer,devname);
2218 if ((s=strchr(buffer,':'))) *s='\0';
2219 lstrcpynA(target,buffer,bufsize);
2220 return strlen(buffer)+1;
2221 } else {
2222 if (strchr(devname,':') || devname[0]=='\\') {
2223 /* This might be a DOS device we do not handle yet ... */
2224 FIXME("(%s) not detected as DOS device!\n",devname);
2226 SetLastError(ERROR_DEV_NOT_EXIST);
2227 return 0;
2233 /***********************************************************************
2234 * QueryDosDeviceW (KERNEL32.@)
2236 * returns array of strings terminated by \0, terminated by \0
2238 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2240 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2241 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2242 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2244 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2245 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2246 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2247 return ret;
2251 /***********************************************************************
2252 * SystemTimeToFileTime (KERNEL32.@)
2254 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2256 #ifdef HAVE_TIMEGM
2257 struct tm xtm;
2258 time_t utctime;
2259 #else
2260 struct tm xtm,*local_tm,*utc_tm;
2261 time_t localtim,utctime;
2262 #endif
2264 xtm.tm_year = syst->wYear-1900;
2265 xtm.tm_mon = syst->wMonth - 1;
2266 xtm.tm_wday = syst->wDayOfWeek;
2267 xtm.tm_mday = syst->wDay;
2268 xtm.tm_hour = syst->wHour;
2269 xtm.tm_min = syst->wMinute;
2270 xtm.tm_sec = syst->wSecond; /* this is UTC */
2271 xtm.tm_isdst = -1;
2272 #ifdef HAVE_TIMEGM
2273 utctime = timegm(&xtm);
2274 DOSFS_UnixTimeToFileTime( utctime, ft,
2275 syst->wMilliseconds * 10000 );
2276 #else
2277 localtim = mktime(&xtm); /* now we've got local time */
2278 local_tm = localtime(&localtim);
2279 utc_tm = gmtime(&localtim);
2280 utctime = mktime(utc_tm);
2281 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2282 syst->wMilliseconds * 10000 );
2283 #endif
2284 return TRUE;
2287 /***********************************************************************
2288 * DefineDosDeviceA (KERNEL32.@)
2290 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2291 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2292 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2293 return FALSE;
2297 --- 16 bit functions ---
2300 /*************************************************************************
2301 * FindFirstFile (KERNEL.413)
2303 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2305 DOS_FULL_NAME full_name;
2306 HGLOBAL16 handle;
2307 FIND_FIRST_INFO *info;
2309 data->dwReserved0 = data->dwReserved1 = 0x0;
2310 if (!path) return 0;
2311 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2312 return INVALID_HANDLE_VALUE16;
2313 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2314 return INVALID_HANDLE_VALUE16;
2315 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2316 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2317 strcpy( info->path, full_name.long_name );
2318 info->long_mask = strrchr( info->path, '/' );
2319 if (info->long_mask )
2320 *(info->long_mask++) = '\0';
2321 info->short_mask = NULL;
2322 info->attr = 0xff;
2323 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2324 else info->drive = DRIVE_GetCurrentDrive();
2325 info->cur_pos = 0;
2327 info->dir = DOSFS_OpenDir( info->path );
2329 GlobalUnlock16( handle );
2330 if (!FindNextFile16( handle, data ))
2332 FindClose16( handle );
2333 SetLastError( ERROR_NO_MORE_FILES );
2334 return INVALID_HANDLE_VALUE16;
2336 return handle;
2339 /*************************************************************************
2340 * FindNextFile (KERNEL.414)
2342 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2344 FIND_FIRST_INFO *info;
2346 if ((handle == INVALID_HANDLE_VALUE16) ||
2347 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2349 SetLastError( ERROR_INVALID_HANDLE );
2350 return FALSE;
2352 GlobalUnlock16( handle );
2353 if (!info->path || !info->dir)
2355 SetLastError( ERROR_NO_MORE_FILES );
2356 return FALSE;
2358 if (!DOSFS_FindNextEx( info, data ))
2360 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2361 HeapFree( GetProcessHeap(), 0, info->path );
2362 info->path = info->long_mask = NULL;
2363 SetLastError( ERROR_NO_MORE_FILES );
2364 return FALSE;
2366 return TRUE;
2369 /*************************************************************************
2370 * FindClose (KERNEL.415)
2372 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2374 FIND_FIRST_INFO *info;
2376 if ((handle == INVALID_HANDLE_VALUE16) ||
2377 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2379 SetLastError( ERROR_INVALID_HANDLE );
2380 return FALSE;
2382 if (info->dir) DOSFS_CloseDir( info->dir );
2383 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2384 GlobalUnlock16( handle );
2385 GlobalFree16( handle );
2386 return TRUE;