Fixed IO when not run with wineconsole.
[wine.git] / files / dos_fs.c
blob497cc1bc19ece3c27e84a34ac0103a3afc0de0e4
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"
39 #include "debugtools.h"
41 DEFAULT_DEBUG_CHANNEL(dosfs);
42 DECLARE_DEBUG_CHANNEL(file);
44 /* Define the VFAT ioctl to get both short and long file names */
45 /* FIXME: is it possible to get this to work on other systems? */
46 #ifdef linux
47 /* We want the real kernel dirent structure, not the libc one */
48 typedef struct
50 long d_ino;
51 long d_off;
52 unsigned short d_reclen;
53 char d_name[256];
54 } KERNEL_DIRENT;
56 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
58 #else /* linux */
59 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
60 #endif /* linux */
62 /* Chars we don't want to see in DOS file names */
63 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
65 static const DOS_DEVICE DOSFS_Devices[] =
66 /* name, device flags (see Int 21/AX=0x4400) */
68 { "CON", 0xc0d3 },
69 { "PRN", 0xa0c0 },
70 { "NUL", 0x80c4 },
71 { "AUX", 0x80c0 },
72 { "LPT1", 0xa0c0 },
73 { "LPT2", 0xa0c0 },
74 { "LPT3", 0xa0c0 },
75 { "LPT4", 0xc0d3 },
76 { "COM1", 0x80c0 },
77 { "COM2", 0x80c0 },
78 { "COM3", 0x80c0 },
79 { "COM4", 0x80c0 },
80 { "SCSIMGR$", 0xc0c0 },
81 { "HPSCAN", 0xc0c0 }
84 #define GET_DRIVE(path) \
85 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
87 /* Directory info for DOSFS_ReadDir */
88 typedef struct
90 DIR *dir;
91 #ifdef VFAT_IOCTL_READDIR_BOTH
92 int fd;
93 char short_name[12];
94 KERNEL_DIRENT dirent[2];
95 #endif
96 } DOS_DIR;
98 /* Info structure for FindFirstFile handle */
99 typedef struct
101 LPSTR path;
102 LPSTR long_mask;
103 LPSTR short_mask;
104 BYTE attr;
105 int drive;
106 int cur_pos;
107 DOS_DIR *dir;
108 } FIND_FIRST_INFO;
111 static WINE_EXCEPTION_FILTER(page_fault)
113 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
114 return EXCEPTION_EXECUTE_HANDLER;
115 return EXCEPTION_CONTINUE_SEARCH;
119 /***********************************************************************
120 * DOSFS_ValidDOSName
122 * Return 1 if Unix file 'name' is also a valid MS-DOS name
123 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
124 * File name can be terminated by '\0', '\\' or '/'.
126 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
128 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
129 const char *p = name;
130 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
131 int len = 0;
133 if (*p == '.')
135 /* Check for "." and ".." */
136 p++;
137 if (*p == '.') p++;
138 /* All other names beginning with '.' are invalid */
139 return (IS_END_OF_NAME(*p));
141 while (!IS_END_OF_NAME(*p))
143 if (strchr( invalid, *p )) return 0; /* Invalid char */
144 if (*p == '.') break; /* Start of the extension */
145 if (++len > 8) return 0; /* Name too long */
146 p++;
148 if (*p != '.') return 1; /* End of name */
149 p++;
150 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
151 len = 0;
152 while (!IS_END_OF_NAME(*p))
154 if (strchr( invalid, *p )) return 0; /* Invalid char */
155 if (*p == '.') return 0; /* Second extension not allowed */
156 if (++len > 3) return 0; /* Extension too long */
157 p++;
159 return 1;
163 /***********************************************************************
164 * DOSFS_ToDosFCBFormat
166 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
167 * expanding wild cards and converting to upper-case in the process.
168 * File name can be terminated by '\0', '\\' or '/'.
169 * Return FALSE if the name is not a valid DOS name.
170 * 'buffer' must be at least 12 characters long.
172 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
174 static const char invalid_chars[] = INVALID_DOS_CHARS;
175 const char *p = name;
176 int i;
178 /* Check for "." and ".." */
179 if (*p == '.')
181 p++;
182 strcpy( buffer, ". " );
183 if (*p == '.')
185 buffer[1] = '.';
186 p++;
188 return (!*p || (*p == '/') || (*p == '\\'));
191 for (i = 0; i < 8; i++)
193 switch(*p)
195 case '\0':
196 case '\\':
197 case '/':
198 case '.':
199 buffer[i] = ' ';
200 break;
201 case '?':
202 p++;
203 /* fall through */
204 case '*':
205 buffer[i] = '?';
206 break;
207 default:
208 if (strchr( invalid_chars, *p )) return FALSE;
209 buffer[i] = FILE_toupper(*p);
210 p++;
211 break;
215 if (*p == '*')
217 /* Skip all chars after wildcard up to first dot */
218 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
220 else
222 /* Check if name too long */
223 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
225 if (*p == '.') p++; /* Skip dot */
227 for (i = 8; i < 11; i++)
229 switch(*p)
231 case '\0':
232 case '\\':
233 case '/':
234 buffer[i] = ' ';
235 break;
236 case '.':
237 return FALSE; /* Second extension not allowed */
238 case '?':
239 p++;
240 /* fall through */
241 case '*':
242 buffer[i] = '?';
243 break;
244 default:
245 if (strchr( invalid_chars, *p )) return FALSE;
246 buffer[i] = FILE_toupper(*p);
247 p++;
248 break;
251 buffer[11] = '\0';
253 /* at most 3 character of the extension are processed
254 * is something behind this ?
256 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
257 return IS_END_OF_NAME(*p);
261 /***********************************************************************
262 * DOSFS_ToDosDTAFormat
264 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
265 * converting to upper-case in the process.
266 * File name can be terminated by '\0', '\\' or '/'.
267 * 'buffer' must be at least 13 characters long.
269 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
271 char *p;
273 memcpy( buffer, name, 8 );
274 p = buffer + 8;
275 while ((p > buffer) && (p[-1] == ' ')) p--;
276 *p++ = '.';
277 memcpy( p, name + 8, 3 );
278 p += 3;
279 while (p[-1] == ' ') p--;
280 if (p[-1] == '.') p--;
281 *p = '\0';
285 /***********************************************************************
286 * DOSFS_MatchShort
288 * Check a DOS file name against a mask (both in FCB format).
290 static int DOSFS_MatchShort( const char *mask, const char *name )
292 int i;
293 for (i = 11; i > 0; i--, mask++, name++)
294 if ((*mask != '?') && (*mask != *name)) return 0;
295 return 1;
299 /***********************************************************************
300 * DOSFS_MatchLong
302 * Check a long file name against a mask.
304 * Tests (done in W95 DOS shell - case insensitive):
305 * *.txt test1.test.txt *
306 * *st1* test1.txt *
307 * *.t??????.t* test1.ta.tornado.txt *
308 * *tornado* test1.ta.tornado.txt *
309 * t*t test1.ta.tornado.txt *
310 * ?est* test1.txt *
311 * ?est??? test1.txt -
312 * *test1.txt* test1.txt *
313 * h?l?o*t.dat hellothisisatest.dat *
315 static int DOSFS_MatchLong( const char *mask, const char *name,
316 int case_sensitive )
318 const char *lastjoker = NULL;
319 const char *next_to_retry = NULL;
321 if (!strcmp( mask, "*.*" )) return 1;
322 while (*name && *mask)
324 if (*mask == '*')
326 mask++;
327 while (*mask == '*') mask++; /* Skip consecutive '*' */
328 lastjoker = mask;
329 if (!*mask) return 1; /* end of mask is all '*', so match */
331 /* skip to the next match after the joker(s) */
332 if (case_sensitive) while (*name && (*name != *mask)) name++;
333 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
335 if (!*name) break;
336 next_to_retry = name;
338 else if (*mask != '?')
340 int mismatch = 0;
341 if (case_sensitive)
343 if (*mask != *name) mismatch = 1;
345 else
347 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
349 if (!mismatch)
351 mask++;
352 name++;
353 if (*mask == '\0')
355 if (*name == '\0')
356 return 1;
357 if (lastjoker)
358 mask = lastjoker;
361 else /* mismatch ! */
363 if (lastjoker) /* we had an '*', so we can try unlimitedly */
365 mask = lastjoker;
367 /* this scan sequence was a mismatch, so restart
368 * 1 char after the first char we checked last time */
369 next_to_retry++;
370 name = next_to_retry;
372 else
373 return 0; /* bad luck */
376 else /* '?' */
378 mask++;
379 name++;
382 while ((*mask == '.') || (*mask == '*'))
383 mask++; /* Ignore trailing '.' or '*' in mask */
384 return (!*name && !*mask);
388 /***********************************************************************
389 * DOSFS_OpenDir
391 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
393 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
394 if (!dir)
396 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
397 return NULL;
400 /* Treat empty path as root directory. This simplifies path split into
401 directory and mask in several other places */
402 if (!*path) path = "/";
404 #ifdef VFAT_IOCTL_READDIR_BOTH
406 /* Check if the VFAT ioctl is supported on this directory */
408 if ((dir->fd = open( path, O_RDONLY )) != -1)
410 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
412 close( dir->fd );
413 dir->fd = -1;
415 else
417 /* Set the file pointer back at the start of the directory */
418 lseek( dir->fd, 0, SEEK_SET );
419 dir->dir = NULL;
420 return dir;
423 #endif /* VFAT_IOCTL_READDIR_BOTH */
425 /* Now use the standard opendir/readdir interface */
427 if (!(dir->dir = opendir( path )))
429 HeapFree( GetProcessHeap(), 0, dir );
430 return NULL;
432 return dir;
436 /***********************************************************************
437 * DOSFS_CloseDir
439 static void DOSFS_CloseDir( DOS_DIR *dir )
441 #ifdef VFAT_IOCTL_READDIR_BOTH
442 if (dir->fd != -1) close( dir->fd );
443 #endif /* VFAT_IOCTL_READDIR_BOTH */
444 if (dir->dir) closedir( dir->dir );
445 HeapFree( GetProcessHeap(), 0, dir );
449 /***********************************************************************
450 * DOSFS_ReadDir
452 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
453 LPCSTR *short_name )
455 struct dirent *dirent;
457 #ifdef VFAT_IOCTL_READDIR_BOTH
458 if (dir->fd != -1)
460 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
461 if (!dir->dirent[0].d_reclen) return FALSE;
462 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
463 dir->short_name[0] = '\0';
464 *short_name = dir->short_name;
465 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
466 else *long_name = dir->dirent[0].d_name;
467 return TRUE;
470 #endif /* VFAT_IOCTL_READDIR_BOTH */
472 if (!(dirent = readdir( dir->dir ))) return FALSE;
473 *long_name = dirent->d_name;
474 *short_name = NULL;
475 return TRUE;
479 /***********************************************************************
480 * DOSFS_Hash
482 * Transform a Unix file name into a hashed DOS name. If the name is a valid
483 * DOS name, it is converted to upper-case; otherwise it is replaced by a
484 * hashed version that fits in 8.3 format.
485 * File name can be terminated by '\0', '\\' or '/'.
486 * 'buffer' must be at least 13 characters long.
488 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
489 BOOL ignore_case )
491 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
492 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
494 const char *p, *ext;
495 char *dst;
496 unsigned short hash;
497 int i;
499 if (dir_format) strcpy( buffer, " " );
501 if (DOSFS_ValidDOSName( name, ignore_case ))
503 /* Check for '.' and '..' */
504 if (*name == '.')
506 buffer[0] = '.';
507 if (!dir_format) buffer[1] = buffer[2] = '\0';
508 if (name[1] == '.') buffer[1] = '.';
509 return;
512 /* Simply copy the name, converting to uppercase */
514 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
515 *dst++ = FILE_toupper(*name);
516 if (*name == '.')
518 if (dir_format) dst = buffer + 8;
519 else *dst++ = '.';
520 for (name++; !IS_END_OF_NAME(*name); name++)
521 *dst++ = FILE_toupper(*name);
523 if (!dir_format) *dst = '\0';
524 return;
527 /* Compute the hash code of the file name */
528 /* If you know something about hash functions, feel free to */
529 /* insert a better algorithm here... */
530 if (ignore_case)
532 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
533 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
534 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
536 else
538 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
539 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
540 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
543 /* Find last dot for start of the extension */
544 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
545 if (*p == '.') ext = p;
546 if (ext && IS_END_OF_NAME(ext[1]))
547 ext = NULL; /* Empty extension ignored */
549 /* Copy first 4 chars, replacing invalid chars with '_' */
550 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
552 if (IS_END_OF_NAME(*p) || (p == ext)) break;
553 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
555 /* Pad to 5 chars with '~' */
556 while (i-- >= 0) *dst++ = '~';
558 /* Insert hash code converted to 3 ASCII chars */
559 *dst++ = hash_chars[(hash >> 10) & 0x1f];
560 *dst++ = hash_chars[(hash >> 5) & 0x1f];
561 *dst++ = hash_chars[hash & 0x1f];
563 /* Copy the first 3 chars of the extension (if any) */
564 if (ext)
566 if (!dir_format) *dst++ = '.';
567 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
568 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
570 if (!dir_format) *dst = '\0';
574 /***********************************************************************
575 * DOSFS_FindUnixName
577 * Find the Unix file name in a given directory that corresponds to
578 * a file name (either in Unix or DOS format).
579 * File name can be terminated by '\0', '\\' or '/'.
580 * Return TRUE if OK, FALSE if no file name matches.
582 * 'long_buf' must be at least 'long_len' characters long. If the long name
583 * turns out to be larger than that, the function returns FALSE.
584 * 'short_buf' must be at least 13 characters long.
586 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
587 INT long_len, LPSTR short_buf, BOOL ignore_case)
589 DOS_DIR *dir;
590 LPCSTR long_name, short_name;
591 char dos_name[12], tmp_buf[13];
592 BOOL ret;
594 const char *p = strchr( name, '/' );
595 int len = p ? (int)(p - name) : strlen(name);
596 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
597 /* Ignore trailing dots and spaces */
598 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
599 if (long_len < len + 1) return FALSE;
601 TRACE("%s,%s\n", path, name );
603 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
605 if (!(dir = DOSFS_OpenDir( path )))
607 WARN("(%s,%s): can't open dir: %s\n",
608 path, name, strerror(errno) );
609 return FALSE;
612 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
614 /* Check against Unix name */
615 if (len == strlen(long_name))
617 if (!ignore_case)
619 if (!strncmp( long_name, name, len )) break;
621 else
623 if (!FILE_strncasecmp( long_name, name, len )) break;
626 if (dos_name[0])
628 /* Check against hashed DOS name */
629 if (!short_name)
631 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
632 short_name = tmp_buf;
634 if (!strcmp( dos_name, short_name )) break;
637 if (ret)
639 if (long_buf) strcpy( long_buf, long_name );
640 if (short_buf)
642 if (short_name)
643 DOSFS_ToDosDTAFormat( short_name, short_buf );
644 else
645 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
647 TRACE("(%s,%s) -> %s (%s)\n",
648 path, name, long_name, short_buf ? short_buf : "***");
650 else
651 WARN("'%s' not found in '%s'\n", name, path);
652 DOSFS_CloseDir( dir );
653 return ret;
657 /***********************************************************************
658 * DOSFS_GetDevice
660 * Check if a DOS file name represents a DOS device and return the device.
662 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
664 int i;
665 const char *p;
667 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
668 if (name[0] && (name[1] == ':')) name += 2;
669 if ((p = strrchr( name, '/' ))) name = p + 1;
670 if ((p = strrchr( name, '\\' ))) name = p + 1;
671 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
673 const char *dev = DOSFS_Devices[i].name;
674 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
676 p = name + strlen( dev );
677 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
680 return NULL;
684 /***********************************************************************
685 * DOSFS_GetDeviceByHandle
687 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
689 const DOS_DEVICE *ret = NULL;
690 SERVER_START_REQ( get_file_info )
692 req->handle = hFile;
693 if (!SERVER_CALL() && (req->type == FILE_TYPE_UNKNOWN))
695 if ((req->attr >= 0) &&
696 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
697 ret = &DOSFS_Devices[req->attr];
700 SERVER_END_REQ;
701 return ret;
705 /**************************************************************************
706 * DOSFS_CreateCommPort
708 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
710 HANDLE ret;
711 char devname[40];
712 size_t len;
714 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
716 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
717 if(!devname[0])
718 return 0;
720 TRACE("opening %s as %s\n", devname, name);
722 len = strlen(devname);
723 SERVER_START_VAR_REQ( create_serial, len )
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 memcpy( server_data_ptr(req), devname, len );
730 SetLastError(0);
731 SERVER_CALL_ERR();
732 ret = req->handle;
734 SERVER_END_VAR_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"))
793 return FILE_CreateDevice( i, access, sa );
796 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
797 return handle;
798 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
799 return 0;
803 return 0;
807 /***********************************************************************
808 * DOSFS_GetPathDrive
810 * Get the drive specified by a given path name (DOS or Unix format).
812 static int DOSFS_GetPathDrive( const char **name )
814 int drive;
815 const char *p = *name;
817 if (*p && (p[1] == ':'))
819 drive = FILE_toupper(*p) - 'A';
820 *name += 2;
822 else if (*p == '/') /* Absolute Unix path? */
824 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
826 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
827 /* Assume it really was a DOS name */
828 drive = DRIVE_GetCurrentDrive();
831 else drive = DRIVE_GetCurrentDrive();
833 if (!DRIVE_IsValid(drive))
835 SetLastError( ERROR_INVALID_DRIVE );
836 return -1;
838 return drive;
842 /***********************************************************************
843 * DOSFS_GetFullName
845 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
846 * Unix name / short DOS name pair.
847 * Return FALSE if one of the path components does not exist. The last path
848 * component is only checked if 'check_last' is non-zero.
849 * The buffers pointed to by 'long_buf' and 'short_buf' must be
850 * at least MAX_PATHNAME_LEN long.
852 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
854 BOOL found;
855 UINT flags;
856 char *p_l, *p_s, *root;
858 TRACE("%s (last=%d)\n", name, check_last );
860 if ((!*name) || (*name=='\n'))
861 { /* error code for Win98 */
862 SetLastError(ERROR_BAD_PATHNAME);
863 return FALSE;
866 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
867 flags = DRIVE_GetFlags( full->drive );
869 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
870 sizeof(full->long_name) );
871 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
872 else root = full->long_name; /* root directory */
874 strcpy( full->short_name, "A:\\" );
875 full->short_name[0] += full->drive;
877 if ((*name == '\\') || (*name == '/')) /* Absolute path */
879 while ((*name == '\\') || (*name == '/')) name++;
881 else /* Relative path */
883 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
884 sizeof(full->long_name) - (root - full->long_name) - 1 );
885 if (root[1]) *root = '/';
886 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
887 sizeof(full->short_name) - 3 );
890 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
891 : full->long_name;
892 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
893 : full->short_name + 2;
894 found = TRUE;
896 while (*name && found)
898 /* Check for '.' and '..' */
900 if (*name == '.')
902 if (IS_END_OF_NAME(name[1]))
904 name++;
905 while ((*name == '\\') || (*name == '/')) name++;
906 continue;
908 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
910 name += 2;
911 while ((*name == '\\') || (*name == '/')) name++;
912 while ((p_l > root) && (*p_l != '/')) p_l--;
913 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
914 *p_l = *p_s = '\0'; /* Remove trailing separator */
915 continue;
919 /* Make sure buffers are large enough */
921 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
922 (p_l >= full->long_name + sizeof(full->long_name) - 1))
924 SetLastError( ERROR_PATH_NOT_FOUND );
925 return FALSE;
928 /* Get the long and short name matching the file name */
930 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
931 sizeof(full->long_name) - (p_l - full->long_name) - 1,
932 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
934 *p_l++ = '/';
935 p_l += strlen(p_l);
936 *p_s++ = '\\';
937 p_s += strlen(p_s);
938 while (!IS_END_OF_NAME(*name)) name++;
940 else if (!check_last)
942 *p_l++ = '/';
943 *p_s++ = '\\';
944 while (!IS_END_OF_NAME(*name) &&
945 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
946 (p_l < full->long_name + sizeof(full->long_name) - 1))
948 *p_s++ = FILE_tolower(*name);
949 /* If the drive is case-sensitive we want to create new */
950 /* files in lower-case otherwise we can't reopen them */
951 /* under the same short name. */
952 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
953 else *p_l++ = *name;
954 name++;
956 /* Ignore trailing dots and spaces */
957 while(p_l[-1] == '.' || p_l[-1] == ' ') {
958 --p_l;
959 --p_s;
961 *p_l = *p_s = '\0';
963 while ((*name == '\\') || (*name == '/')) name++;
966 if (!found)
968 if (check_last)
970 SetLastError( ERROR_FILE_NOT_FOUND );
971 return FALSE;
973 if (*name) /* Not last */
975 SetLastError( ERROR_PATH_NOT_FOUND );
976 return FALSE;
979 if (!full->long_name[0]) strcpy( full->long_name, "/" );
980 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
981 TRACE("returning %s = %s\n", full->long_name, full->short_name );
982 return TRUE;
986 /***********************************************************************
987 * GetShortPathNameA (KERNEL32.@)
989 * NOTES
990 * observed:
991 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
992 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
994 * more observations ( with NT 3.51 (WinDD) ):
995 * longpath <= 8.3 -> just copy longpath to shortpath
996 * longpath > 8.3 ->
997 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
998 * b) file does exist -> set the short filename.
999 * - trailing slashes are reproduced in the short name, even if the
1000 * file is not a directory
1001 * - the absolute/relative path of the short name is reproduced like found
1002 * in the long name
1003 * - longpath and shortpath may have the same address
1004 * Peter Ganten, 1999
1006 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1007 DWORD shortlen )
1009 DOS_FULL_NAME full_name;
1010 LPSTR tmpshortpath;
1011 DWORD sp = 0, lp = 0;
1012 int tmplen, drive;
1013 UINT flags;
1015 TRACE("%s\n", debugstr_a(longpath));
1017 if (!longpath) {
1018 SetLastError(ERROR_INVALID_PARAMETER);
1019 return 0;
1021 if (!longpath[0]) {
1022 SetLastError(ERROR_BAD_PATHNAME);
1023 return 0;
1026 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1027 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1028 return 0;
1031 /* check for drive letter */
1032 if ( longpath[1] == ':' ) {
1033 tmpshortpath[0] = longpath[0];
1034 tmpshortpath[1] = ':';
1035 sp = 2;
1038 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1039 flags = DRIVE_GetFlags ( drive );
1041 while ( longpath[lp] ) {
1043 /* check for path delimiters and reproduce them */
1044 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1045 if (!sp || tmpshortpath[sp-1]!= '\\')
1047 /* strip double "\\" */
1048 tmpshortpath[sp] = '\\';
1049 sp++;
1051 tmpshortpath[sp]=0;/*terminate string*/
1052 lp++;
1053 continue;
1056 tmplen = strcspn ( longpath + lp, "\\/" );
1057 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1059 /* Check, if the current element is a valid dos name */
1060 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1061 sp += tmplen;
1062 lp += tmplen;
1063 continue;
1066 /* Check if the file exists and use the existing file name */
1067 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1068 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1069 sp += strlen ( tmpshortpath+sp );
1070 lp += tmplen;
1071 continue;
1074 TRACE("not found!\n" );
1075 SetLastError ( ERROR_FILE_NOT_FOUND );
1076 return 0;
1078 tmpshortpath[sp] = 0;
1080 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1081 TRACE("returning %s\n", debugstr_a(shortpath) );
1082 tmplen = strlen ( tmpshortpath );
1083 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1085 return tmplen;
1089 /***********************************************************************
1090 * GetShortPathNameW (KERNEL32.@)
1092 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1093 DWORD shortlen )
1095 LPSTR longpathA, shortpathA;
1096 DWORD ret = 0;
1098 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1099 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1101 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1102 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1103 shortpath[shortlen-1] = 0;
1104 HeapFree( GetProcessHeap(), 0, longpathA );
1105 HeapFree( GetProcessHeap(), 0, shortpathA );
1107 return ret;
1111 /***********************************************************************
1112 * GetLongPathNameA (KERNEL32.@)
1114 * NOTES
1115 * observed (Win2000):
1116 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1117 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1119 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1120 DWORD longlen )
1122 DOS_FULL_NAME full_name;
1123 char *p, *r, *ll, *ss;
1125 if (!shortpath) {
1126 SetLastError(ERROR_INVALID_PARAMETER);
1127 return 0;
1129 if (!shortpath[0]) {
1130 SetLastError(ERROR_PATH_NOT_FOUND);
1131 return 0;
1134 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1135 lstrcpynA( longpath, full_name.short_name, longlen );
1137 /* Do some hackery to get the long filename. */
1139 if (longpath) {
1140 ss=longpath+strlen(longpath);
1141 ll=full_name.long_name+strlen(full_name.long_name);
1142 p=NULL;
1143 while (ss>=longpath)
1145 /* FIXME: aren't we more paranoid, than needed? */
1146 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1147 p=ss;
1148 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1149 if (ss>=longpath)
1151 /* FIXME: aren't we more paranoid, than needed? */
1152 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1153 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1154 if (ll<full_name.long_name)
1156 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1157 ,ss ,ll );
1158 return 0;
1163 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1164 if (p && p[2])
1166 p+=1;
1167 if ((p-longpath)>0) longlen -= (p-longpath);
1168 lstrcpynA( p, ll , longlen);
1170 /* Now, change all '/' to '\' */
1171 for (r=p; r<(p+longlen); r++ )
1172 if (r[0]=='/') r[0]='\\';
1173 return strlen(longpath) - strlen(p) + longlen;
1177 return strlen(longpath);
1181 /***********************************************************************
1182 * GetLongPathNameW (KERNEL32.@)
1184 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1185 DWORD longlen )
1187 DOS_FULL_NAME full_name;
1188 DWORD ret = 0;
1189 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1191 /* FIXME: is it correct to always return a fully qualified short path? */
1192 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1194 ret = strlen( full_name.short_name );
1195 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1196 longpath, longlen ))
1197 longpath[longlen-1] = 0;
1199 HeapFree( GetProcessHeap(), 0, shortpathA );
1200 return ret;
1204 /***********************************************************************
1205 * DOSFS_DoGetFullPathName
1207 * Implementation of GetFullPathNameA/W.
1209 * bon@elektron 000331:
1210 * A test for GetFullPathName with many pathological cases
1211 * now gives identical output for Wine and OSR2
1213 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1214 BOOL unicode )
1216 DWORD ret;
1217 DOS_FULL_NAME full_name;
1218 char *p,*q;
1219 const char * root;
1220 char drivecur[]="c:.";
1221 char driveletter=0;
1222 int namelen,drive=0;
1224 if ((strlen(name) >1)&& (name[1]==':'))
1225 /* drive letter given */
1227 driveletter = name[0];
1229 if ((strlen(name) >2)&& (name[1]==':') &&
1230 ((name[2]=='\\') || (name[2]=='/')))
1231 /* absolute path given */
1233 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1234 drive = (int)FILE_toupper(name[0]) - 'A';
1236 else
1238 if (driveletter)
1239 drivecur[0]=driveletter;
1240 else
1241 strcpy(drivecur,".");
1242 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1244 FIXME("internal: error getting drive/path\n");
1245 return 0;
1247 /* find path that drive letter substitutes*/
1248 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1249 root= DRIVE_GetRoot(drive);
1250 if (!root)
1252 FIXME("internal: error getting DOS Drive Root\n");
1253 return 0;
1255 if (!strcmp(root,"/"))
1257 /* we have just the last / and we need it. */
1258 p= full_name.long_name;
1260 else
1262 p= full_name.long_name +strlen(root);
1264 /* append long name (= unix name) to drive */
1265 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1266 /* append name to treat */
1267 namelen= strlen(full_name.short_name);
1268 p = (char*)name;
1269 if (driveletter)
1270 p += +2; /* skip drive name when appending */
1271 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1273 FIXME("internal error: buffer too small\n");
1274 return 0;
1276 full_name.short_name[namelen++] ='\\';
1277 full_name.short_name[namelen] = 0;
1278 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1280 /* reverse all slashes */
1281 for (p=full_name.short_name;
1282 p < full_name.short_name+strlen(full_name.short_name);
1283 p++)
1285 if ( *p == '/' )
1286 *p = '\\';
1288 /* Use memmove, as areas overlap */
1289 /* Delete .. */
1290 while ((p = strstr(full_name.short_name,"\\..\\")))
1292 if (p > full_name.short_name+2)
1294 *p = 0;
1295 q = strrchr(full_name.short_name,'\\');
1296 memmove(q+1,p+4,strlen(p+4)+1);
1298 else
1300 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1303 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1305 /* This case istn't treated yet : c:..\test */
1306 memmove(full_name.short_name+2,full_name.short_name+4,
1307 strlen(full_name.short_name+4)+1);
1309 /* Delete . */
1310 while ((p = strstr(full_name.short_name,"\\.\\")))
1312 *(p+1) = 0;
1313 memmove(p+1,p+3,strlen(p+3)+1);
1315 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1316 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1317 namelen=strlen(full_name.short_name);
1318 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1320 /* one more strange case: "c:\test\test1\.."
1321 return "c:\test" */
1322 *(full_name.short_name+namelen-3)=0;
1323 q = strrchr(full_name.short_name,'\\');
1324 *q =0;
1326 if (full_name.short_name[namelen-1]=='.')
1327 full_name.short_name[(namelen--)-1] =0;
1328 if (!driveletter)
1329 if (full_name.short_name[namelen-1]=='\\')
1330 full_name.short_name[(namelen--)-1] =0;
1331 TRACE("got %s\n",full_name.short_name);
1333 /* If the lpBuffer buffer is too small, the return value is the
1334 size of the buffer, in characters, required to hold the path
1335 plus the terminating \0 (tested against win95osr2, bon 001118)
1336 . */
1337 ret = strlen(full_name.short_name);
1338 if (ret >= len )
1340 /* don't touch anything when the buffer is not large enough */
1341 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1342 return ret+1;
1344 if (result)
1346 if (unicode)
1347 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1348 else
1349 lstrcpynA( result, full_name.short_name, len );
1352 TRACE("returning '%s'\n", full_name.short_name );
1353 return ret;
1357 /***********************************************************************
1358 * GetFullPathNameA (KERNEL32.@)
1359 * NOTES
1360 * if the path closed with '\', *lastpart is 0
1362 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1363 LPSTR *lastpart )
1365 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1366 if (ret && (ret<=len) && buffer && lastpart)
1368 LPSTR p = buffer + strlen(buffer);
1370 if (*p != '\\')
1372 while ((p > buffer + 2) && (*p != '\\')) p--;
1373 *lastpart = p + 1;
1375 else *lastpart = NULL;
1377 return ret;
1381 /***********************************************************************
1382 * GetFullPathNameW (KERNEL32.@)
1384 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1385 LPWSTR *lastpart )
1387 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1388 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1389 HeapFree( GetProcessHeap(), 0, nameA );
1390 if (ret && (ret<=len) && buffer && lastpart)
1392 LPWSTR p = buffer + strlenW(buffer);
1393 if (*p != (WCHAR)'\\')
1395 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1396 *lastpart = p + 1;
1398 else *lastpart = NULL;
1400 return ret;
1404 /***********************************************************************
1405 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1407 * Return the full Unix file name for a given path.
1409 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1411 BOOL ret;
1412 DOS_FULL_NAME path;
1413 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1414 return ret;
1418 /***********************************************************************
1419 * DOSFS_FindNextEx
1421 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1423 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1424 UINT flags = DRIVE_GetFlags( info->drive );
1425 char *p, buffer[MAX_PATHNAME_LEN];
1426 const char *drive_path;
1427 int drive_root;
1428 LPCSTR long_name, short_name;
1429 BY_HANDLE_FILE_INFORMATION fileinfo;
1430 char dos_name[13];
1432 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1434 if (info->cur_pos) return 0;
1435 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1436 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1437 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1438 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1439 entry->nFileSizeHigh = 0;
1440 entry->nFileSizeLow = 0;
1441 entry->dwReserved0 = 0;
1442 entry->dwReserved1 = 0;
1443 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1444 strcpy( entry->cAlternateFileName, entry->cFileName );
1445 info->cur_pos++;
1446 TRACE("returning %s (%s) as label\n",
1447 entry->cFileName, entry->cAlternateFileName);
1448 return 1;
1451 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1452 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1453 drive_root = !*drive_path;
1455 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1456 strcat( buffer, "/" );
1457 p = buffer + strlen(buffer);
1459 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1461 info->cur_pos++;
1463 /* Don't return '.' and '..' in the root of the drive */
1464 if (drive_root && (long_name[0] == '.') &&
1465 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1466 continue;
1468 /* Check the long mask */
1470 if (info->long_mask)
1472 if (!DOSFS_MatchLong( info->long_mask, long_name,
1473 flags & DRIVE_CASE_SENSITIVE )) continue;
1476 /* Check the short mask */
1478 if (info->short_mask)
1480 if (!short_name)
1482 DOSFS_Hash( long_name, dos_name, TRUE,
1483 !(flags & DRIVE_CASE_SENSITIVE) );
1484 short_name = dos_name;
1486 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1489 /* Check the file attributes */
1491 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1492 if (!FILE_Stat( buffer, &fileinfo ))
1494 WARN("can't stat %s\n", buffer);
1495 continue;
1497 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1498 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1500 static int show_dir_symlinks = -1;
1501 if (show_dir_symlinks == -1)
1502 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1503 if (!show_dir_symlinks) continue;
1506 if (fileinfo.dwFileAttributes & ~attr) continue;
1508 /* We now have a matching entry; fill the result and return */
1510 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1511 entry->ftCreationTime = fileinfo.ftCreationTime;
1512 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1513 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1514 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1515 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1517 if (short_name)
1518 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1519 else
1520 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1521 !(flags & DRIVE_CASE_SENSITIVE) );
1523 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1524 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1525 TRACE("returning %s (%s) %02lx %ld\n",
1526 entry->cFileName, entry->cAlternateFileName,
1527 entry->dwFileAttributes, entry->nFileSizeLow );
1528 return 1;
1530 return 0; /* End of directory */
1533 /***********************************************************************
1534 * DOSFS_FindNext
1536 * Find the next matching file. Return the number of entries read to find
1537 * the matching one, or 0 if no more entries.
1538 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1539 * file name mask. Either or both can be NULL.
1541 * NOTE: This is supposed to be only called by the int21 emulation
1542 * routines. Thus, we should own the Win16Mutex anyway.
1543 * Nevertheless, we explicitly enter it to ensure the static
1544 * directory cache is protected.
1546 int DOSFS_FindNext( const char *path, const char *short_mask,
1547 const char *long_mask, int drive, BYTE attr,
1548 int skip, WIN32_FIND_DATAA *entry )
1550 static FIND_FIRST_INFO info;
1551 LPCSTR short_name, long_name;
1552 int count;
1554 _EnterWin16Lock();
1556 /* Check the cached directory */
1557 if (!(info.dir && info.path == path && info.short_mask == short_mask
1558 && info.long_mask == long_mask && info.drive == drive
1559 && info.attr == attr && info.cur_pos <= skip))
1561 /* Not in the cache, open it anew */
1562 if (info.dir) DOSFS_CloseDir( info.dir );
1564 info.path = (LPSTR)path;
1565 info.long_mask = (LPSTR)long_mask;
1566 info.short_mask = (LPSTR)short_mask;
1567 info.attr = attr;
1568 info.drive = drive;
1569 info.cur_pos = 0;
1570 info.dir = DOSFS_OpenDir( info.path );
1573 /* Skip to desired position */
1574 while (info.cur_pos < skip)
1575 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1576 info.cur_pos++;
1577 else
1578 break;
1580 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1581 count = info.cur_pos - skip;
1582 else
1583 count = 0;
1585 if (!count)
1587 if (info.dir) DOSFS_CloseDir( info.dir );
1588 memset( &info, '\0', sizeof(info) );
1591 _LeaveWin16Lock();
1593 return count;
1596 /*************************************************************************
1597 * FindFirstFileExA (KERNEL32.@)
1599 HANDLE WINAPI FindFirstFileExA(
1600 LPCSTR lpFileName,
1601 FINDEX_INFO_LEVELS fInfoLevelId,
1602 LPVOID lpFindFileData,
1603 FINDEX_SEARCH_OPS fSearchOp,
1604 LPVOID lpSearchFilter,
1605 DWORD dwAdditionalFlags)
1607 DOS_FULL_NAME full_name;
1608 HGLOBAL handle;
1609 FIND_FIRST_INFO *info;
1611 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1613 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1614 return INVALID_HANDLE_VALUE;
1617 switch(fInfoLevelId)
1619 case FindExInfoStandard:
1621 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1622 data->dwReserved0 = data->dwReserved1 = 0x0;
1623 if (!lpFileName) return 0;
1624 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1625 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1626 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1627 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1628 strcpy( info->path, full_name.long_name );
1629 info->long_mask = strrchr( info->path, '/' );
1630 *(info->long_mask++) = '\0';
1631 info->short_mask = NULL;
1632 info->attr = 0xff;
1633 if (lpFileName[0] && (lpFileName[1] == ':'))
1634 info->drive = FILE_toupper(*lpFileName) - 'A';
1635 else info->drive = DRIVE_GetCurrentDrive();
1636 info->cur_pos = 0;
1638 info->dir = DOSFS_OpenDir( info->path );
1640 GlobalUnlock( handle );
1641 if (!FindNextFileA( handle, data ))
1643 FindClose( handle );
1644 SetLastError( ERROR_NO_MORE_FILES );
1645 break;
1647 return handle;
1649 break;
1650 default:
1651 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1653 return INVALID_HANDLE_VALUE;
1656 /*************************************************************************
1657 * FindFirstFileA (KERNEL32.@)
1659 HANDLE WINAPI FindFirstFileA(
1660 LPCSTR lpFileName,
1661 WIN32_FIND_DATAA *lpFindData )
1663 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1664 FindExSearchNameMatch, NULL, 0);
1667 /*************************************************************************
1668 * FindFirstFileExW (KERNEL32.@)
1670 HANDLE WINAPI FindFirstFileExW(
1671 LPCWSTR lpFileName,
1672 FINDEX_INFO_LEVELS fInfoLevelId,
1673 LPVOID lpFindFileData,
1674 FINDEX_SEARCH_OPS fSearchOp,
1675 LPVOID lpSearchFilter,
1676 DWORD dwAdditionalFlags)
1678 HANDLE handle;
1679 WIN32_FIND_DATAA dataA;
1680 LPVOID _lpFindFileData;
1681 LPSTR pathA;
1683 switch(fInfoLevelId)
1685 case FindExInfoStandard:
1687 _lpFindFileData = &dataA;
1689 break;
1690 default:
1691 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1692 return INVALID_HANDLE_VALUE;
1695 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1696 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1697 HeapFree( GetProcessHeap(), 0, pathA );
1698 if (handle == INVALID_HANDLE_VALUE) return handle;
1700 switch(fInfoLevelId)
1702 case FindExInfoStandard:
1704 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1705 dataW->dwFileAttributes = dataA.dwFileAttributes;
1706 dataW->ftCreationTime = dataA.ftCreationTime;
1707 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1708 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1709 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1710 dataW->nFileSizeLow = dataA.nFileSizeLow;
1711 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1712 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1713 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1714 dataW->cAlternateFileName,
1715 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1717 break;
1718 default:
1719 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1720 return INVALID_HANDLE_VALUE;
1722 return handle;
1725 /*************************************************************************
1726 * FindFirstFileW (KERNEL32.@)
1728 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1730 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1731 FindExSearchNameMatch, NULL, 0);
1734 /*************************************************************************
1735 * FindNextFileA (KERNEL32.@)
1737 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1739 FIND_FIRST_INFO *info;
1741 if ((handle == INVALID_HANDLE_VALUE) ||
1742 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1744 SetLastError( ERROR_INVALID_HANDLE );
1745 return FALSE;
1747 GlobalUnlock( handle );
1748 if (!info->path || !info->dir)
1750 SetLastError( ERROR_NO_MORE_FILES );
1751 return FALSE;
1753 if (!DOSFS_FindNextEx( info, data ))
1755 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1756 HeapFree( GetProcessHeap(), 0, info->path );
1757 info->path = info->long_mask = NULL;
1758 SetLastError( ERROR_NO_MORE_FILES );
1759 return FALSE;
1761 return TRUE;
1765 /*************************************************************************
1766 * FindNextFileW (KERNEL32.@)
1768 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1770 WIN32_FIND_DATAA dataA;
1771 if (!FindNextFileA( handle, &dataA )) return FALSE;
1772 data->dwFileAttributes = dataA.dwFileAttributes;
1773 data->ftCreationTime = dataA.ftCreationTime;
1774 data->ftLastAccessTime = dataA.ftLastAccessTime;
1775 data->ftLastWriteTime = dataA.ftLastWriteTime;
1776 data->nFileSizeHigh = dataA.nFileSizeHigh;
1777 data->nFileSizeLow = dataA.nFileSizeLow;
1778 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1779 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1780 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1781 data->cAlternateFileName,
1782 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1783 return TRUE;
1786 /*************************************************************************
1787 * FindClose (KERNEL32.@)
1789 BOOL WINAPI FindClose( HANDLE handle )
1791 FIND_FIRST_INFO *info;
1793 if ((handle == INVALID_HANDLE_VALUE) ||
1794 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1796 SetLastError( ERROR_INVALID_HANDLE );
1797 return FALSE;
1799 __TRY
1801 if (info->dir) DOSFS_CloseDir( info->dir );
1802 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1804 __EXCEPT(page_fault)
1806 WARN("Illegal handle %x\n", handle);
1807 SetLastError( ERROR_INVALID_HANDLE );
1808 return FALSE;
1810 __ENDTRY
1811 GlobalUnlock( handle );
1812 GlobalFree( handle );
1813 return TRUE;
1816 /***********************************************************************
1817 * DOSFS_UnixTimeToFileTime
1819 * Convert a Unix time to FILETIME format.
1820 * The FILETIME structure is a 64-bit value representing the number of
1821 * 100-nanosecond intervals since January 1, 1601, 0:00.
1822 * 'remainder' is the nonnegative number of 100-ns intervals
1823 * corresponding to the time fraction smaller than 1 second that
1824 * couldn't be stored in the time_t value.
1826 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1827 DWORD remainder )
1829 /* NOTES:
1831 CONSTANTS:
1832 The time difference between 1 January 1601, 00:00:00 and
1833 1 January 1970, 00:00:00 is 369 years, plus the leap years
1834 from 1604 to 1968, excluding 1700, 1800, 1900.
1835 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1836 of 134774 days.
1838 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1840 The time difference is 134774 * 86400 * 10000000, which can be written
1841 116444736000000000
1842 27111902 * 2^32 + 3577643008
1843 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1845 If you find that these constants are buggy, please change them in all
1846 instances in both conversion functions.
1848 VERSIONS:
1849 There are two versions, one of them uses long long variables and
1850 is presumably faster but not ISO C. The other one uses standard C
1851 data types and operations but relies on the assumption that negative
1852 numbers are stored as 2's complement (-1 is 0xffff....). If this
1853 assumption is violated, dates before 1970 will not convert correctly.
1854 This should however work on any reasonable architecture where WINE
1855 will run.
1857 DETAILS:
1859 Take care not to remove the casts. I have tested these functions
1860 (in both versions) for a lot of numbers. I would be interested in
1861 results on other compilers than GCC.
1863 The operations have been designed to account for the possibility
1864 of 64-bit time_t in future UNICES. Even the versions without
1865 internal long long numbers will work if time_t only is 64 bit.
1866 A 32-bit shift, which was necessary for that operation, turned out
1867 not to work correctly in GCC, besides giving the warning. So I
1868 used a double 16-bit shift instead. Numbers are in the ISO version
1869 represented by three limbs, the most significant with 32 bit, the
1870 other two with 16 bit each.
1872 As the modulo-operator % is not well-defined for negative numbers,
1873 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1875 There might be quicker ways to do this in C. Certainly so in
1876 assembler.
1878 Claus Fischer, fischer@iue.tuwien.ac.at
1881 #if SIZEOF_LONG_LONG >= 8
1882 # define USE_LONG_LONG 1
1883 #else
1884 # define USE_LONG_LONG 0
1885 #endif
1887 #if USE_LONG_LONG /* gcc supports long long type */
1889 long long int t = unix_time;
1890 t *= 10000000;
1891 t += 116444736000000000LL;
1892 t += remainder;
1893 filetime->dwLowDateTime = (UINT)t;
1894 filetime->dwHighDateTime = (UINT)(t >> 32);
1896 #else /* ISO version */
1898 UINT a0; /* 16 bit, low bits */
1899 UINT a1; /* 16 bit, medium bits */
1900 UINT a2; /* 32 bit, high bits */
1902 /* Copy the unix time to a2/a1/a0 */
1903 a0 = unix_time & 0xffff;
1904 a1 = (unix_time >> 16) & 0xffff;
1905 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1906 Do not replace this by >> 32, it gives a compiler warning and it does
1907 not work. */
1908 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1909 ~((~unix_time >> 16) >> 16));
1911 /* Multiply a by 10000000 (a = a2/a1/a0)
1912 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1913 a0 *= 10000;
1914 a1 = a1 * 10000 + (a0 >> 16);
1915 a2 = a2 * 10000 + (a1 >> 16);
1916 a0 &= 0xffff;
1917 a1 &= 0xffff;
1919 a0 *= 1000;
1920 a1 = a1 * 1000 + (a0 >> 16);
1921 a2 = a2 * 1000 + (a1 >> 16);
1922 a0 &= 0xffff;
1923 a1 &= 0xffff;
1925 /* Add the time difference and the remainder */
1926 a0 += 32768 + (remainder & 0xffff);
1927 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1928 a2 += 27111902 + (a1 >> 16);
1929 a0 &= 0xffff;
1930 a1 &= 0xffff;
1932 /* Set filetime */
1933 filetime->dwLowDateTime = (a1 << 16) + a0;
1934 filetime->dwHighDateTime = a2;
1935 #endif
1939 /***********************************************************************
1940 * DOSFS_FileTimeToUnixTime
1942 * Convert a FILETIME format to Unix time.
1943 * If not NULL, 'remainder' contains the fractional part of the filetime,
1944 * in the range of [0..9999999] (even if time_t is negative).
1946 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1948 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1949 #if USE_LONG_LONG
1951 long long int t = filetime->dwHighDateTime;
1952 t <<= 32;
1953 t += (UINT)filetime->dwLowDateTime;
1954 t -= 116444736000000000LL;
1955 if (t < 0)
1957 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1958 return -1 - ((-t - 1) / 10000000);
1960 else
1962 if (remainder) *remainder = t % 10000000;
1963 return t / 10000000;
1966 #else /* ISO version */
1968 UINT a0; /* 16 bit, low bits */
1969 UINT a1; /* 16 bit, medium bits */
1970 UINT a2; /* 32 bit, high bits */
1971 UINT r; /* remainder of division */
1972 unsigned int carry; /* carry bit for subtraction */
1973 int negative; /* whether a represents a negative value */
1975 /* Copy the time values to a2/a1/a0 */
1976 a2 = (UINT)filetime->dwHighDateTime;
1977 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1978 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1980 /* Subtract the time difference */
1981 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1982 else a0 += (1 << 16) - 32768 , carry = 1;
1984 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1985 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1987 a2 -= 27111902 + carry;
1989 /* If a is negative, replace a by (-1-a) */
1990 negative = (a2 >= ((UINT)1) << 31);
1991 if (negative)
1993 /* Set a to -a - 1 (a is a2/a1/a0) */
1994 a0 = 0xffff - a0;
1995 a1 = 0xffff - a1;
1996 a2 = ~a2;
1999 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2000 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2001 a1 += (a2 % 10000) << 16;
2002 a2 /= 10000;
2003 a0 += (a1 % 10000) << 16;
2004 a1 /= 10000;
2005 r = a0 % 10000;
2006 a0 /= 10000;
2008 a1 += (a2 % 1000) << 16;
2009 a2 /= 1000;
2010 a0 += (a1 % 1000) << 16;
2011 a1 /= 1000;
2012 r += (a0 % 1000) * 10000;
2013 a0 /= 1000;
2015 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2016 if (negative)
2018 /* Set a to -a - 1 (a is a2/a1/a0) */
2019 a0 = 0xffff - a0;
2020 a1 = 0xffff - a1;
2021 a2 = ~a2;
2023 r = 9999999 - r;
2026 if (remainder) *remainder = r;
2028 /* Do not replace this by << 32, it gives a compiler warning and it does
2029 not work. */
2030 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2031 #endif
2035 /***********************************************************************
2036 * MulDiv (KERNEL32.@)
2037 * RETURNS
2038 * Result of multiplication and division
2039 * -1: Overflow occurred or Divisor was 0
2041 INT WINAPI MulDiv(
2042 INT nMultiplicand,
2043 INT nMultiplier,
2044 INT nDivisor)
2046 #if SIZEOF_LONG_LONG >= 8
2047 long long ret;
2049 if (!nDivisor) return -1;
2051 /* We want to deal with a positive divisor to simplify the logic. */
2052 if (nDivisor < 0)
2054 nMultiplicand = - nMultiplicand;
2055 nDivisor = -nDivisor;
2058 /* If the result is positive, we "add" to round. else, we subtract to round. */
2059 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2060 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2061 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2062 else
2063 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2065 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2066 return ret;
2067 #else
2068 if (!nDivisor) return -1;
2070 /* We want to deal with a positive divisor to simplify the logic. */
2071 if (nDivisor < 0)
2073 nMultiplicand = - nMultiplicand;
2074 nDivisor = -nDivisor;
2077 /* If the result is positive, we "add" to round. else, we subtract to round. */
2078 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2079 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2080 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2082 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2084 #endif
2088 /***********************************************************************
2089 * DosDateTimeToFileTime (KERNEL32.@)
2091 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2093 struct tm newtm;
2095 newtm.tm_sec = (fattime & 0x1f) * 2;
2096 newtm.tm_min = (fattime >> 5) & 0x3f;
2097 newtm.tm_hour = (fattime >> 11);
2098 newtm.tm_mday = (fatdate & 0x1f);
2099 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2100 newtm.tm_year = (fatdate >> 9) + 80;
2101 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2102 return TRUE;
2106 /***********************************************************************
2107 * FileTimeToDosDateTime (KERNEL32.@)
2109 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2110 LPWORD fattime )
2112 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2113 struct tm *tm = localtime( &unixtime );
2114 if (fattime)
2115 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2116 if (fatdate)
2117 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2118 + tm->tm_mday;
2119 return TRUE;
2123 /***********************************************************************
2124 * LocalFileTimeToFileTime (KERNEL32.@)
2126 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2127 LPFILETIME utcft )
2129 struct tm *xtm;
2130 DWORD remainder;
2132 /* convert from local to UTC. Perhaps not correct. FIXME */
2133 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2134 xtm = gmtime( &unixtime );
2135 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2136 return TRUE;
2140 /***********************************************************************
2141 * FileTimeToLocalFileTime (KERNEL32.@)
2143 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2144 LPFILETIME localft )
2146 DWORD remainder;
2147 /* convert from UTC to local. Perhaps not correct. FIXME */
2148 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2149 #ifdef HAVE_TIMEGM
2150 struct tm *xtm = localtime( &unixtime );
2151 time_t localtime;
2153 localtime = timegm(xtm);
2154 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2156 #else
2157 struct tm *xtm,*gtm;
2158 time_t time1,time2;
2160 xtm = localtime( &unixtime );
2161 gtm = gmtime( &unixtime );
2162 time1 = mktime(xtm);
2163 time2 = mktime(gtm);
2164 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2165 #endif
2166 return TRUE;
2170 /***********************************************************************
2171 * FileTimeToSystemTime (KERNEL32.@)
2173 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2175 struct tm *xtm;
2176 DWORD remainder;
2177 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2178 xtm = gmtime(&xtime);
2179 syst->wYear = xtm->tm_year+1900;
2180 syst->wMonth = xtm->tm_mon + 1;
2181 syst->wDayOfWeek = xtm->tm_wday;
2182 syst->wDay = xtm->tm_mday;
2183 syst->wHour = xtm->tm_hour;
2184 syst->wMinute = xtm->tm_min;
2185 syst->wSecond = xtm->tm_sec;
2186 syst->wMilliseconds = remainder / 10000;
2187 return TRUE;
2190 /***********************************************************************
2191 * QueryDosDeviceA (KERNEL32.@)
2193 * returns array of strings terminated by \0, terminated by \0
2195 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2197 LPSTR s;
2198 char buffer[200];
2200 TRACE("(%s,...)\n", devname ? devname : "<null>");
2201 if (!devname) {
2202 /* return known MSDOS devices */
2203 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2204 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2205 return min(bufsize,sizeof(devices));
2207 /* In theory all that are possible and have been defined.
2208 * Now just those below, since mirc uses it to check for special files.
2210 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2211 * but currently we just ignore that.)
2213 #define CHECK(x) (strstr(devname,#x)==devname)
2214 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2215 strcpy(buffer,"\\DEV\\");
2216 strcat(buffer,devname);
2217 if ((s=strchr(buffer,':'))) *s='\0';
2218 lstrcpynA(target,buffer,bufsize);
2219 return strlen(buffer)+1;
2220 } else {
2221 if (strchr(devname,':') || devname[0]=='\\') {
2222 /* This might be a DOS device we do not handle yet ... */
2223 FIXME("(%s) not detected as DOS device!\n",devname);
2225 SetLastError(ERROR_DEV_NOT_EXIST);
2226 return 0;
2232 /***********************************************************************
2233 * QueryDosDeviceW (KERNEL32.@)
2235 * returns array of strings terminated by \0, terminated by \0
2237 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2239 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2240 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2241 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2243 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2244 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2245 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2246 return ret;
2250 /***********************************************************************
2251 * SystemTimeToFileTime (KERNEL32.@)
2253 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2255 #ifdef HAVE_TIMEGM
2256 struct tm xtm;
2257 time_t utctime;
2258 #else
2259 struct tm xtm,*local_tm,*utc_tm;
2260 time_t localtim,utctime;
2261 #endif
2263 xtm.tm_year = syst->wYear-1900;
2264 xtm.tm_mon = syst->wMonth - 1;
2265 xtm.tm_wday = syst->wDayOfWeek;
2266 xtm.tm_mday = syst->wDay;
2267 xtm.tm_hour = syst->wHour;
2268 xtm.tm_min = syst->wMinute;
2269 xtm.tm_sec = syst->wSecond; /* this is UTC */
2270 xtm.tm_isdst = -1;
2271 #ifdef HAVE_TIMEGM
2272 utctime = timegm(&xtm);
2273 DOSFS_UnixTimeToFileTime( utctime, ft,
2274 syst->wMilliseconds * 10000 );
2275 #else
2276 localtim = mktime(&xtm); /* now we've got local time */
2277 local_tm = localtime(&localtim);
2278 utc_tm = gmtime(&localtim);
2279 utctime = mktime(utc_tm);
2280 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2281 syst->wMilliseconds * 10000 );
2282 #endif
2283 return TRUE;
2286 /***********************************************************************
2287 * DefineDosDeviceA (KERNEL32.@)
2289 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2290 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2291 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2292 return FALSE;
2296 --- 16 bit functions ---
2299 /*************************************************************************
2300 * FindFirstFile (KERNEL.413)
2302 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2304 DOS_FULL_NAME full_name;
2305 HGLOBAL16 handle;
2306 FIND_FIRST_INFO *info;
2308 data->dwReserved0 = data->dwReserved1 = 0x0;
2309 if (!path) return 0;
2310 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2311 return INVALID_HANDLE_VALUE16;
2312 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2313 return INVALID_HANDLE_VALUE16;
2314 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2315 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2316 strcpy( info->path, full_name.long_name );
2317 info->long_mask = strrchr( info->path, '/' );
2318 if (info->long_mask )
2319 *(info->long_mask++) = '\0';
2320 info->short_mask = NULL;
2321 info->attr = 0xff;
2322 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2323 else info->drive = DRIVE_GetCurrentDrive();
2324 info->cur_pos = 0;
2326 info->dir = DOSFS_OpenDir( info->path );
2328 GlobalUnlock16( handle );
2329 if (!FindNextFile16( handle, data ))
2331 FindClose16( handle );
2332 SetLastError( ERROR_NO_MORE_FILES );
2333 return INVALID_HANDLE_VALUE16;
2335 return handle;
2338 /*************************************************************************
2339 * FindNextFile (KERNEL.414)
2341 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2343 FIND_FIRST_INFO *info;
2345 if ((handle == INVALID_HANDLE_VALUE16) ||
2346 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2348 SetLastError( ERROR_INVALID_HANDLE );
2349 return FALSE;
2351 GlobalUnlock16( handle );
2352 if (!info->path || !info->dir)
2354 SetLastError( ERROR_NO_MORE_FILES );
2355 return FALSE;
2357 if (!DOSFS_FindNextEx( info, data ))
2359 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2360 HeapFree( GetProcessHeap(), 0, info->path );
2361 info->path = info->long_mask = NULL;
2362 SetLastError( ERROR_NO_MORE_FILES );
2363 return FALSE;
2365 return TRUE;
2368 /*************************************************************************
2369 * FindClose (KERNEL.415)
2371 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2373 FIND_FIRST_INFO *info;
2375 if ((handle == INVALID_HANDLE_VALUE16) ||
2376 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2378 SetLastError( ERROR_INVALID_HANDLE );
2379 return FALSE;
2381 if (info->dir) DOSFS_CloseDir( info->dir );
2382 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2383 GlobalUnlock16( handle );
2384 GlobalFree16( handle );
2385 return TRUE;