Added support for DirectDraw overlays using the XVideo extension.
[wine/multimedia.git] / files / dos_fs.c
blob73d9954ffa36a5b9460ce7a61876444547872a36
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
9 #include <sys/types.h>
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #ifdef HAVE_SYS_ERRNO_H
14 #include <sys/errno.h>
15 #endif
16 #include <fcntl.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <time.h>
22 #include <unistd.h>
24 #include "windef.h"
25 #include "ntddk.h"
26 #include "wine/winbase16.h"
27 #include "wine/unicode.h"
28 #include "wine/winestring.h"
29 #include "winerror.h"
30 #include "drive.h"
31 #include "file.h"
32 #include "comm.h"
33 #include "heap.h"
34 #include "msdos.h"
35 #include "syslevel.h"
36 #include "server.h"
37 #include "options.h"
38 #include "debugtools.h"
40 DEFAULT_DEBUG_CHANNEL(dosfs);
41 DECLARE_DEBUG_CHANNEL(file);
43 /* Define the VFAT ioctl to get both short and long file names */
44 /* FIXME: is it possible to get this to work on other systems? */
45 #ifdef linux
46 /* We want the real kernel dirent structure, not the libc one */
47 typedef struct
49 long d_ino;
50 long d_off;
51 unsigned short d_reclen;
52 char d_name[256];
53 } KERNEL_DIRENT;
55 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
57 #else /* linux */
58 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
59 #endif /* linux */
61 /* Chars we don't want to see in DOS file names */
62 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
64 static const DOS_DEVICE DOSFS_Devices[] =
65 /* name, device flags (see Int 21/AX=0x4400) */
67 { "CON", 0xc0d3 },
68 { "PRN", 0xa0c0 },
69 { "NUL", 0x80c4 },
70 { "AUX", 0x80c0 },
71 { "LPT1", 0xa0c0 },
72 { "LPT2", 0xa0c0 },
73 { "LPT3", 0xa0c0 },
74 { "LPT4", 0xc0d3 },
75 { "COM1", 0x80c0 },
76 { "COM2", 0x80c0 },
77 { "COM3", 0x80c0 },
78 { "COM4", 0x80c0 },
79 { "SCSIMGR$", 0xc0c0 },
80 { "HPSCAN", 0xc0c0 }
83 #define GET_DRIVE(path) \
84 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
86 /* Directory info for DOSFS_ReadDir */
87 typedef struct
89 DIR *dir;
90 #ifdef VFAT_IOCTL_READDIR_BOTH
91 int fd;
92 char short_name[12];
93 KERNEL_DIRENT dirent[2];
94 #endif
95 } DOS_DIR;
97 /* Info structure for FindFirstFile handle */
98 typedef struct
100 LPSTR path;
101 LPSTR long_mask;
102 LPSTR short_mask;
103 BYTE attr;
104 int drive;
105 int cur_pos;
106 DOS_DIR *dir;
107 } FIND_FIRST_INFO;
111 /***********************************************************************
112 * DOSFS_ValidDOSName
114 * Return 1 if Unix file 'name' is also a valid MS-DOS name
115 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
116 * File name can be terminated by '\0', '\\' or '/'.
118 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
120 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
121 const char *p = name;
122 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
123 int len = 0;
125 if (*p == '.')
127 /* Check for "." and ".." */
128 p++;
129 if (*p == '.') p++;
130 /* All other names beginning with '.' are invalid */
131 return (IS_END_OF_NAME(*p));
133 while (!IS_END_OF_NAME(*p))
135 if (strchr( invalid, *p )) return 0; /* Invalid char */
136 if (*p == '.') break; /* Start of the extension */
137 if (++len > 8) return 0; /* Name too long */
138 p++;
140 if (*p != '.') return 1; /* End of name */
141 p++;
142 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
143 len = 0;
144 while (!IS_END_OF_NAME(*p))
146 if (strchr( invalid, *p )) return 0; /* Invalid char */
147 if (*p == '.') return 0; /* Second extension not allowed */
148 if (++len > 3) return 0; /* Extension too long */
149 p++;
151 return 1;
155 /***********************************************************************
156 * DOSFS_ToDosFCBFormat
158 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
159 * expanding wild cards and converting to upper-case in the process.
160 * File name can be terminated by '\0', '\\' or '/'.
161 * Return FALSE if the name is not a valid DOS name.
162 * 'buffer' must be at least 12 characters long.
164 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
166 static const char invalid_chars[] = INVALID_DOS_CHARS;
167 const char *p = name;
168 int i;
170 /* Check for "." and ".." */
171 if (*p == '.')
173 p++;
174 strcpy( buffer, ". " );
175 if (*p == '.')
177 buffer[1] = '.';
178 p++;
180 return (!*p || (*p == '/') || (*p == '\\'));
183 for (i = 0; i < 8; i++)
185 switch(*p)
187 case '\0':
188 case '\\':
189 case '/':
190 case '.':
191 buffer[i] = ' ';
192 break;
193 case '?':
194 p++;
195 /* fall through */
196 case '*':
197 buffer[i] = '?';
198 break;
199 default:
200 if (strchr( invalid_chars, *p )) return FALSE;
201 buffer[i] = toupper(*p);
202 p++;
203 break;
207 if (*p == '*')
209 /* Skip all chars after wildcard up to first dot */
210 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
212 else
214 /* Check if name too long */
215 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
217 if (*p == '.') p++; /* Skip dot */
219 for (i = 8; i < 11; i++)
221 switch(*p)
223 case '\0':
224 case '\\':
225 case '/':
226 buffer[i] = ' ';
227 break;
228 case '.':
229 return FALSE; /* Second extension not allowed */
230 case '?':
231 p++;
232 /* fall through */
233 case '*':
234 buffer[i] = '?';
235 break;
236 default:
237 if (strchr( invalid_chars, *p )) return FALSE;
238 buffer[i] = toupper(*p);
239 p++;
240 break;
243 buffer[11] = '\0';
245 /* at most 3 character of the extension are processed
246 * is something behind this ?
248 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
249 return IS_END_OF_NAME(*p);
253 /***********************************************************************
254 * DOSFS_ToDosDTAFormat
256 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
257 * converting to upper-case in the process.
258 * File name can be terminated by '\0', '\\' or '/'.
259 * 'buffer' must be at least 13 characters long.
261 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
263 char *p;
265 memcpy( buffer, name, 8 );
266 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
267 *p++ = '.';
268 memcpy( p, name + 8, 3 );
269 for (p += 3; p[-1] == ' '; p--);
270 if (p[-1] == '.') p--;
271 *p = '\0';
275 /***********************************************************************
276 * DOSFS_MatchShort
278 * Check a DOS file name against a mask (both in FCB format).
280 static int DOSFS_MatchShort( const char *mask, const char *name )
282 int i;
283 for (i = 11; i > 0; i--, mask++, name++)
284 if ((*mask != '?') && (*mask != *name)) return 0;
285 return 1;
289 /***********************************************************************
290 * DOSFS_MatchLong
292 * Check a long file name against a mask.
294 * Tests (done in W95 DOS shell - case insensitive):
295 * *.txt test1.test.txt *
296 * *st1* test1.txt *
297 * *.t??????.t* test1.ta.tornado.txt *
298 * *tornado* test1.ta.tornado.txt *
299 * t*t test1.ta.tornado.txt *
300 * ?est* test1.txt *
301 * ?est??? test1.txt -
302 * *test1.txt* test1.txt *
303 * h?l?o*t.dat hellothisisatest.dat *
305 static int DOSFS_MatchLong( const char *mask, const char *name,
306 int case_sensitive )
308 const char *lastjoker = NULL;
309 const char *next_to_retry = NULL;
311 if (!strcmp( mask, "*.*" )) return 1;
312 while (*name && *mask)
314 if (*mask == '*')
316 mask++;
317 while (*mask == '*') mask++; /* Skip consecutive '*' */
318 lastjoker = mask;
319 if (!*mask) return 1; /* end of mask is all '*', so match */
321 /* skip to the next match after the joker(s) */
322 if (case_sensitive) while (*name && (*name != *mask)) name++;
323 else while (*name && (toupper(*name) != toupper(*mask))) name++;
325 if (!*name) break;
326 next_to_retry = name;
328 else if (*mask != '?')
330 int mismatch = 0;
331 if (case_sensitive)
333 if (*mask != *name) mismatch = 1;
335 else
337 if (toupper(*mask) != toupper(*name)) mismatch = 1;
339 if (!mismatch)
341 mask++;
342 name++;
343 if (*mask == '\0')
345 if (*name == '\0')
346 return 1;
347 if (lastjoker)
348 mask = lastjoker;
351 else /* mismatch ! */
353 if (lastjoker) /* we had an '*', so we can try unlimitedly */
355 mask = lastjoker;
357 /* this scan sequence was a mismatch, so restart
358 * 1 char after the first char we checked last time */
359 next_to_retry++;
360 name = next_to_retry;
362 else
363 return 0; /* bad luck */
366 else /* '?' */
368 mask++;
369 name++;
372 while ((*mask == '.') || (*mask == '*'))
373 mask++; /* Ignore trailing '.' or '*' in mask */
374 return (!*name && !*mask);
378 /***********************************************************************
379 * DOSFS_OpenDir
381 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
383 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
384 if (!dir)
386 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
387 return NULL;
390 /* Treat empty path as root directory. This simplifies path split into
391 directory and mask in several other places */
392 if (!*path) path = "/";
394 #ifdef VFAT_IOCTL_READDIR_BOTH
396 /* Check if the VFAT ioctl is supported on this directory */
398 if ((dir->fd = open( path, O_RDONLY )) != -1)
400 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
402 close( dir->fd );
403 dir->fd = -1;
405 else
407 /* Set the file pointer back at the start of the directory */
408 lseek( dir->fd, 0, SEEK_SET );
409 dir->dir = NULL;
410 return dir;
413 #endif /* VFAT_IOCTL_READDIR_BOTH */
415 /* Now use the standard opendir/readdir interface */
417 if (!(dir->dir = opendir( path )))
419 HeapFree( GetProcessHeap(), 0, dir );
420 return NULL;
422 return dir;
426 /***********************************************************************
427 * DOSFS_CloseDir
429 static void DOSFS_CloseDir( DOS_DIR *dir )
431 #ifdef VFAT_IOCTL_READDIR_BOTH
432 if (dir->fd != -1) close( dir->fd );
433 #endif /* VFAT_IOCTL_READDIR_BOTH */
434 if (dir->dir) closedir( dir->dir );
435 HeapFree( GetProcessHeap(), 0, dir );
439 /***********************************************************************
440 * DOSFS_ReadDir
442 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
443 LPCSTR *short_name )
445 struct dirent *dirent;
447 #ifdef VFAT_IOCTL_READDIR_BOTH
448 if (dir->fd != -1)
450 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
451 if (!dir->dirent[0].d_reclen) return FALSE;
452 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
453 dir->short_name[0] = '\0';
454 *short_name = dir->short_name;
455 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
456 else *long_name = dir->dirent[0].d_name;
457 return TRUE;
460 #endif /* VFAT_IOCTL_READDIR_BOTH */
462 if (!(dirent = readdir( dir->dir ))) return FALSE;
463 *long_name = dirent->d_name;
464 *short_name = NULL;
465 return TRUE;
469 /***********************************************************************
470 * DOSFS_Hash
472 * Transform a Unix file name into a hashed DOS name. If the name is a valid
473 * DOS name, it is converted to upper-case; otherwise it is replaced by a
474 * hashed version that fits in 8.3 format.
475 * File name can be terminated by '\0', '\\' or '/'.
476 * 'buffer' must be at least 13 characters long.
478 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
479 BOOL ignore_case )
481 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
482 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
484 const char *p, *ext;
485 char *dst;
486 unsigned short hash;
487 int i;
489 if (dir_format) strcpy( buffer, " " );
491 if (DOSFS_ValidDOSName( name, ignore_case ))
493 /* Check for '.' and '..' */
494 if (*name == '.')
496 buffer[0] = '.';
497 if (!dir_format) buffer[1] = buffer[2] = '\0';
498 if (name[1] == '.') buffer[1] = '.';
499 return;
502 /* Simply copy the name, converting to uppercase */
504 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
505 *dst++ = toupper(*name);
506 if (*name == '.')
508 if (dir_format) dst = buffer + 8;
509 else *dst++ = '.';
510 for (name++; !IS_END_OF_NAME(*name); name++)
511 *dst++ = toupper(*name);
513 if (!dir_format) *dst = '\0';
514 return;
517 /* Compute the hash code of the file name */
518 /* If you know something about hash functions, feel free to */
519 /* insert a better algorithm here... */
520 if (ignore_case)
522 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
523 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
524 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
526 else
528 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
529 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
530 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
533 /* Find last dot for start of the extension */
534 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
535 if (*p == '.') ext = p;
536 if (ext && IS_END_OF_NAME(ext[1]))
537 ext = NULL; /* Empty extension ignored */
539 /* Copy first 4 chars, replacing invalid chars with '_' */
540 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
542 if (IS_END_OF_NAME(*p) || (p == ext)) break;
543 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
545 /* Pad to 5 chars with '~' */
546 while (i-- >= 0) *dst++ = '~';
548 /* Insert hash code converted to 3 ASCII chars */
549 *dst++ = hash_chars[(hash >> 10) & 0x1f];
550 *dst++ = hash_chars[(hash >> 5) & 0x1f];
551 *dst++ = hash_chars[hash & 0x1f];
553 /* Copy the first 3 chars of the extension (if any) */
554 if (ext)
556 if (!dir_format) *dst++ = '.';
557 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
558 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
560 if (!dir_format) *dst = '\0';
564 /***********************************************************************
565 * DOSFS_FindUnixName
567 * Find the Unix file name in a given directory that corresponds to
568 * a file name (either in Unix or DOS format).
569 * File name can be terminated by '\0', '\\' or '/'.
570 * Return TRUE if OK, FALSE if no file name matches.
572 * 'long_buf' must be at least 'long_len' characters long. If the long name
573 * turns out to be larger than that, the function returns FALSE.
574 * 'short_buf' must be at least 13 characters long.
576 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
577 INT long_len, LPSTR short_buf, BOOL ignore_case)
579 DOS_DIR *dir;
580 LPCSTR long_name, short_name;
581 char dos_name[12], tmp_buf[13];
582 BOOL ret;
584 const char *p = strchr( name, '/' );
585 int len = p ? (int)(p - name) : strlen(name);
586 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
587 /* Ignore trailing dots and spaces */
588 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
589 if (long_len < len + 1) return FALSE;
591 TRACE("%s,%s\n", path, name );
593 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
595 if (!(dir = DOSFS_OpenDir( path )))
597 WARN("(%s,%s): can't open dir: %s\n",
598 path, name, strerror(errno) );
599 return FALSE;
602 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
604 /* Check against Unix name */
605 if (len == strlen(long_name))
607 if (!ignore_case)
609 if (!strncmp( long_name, name, len )) break;
611 else
613 if (!strncasecmp( long_name, name, len )) break;
616 if (dos_name[0])
618 /* Check against hashed DOS name */
619 if (!short_name)
621 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
622 short_name = tmp_buf;
624 if (!strcmp( dos_name, short_name )) break;
627 if (ret)
629 if (long_buf) strcpy( long_buf, long_name );
630 if (short_buf)
632 if (short_name)
633 DOSFS_ToDosDTAFormat( short_name, short_buf );
634 else
635 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
637 TRACE("(%s,%s) -> %s (%s)\n",
638 path, name, long_name, short_buf ? short_buf : "***");
640 else
641 WARN("'%s' not found in '%s'\n", name, path);
642 DOSFS_CloseDir( dir );
643 return ret;
647 /***********************************************************************
648 * DOSFS_GetDevice
650 * Check if a DOS file name represents a DOS device and return the device.
652 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
654 int i;
655 const char *p;
657 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
658 if (name[0] && (name[1] == ':')) name += 2;
659 if ((p = strrchr( name, '/' ))) name = p + 1;
660 if ((p = strrchr( name, '\\' ))) name = p + 1;
661 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
663 const char *dev = DOSFS_Devices[i].name;
664 if (!strncasecmp( dev, name, strlen(dev) ))
666 p = name + strlen( dev );
667 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
670 return NULL;
674 /***********************************************************************
675 * DOSFS_GetDeviceByHandle
677 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
679 const DOS_DEVICE *ret = NULL;
680 SERVER_START_REQ
682 struct get_file_info_request *req = server_alloc_req( sizeof(*req), 0 );
684 req->handle = hFile;
685 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
687 if ((req->attr >= 0) &&
688 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
689 ret = &DOSFS_Devices[req->attr];
692 SERVER_END_REQ;
693 return ret;
697 /***********************************************************************
698 * DOSFS_OpenDevice
700 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
702 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
704 int i;
705 const char *p;
706 HFILE handle;
708 if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
709 if (name[0] && (name[1] == ':')) name += 2;
710 if ((p = strrchr( name, '/' ))) name = p + 1;
711 if ((p = strrchr( name, '\\' ))) name = p + 1;
712 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
714 const char *dev = DOSFS_Devices[i].name;
715 if (!strncasecmp( dev, name, strlen(dev) ))
717 p = name + strlen( dev );
718 if (!*p || (*p == '.')) {
719 /* got it */
720 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
721 return FILE_CreateFile( "/dev/null", access,
722 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
723 OPEN_EXISTING, 0, -1, TRUE );
724 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
725 HFILE to_dup;
726 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
727 case GENERIC_READ:
728 to_dup = GetStdHandle( STD_INPUT_HANDLE );
729 break;
730 case GENERIC_WRITE:
731 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
732 break;
733 default:
734 FIXME("can't open CON read/write\n");
735 return HFILE_ERROR;
736 break;
738 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
739 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
740 handle = HFILE_ERROR;
741 return handle;
743 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
744 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
746 return FILE_CreateDevice( i, access, NULL );
749 if( (handle=COMM_CreatePort(name,access)) )
750 return handle;
752 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
753 return HFILE_ERROR;
757 return HFILE_ERROR;
761 /***********************************************************************
762 * DOSFS_GetPathDrive
764 * Get the drive specified by a given path name (DOS or Unix format).
766 static int DOSFS_GetPathDrive( const char **name )
768 int drive;
769 const char *p = *name;
771 if (*p && (p[1] == ':'))
773 drive = toupper(*p) - 'A';
774 *name += 2;
776 else if (*p == '/') /* Absolute Unix path? */
778 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
780 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
781 /* Assume it really was a DOS name */
782 drive = DRIVE_GetCurrentDrive();
785 else drive = DRIVE_GetCurrentDrive();
787 if (!DRIVE_IsValid(drive))
789 SetLastError( ERROR_INVALID_DRIVE );
790 return -1;
792 return drive;
796 /***********************************************************************
797 * DOSFS_GetFullName
799 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
800 * Unix name / short DOS name pair.
801 * Return FALSE if one of the path components does not exist. The last path
802 * component is only checked if 'check_last' is non-zero.
803 * The buffers pointed to by 'long_buf' and 'short_buf' must be
804 * at least MAX_PATHNAME_LEN long.
806 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
808 BOOL found;
809 UINT flags;
810 char *p_l, *p_s, *root;
812 TRACE("%s (last=%d)\n", name, check_last );
814 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
815 flags = DRIVE_GetFlags( full->drive );
817 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
818 sizeof(full->long_name) );
819 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
820 else root = full->long_name; /* root directory */
822 strcpy( full->short_name, "A:\\" );
823 full->short_name[0] += full->drive;
825 if ((*name == '\\') || (*name == '/')) /* Absolute path */
827 while ((*name == '\\') || (*name == '/')) name++;
829 else /* Relative path */
831 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
832 sizeof(full->long_name) - (root - full->long_name) - 1 );
833 if (root[1]) *root = '/';
834 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
835 sizeof(full->short_name) - 3 );
838 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
839 : full->long_name;
840 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
841 : full->short_name + 2;
842 found = TRUE;
844 while (*name && found)
846 /* Check for '.' and '..' */
848 if (*name == '.')
850 if (IS_END_OF_NAME(name[1]))
852 name++;
853 while ((*name == '\\') || (*name == '/')) name++;
854 continue;
856 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
858 name += 2;
859 while ((*name == '\\') || (*name == '/')) name++;
860 while ((p_l > root) && (*p_l != '/')) p_l--;
861 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
862 *p_l = *p_s = '\0'; /* Remove trailing separator */
863 continue;
867 /* Make sure buffers are large enough */
869 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
870 (p_l >= full->long_name + sizeof(full->long_name) - 1))
872 SetLastError( ERROR_PATH_NOT_FOUND );
873 return FALSE;
876 /* Get the long and short name matching the file name */
878 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
879 sizeof(full->long_name) - (p_l - full->long_name) - 1,
880 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
882 *p_l++ = '/';
883 p_l += strlen(p_l);
884 *p_s++ = '\\';
885 p_s += strlen(p_s);
886 while (!IS_END_OF_NAME(*name)) name++;
888 else if (!check_last)
890 *p_l++ = '/';
891 *p_s++ = '\\';
892 while (!IS_END_OF_NAME(*name) &&
893 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
894 (p_l < full->long_name + sizeof(full->long_name) - 1))
896 *p_s++ = tolower(*name);
897 /* If the drive is case-sensitive we want to create new */
898 /* files in lower-case otherwise we can't reopen them */
899 /* under the same short name. */
900 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
901 else *p_l++ = *name;
902 name++;
904 /* Ignore trailing dots and spaces */
905 while(p_l[-1] == '.' || p_l[-1] == ' ') {
906 --p_l;
907 --p_s;
909 *p_l = *p_s = '\0';
911 while ((*name == '\\') || (*name == '/')) name++;
914 if (!found)
916 if (check_last)
918 SetLastError( ERROR_FILE_NOT_FOUND );
919 return FALSE;
921 if (*name) /* Not last */
923 SetLastError( ERROR_PATH_NOT_FOUND );
924 return FALSE;
927 if (!full->long_name[0]) strcpy( full->long_name, "/" );
928 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
929 TRACE("returning %s = %s\n", full->long_name, full->short_name );
930 return TRUE;
934 /***********************************************************************
935 * GetShortPathNameA (KERNEL32.271)
937 * NOTES
938 * observed:
939 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
940 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
942 * more observations ( with NT 3.51 (WinDD) ):
943 * longpath <= 8.3 -> just copy longpath to shortpath
944 * longpath > 8.3 ->
945 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
946 * b) file does exist -> set the short filename.
947 * - trailing slashes are reproduced in the short name, even if the
948 * file is not a directory
949 * - the absolute/relative path of the short name is reproduced like found
950 * in the long name
951 * - longpath and shortpath may have the same adress
952 * Peter Ganten, 1999
954 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
955 DWORD shortlen )
957 DOS_FULL_NAME full_name;
958 LPSTR tmpshortpath;
959 DWORD sp = 0, lp = 0;
960 int tmplen, drive;
961 UINT flags;
963 TRACE("%s\n", debugstr_a(longpath));
965 if (!longpath) {
966 SetLastError(ERROR_INVALID_PARAMETER);
967 return 0;
969 if (!longpath[0]) {
970 SetLastError(ERROR_BAD_PATHNAME);
971 return 0;
974 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
975 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
976 return 0;
979 /* check for drive letter */
980 if ( longpath[1] == ':' ) {
981 tmpshortpath[0] = longpath[0];
982 tmpshortpath[1] = ':';
983 sp = 2;
986 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
987 flags = DRIVE_GetFlags ( drive );
989 while ( longpath[lp] ) {
991 /* check for path delimiters and reproduce them */
992 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
993 if (!sp || tmpshortpath[sp-1]!= '\\')
995 /* strip double "\\" */
996 tmpshortpath[sp] = '\\';
997 sp++;
999 tmpshortpath[sp]=0;/*terminate string*/
1000 lp++;
1001 continue;
1004 tmplen = strcspn ( longpath + lp, "\\/" );
1005 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1007 /* Check, if the current element is a valid dos name */
1008 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1009 sp += tmplen;
1010 lp += tmplen;
1011 continue;
1014 /* Check if the file exists and use the existing file name */
1015 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1016 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1017 sp += strlen ( tmpshortpath+sp );
1018 lp += tmplen;
1019 continue;
1022 TRACE("not found!\n" );
1023 SetLastError ( ERROR_FILE_NOT_FOUND );
1024 return 0;
1026 tmpshortpath[sp] = 0;
1028 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1029 TRACE("returning %s\n", debugstr_a(shortpath) );
1030 tmplen = strlen ( tmpshortpath );
1031 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1033 return tmplen;
1037 /***********************************************************************
1038 * GetShortPathNameW (KERNEL32.272)
1040 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1041 DWORD shortlen )
1043 LPSTR longpathA, shortpathA;
1044 DWORD ret = 0;
1046 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1047 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1049 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1050 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
1052 HeapFree( GetProcessHeap(), 0, longpathA );
1053 HeapFree( GetProcessHeap(), 0, shortpathA );
1055 return ret;
1059 /***********************************************************************
1060 * GetLongPathNameA (KERNEL32.xxx)
1062 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1063 DWORD longlen )
1065 DOS_FULL_NAME full_name;
1066 char *p, *r, *ll, *ss;
1068 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1069 lstrcpynA( longpath, full_name.short_name, longlen );
1071 /* Do some hackery to get the long filename. */
1073 if (longpath) {
1074 ss=longpath+strlen(longpath);
1075 ll=full_name.long_name+strlen(full_name.long_name);
1076 p=NULL;
1077 while (ss>=longpath)
1079 /* FIXME: aren't we more paranoid, than needed? */
1080 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1081 p=ss;
1082 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1083 if (ss>=longpath)
1085 /* FIXME: aren't we more paranoid, than needed? */
1086 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1087 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1088 if (ll<full_name.long_name)
1090 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1091 ,ss ,ll );
1092 return 0;
1097 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1098 if (p && p[2])
1100 p+=1;
1101 if ((p-longpath)>0) longlen -= (p-longpath);
1102 lstrcpynA( p, ll , longlen);
1104 /* Now, change all '/' to '\' */
1105 for (r=p; r<(p+longlen); r++ )
1106 if (r[0]=='/') r[0]='\\';
1107 return strlen(longpath) - strlen(p) + longlen;
1111 return strlen(longpath);
1115 /***********************************************************************
1116 * GetLongPathNameW (KERNEL32.269)
1118 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1119 DWORD longlen )
1121 DOS_FULL_NAME full_name;
1122 DWORD ret = 0;
1123 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1125 /* FIXME: is it correct to always return a fully qualified short path? */
1126 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1128 ret = strlen( full_name.short_name );
1129 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1131 HeapFree( GetProcessHeap(), 0, shortpathA );
1132 return ret;
1136 /***********************************************************************
1137 * DOSFS_DoGetFullPathName
1139 * Implementation of GetFullPathNameA/W.
1141 * bon@elektron 000331:
1142 * A test for GetFullPathName with many patholotical case
1143 * gives now identical output for Wine and OSR2
1145 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1146 BOOL unicode )
1148 DWORD ret;
1149 DOS_FULL_NAME full_name;
1150 char *p,*q;
1151 const char * root;
1152 char drivecur[]="c:.";
1153 char driveletter=0;
1154 int namelen,drive=0;
1156 if ((strlen(name) >1)&& (name[1]==':'))
1157 /*drive letter given */
1159 driveletter = name[0];
1161 if ((strlen(name) >2)&& (name[1]==':') &&
1162 ((name[2]=='\\') || (name[2]=='/')))
1163 /*absolute path given */
1165 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1166 drive = (int)toupper(name[0]) - 'A';
1168 else
1170 if (driveletter)
1171 drivecur[0]=driveletter;
1172 else
1173 strcpy(drivecur,".");
1174 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1176 FIXME("internal: error getting drive/path\n");
1177 return 0;
1179 /* find path that drive letter substitutes*/
1180 drive = (int)toupper(full_name.short_name[0]) -0x41;
1181 root= DRIVE_GetRoot(drive);
1182 if (!root)
1184 FIXME("internal: error getting DOS Drive Root\n");
1185 return 0;
1187 p= full_name.long_name +strlen(root);
1188 /* append long name (= unix name) to drive */
1189 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1190 /* append name to treat */
1191 namelen= strlen(full_name.short_name);
1192 p = (char*)name;
1193 if (driveletter)
1194 p += +2; /* skip drive name when appending */
1195 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1197 FIXME("internal error: buffer too small\n");
1198 return 0;
1200 full_name.short_name[namelen++] ='\\';
1201 full_name.short_name[namelen] = 0;
1202 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1204 /* reverse all slashes */
1205 for (p=full_name.short_name;
1206 p < full_name.short_name+strlen(full_name.short_name);
1207 p++)
1209 if ( *p == '/' )
1210 *p = '\\';
1212 /* Use memmove, as areas overlap*/
1213 /* Delete .. */
1214 while ((p = strstr(full_name.short_name,"\\..\\")))
1216 if (p > full_name.short_name+2)
1218 *p = 0;
1219 q = strrchr(full_name.short_name,'\\');
1220 memmove(q+1,p+4,strlen(p+4)+1);
1222 else
1224 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1227 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1229 /* This case istn't treated yet : c:..\test */
1230 memmove(full_name.short_name+2,full_name.short_name+4,
1231 strlen(full_name.short_name+4)+1);
1233 /* Delete . */
1234 while ((p = strstr(full_name.short_name,"\\.\\")))
1236 *(p+1) = 0;
1237 memmove(p+1,p+3,strlen(p+3)+1);
1239 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1240 _strupr( full_name.short_name );
1241 namelen=strlen(full_name.short_name);
1242 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1244 /* one more starnge case: "c:\test\test1\.."
1245 return "c:\test"*/
1246 *(full_name.short_name+namelen-3)=0;
1247 q = strrchr(full_name.short_name,'\\');
1248 *q =0;
1250 if (full_name.short_name[namelen-1]=='.')
1251 full_name.short_name[(namelen--)-1] =0;
1252 if (!driveletter)
1253 if (full_name.short_name[namelen-1]=='\\')
1254 full_name.short_name[(namelen--)-1] =0;
1255 TRACE("got %s\n",full_name.short_name);
1257 /* If the lpBuffer buffer is too small, the return value is the
1258 size of the buffer, in characters, required to hold the path
1259 plus the terminating \0 (tested against win95osr, bon 001118)
1260 . */
1261 ret = strlen(full_name.short_name);
1262 if (ret >= len )
1264 /* don't touch anything when the buffer is not large enough */
1265 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1266 return ret+1;
1268 if (result)
1270 if (unicode)
1271 lstrcpynAtoW( (LPWSTR)result, full_name.short_name, len );
1272 else
1273 lstrcpynA( result, full_name.short_name, len );
1276 TRACE("returning '%s'\n", full_name.short_name );
1277 return ret;
1281 /***********************************************************************
1282 * GetFullPathNameA (KERNEL32.272)
1283 * NOTES
1284 * if the path closed with '\', *lastpart is 0
1286 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1287 LPSTR *lastpart )
1289 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1290 if (ret && (ret<=len) && buffer && lastpart)
1292 LPSTR p = buffer + strlen(buffer);
1294 if (*p != '\\')
1296 while ((p > buffer + 2) && (*p != '\\')) p--;
1297 *lastpart = p + 1;
1299 else *lastpart = NULL;
1301 return ret;
1305 /***********************************************************************
1306 * GetFullPathNameW (KERNEL32.273)
1308 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1309 LPWSTR *lastpart )
1311 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1312 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1313 HeapFree( GetProcessHeap(), 0, nameA );
1314 if (ret && (ret<=len) && buffer && lastpart)
1316 LPWSTR p = buffer + strlenW(buffer);
1317 if (*p != (WCHAR)'\\')
1319 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1320 *lastpart = p + 1;
1322 else *lastpart = NULL;
1324 return ret;
1327 /***********************************************************************
1328 * DOSFS_FindNextEx
1330 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1332 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1333 UINT flags = DRIVE_GetFlags( info->drive );
1334 char *p, buffer[MAX_PATHNAME_LEN];
1335 const char *drive_path;
1336 int drive_root;
1337 LPCSTR long_name, short_name;
1338 BY_HANDLE_FILE_INFORMATION fileinfo;
1339 char dos_name[13];
1341 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1343 if (info->cur_pos) return 0;
1344 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1345 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1346 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1347 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1348 entry->nFileSizeHigh = 0;
1349 entry->nFileSizeLow = 0;
1350 entry->dwReserved0 = 0;
1351 entry->dwReserved1 = 0;
1352 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1353 strcpy( entry->cAlternateFileName, entry->cFileName );
1354 info->cur_pos++;
1355 TRACE("returning %s (%s) as label\n",
1356 entry->cFileName, entry->cAlternateFileName);
1357 return 1;
1360 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1361 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1362 drive_root = !*drive_path;
1364 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1365 strcat( buffer, "/" );
1366 p = buffer + strlen(buffer);
1368 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1370 info->cur_pos++;
1372 /* Don't return '.' and '..' in the root of the drive */
1373 if (drive_root && (long_name[0] == '.') &&
1374 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1375 continue;
1377 /* Check the long mask */
1379 if (info->long_mask)
1381 if (!DOSFS_MatchLong( info->long_mask, long_name,
1382 flags & DRIVE_CASE_SENSITIVE )) continue;
1385 /* Check the short mask */
1387 if (info->short_mask)
1389 if (!short_name)
1391 DOSFS_Hash( long_name, dos_name, TRUE,
1392 !(flags & DRIVE_CASE_SENSITIVE) );
1393 short_name = dos_name;
1395 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1398 /* Check the file attributes */
1400 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1401 if (!FILE_Stat( buffer, &fileinfo ))
1403 WARN("can't stat %s\n", buffer);
1404 continue;
1406 if (fileinfo.dwFileAttributes & ~attr) continue;
1408 /* We now have a matching entry; fill the result and return */
1410 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1411 entry->ftCreationTime = fileinfo.ftCreationTime;
1412 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1413 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1414 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1415 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1417 if (short_name)
1418 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1419 else
1420 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1421 !(flags & DRIVE_CASE_SENSITIVE) );
1423 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1424 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1425 TRACE("returning %s (%s) %02lx %ld\n",
1426 entry->cFileName, entry->cAlternateFileName,
1427 entry->dwFileAttributes, entry->nFileSizeLow );
1428 return 1;
1430 return 0; /* End of directory */
1433 /***********************************************************************
1434 * DOSFS_FindNext
1436 * Find the next matching file. Return the number of entries read to find
1437 * the matching one, or 0 if no more entries.
1438 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1439 * file name mask. Either or both can be NULL.
1441 * NOTE: This is supposed to be only called by the int21 emulation
1442 * routines. Thus, we should own the Win16Mutex anyway.
1443 * Nevertheless, we explicitly enter it to ensure the static
1444 * directory cache is protected.
1446 int DOSFS_FindNext( const char *path, const char *short_mask,
1447 const char *long_mask, int drive, BYTE attr,
1448 int skip, WIN32_FIND_DATAA *entry )
1450 static FIND_FIRST_INFO info = { NULL };
1451 LPCSTR short_name, long_name;
1452 int count;
1454 SYSLEVEL_EnterWin16Lock();
1456 /* Check the cached directory */
1457 if (!(info.dir && info.path == path && info.short_mask == short_mask
1458 && info.long_mask == long_mask && info.drive == drive
1459 && info.attr == attr && info.cur_pos <= skip))
1461 /* Not in the cache, open it anew */
1462 if (info.dir) DOSFS_CloseDir( info.dir );
1464 info.path = (LPSTR)path;
1465 info.long_mask = (LPSTR)long_mask;
1466 info.short_mask = (LPSTR)short_mask;
1467 info.attr = attr;
1468 info.drive = drive;
1469 info.cur_pos = 0;
1470 info.dir = DOSFS_OpenDir( info.path );
1473 /* Skip to desired position */
1474 while (info.cur_pos < skip)
1475 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1476 info.cur_pos++;
1477 else
1478 break;
1480 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1481 count = info.cur_pos - skip;
1482 else
1483 count = 0;
1485 if (!count)
1487 if (info.dir) DOSFS_CloseDir( info.dir );
1488 memset( &info, '\0', sizeof(info) );
1491 SYSLEVEL_LeaveWin16Lock();
1493 return count;
1496 /*************************************************************************
1497 * FindFirstFileExA (KERNEL32)
1499 HANDLE WINAPI FindFirstFileExA(
1500 LPCSTR lpFileName,
1501 FINDEX_INFO_LEVELS fInfoLevelId,
1502 LPVOID lpFindFileData,
1503 FINDEX_SEARCH_OPS fSearchOp,
1504 LPVOID lpSearchFilter,
1505 DWORD dwAdditionalFlags)
1507 DOS_FULL_NAME full_name;
1508 HGLOBAL handle;
1509 FIND_FIRST_INFO *info;
1511 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1513 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1514 return INVALID_HANDLE_VALUE;
1517 switch(fInfoLevelId)
1519 case FindExInfoStandard:
1521 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1522 data->dwReserved0 = data->dwReserved1 = 0x0;
1523 if (!lpFileName) return 0;
1524 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1525 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1526 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1527 info->path = HEAP_strdupA( GetProcessHeap(), 0, full_name.long_name );
1528 info->long_mask = strrchr( info->path, '/' );
1529 *(info->long_mask++) = '\0';
1530 info->short_mask = NULL;
1531 info->attr = 0xff;
1532 if (lpFileName[0] && (lpFileName[1] == ':'))
1533 info->drive = toupper(*lpFileName) - 'A';
1534 else info->drive = DRIVE_GetCurrentDrive();
1535 info->cur_pos = 0;
1537 info->dir = DOSFS_OpenDir( info->path );
1539 GlobalUnlock( handle );
1540 if (!FindNextFileA( handle, data ))
1542 FindClose( handle );
1543 SetLastError( ERROR_NO_MORE_FILES );
1544 break;
1546 return handle;
1548 break;
1549 default:
1550 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1552 return INVALID_HANDLE_VALUE;
1555 /*************************************************************************
1556 * FindFirstFileA (KERNEL32.123)
1558 HANDLE WINAPI FindFirstFileA(
1559 LPCSTR lpFileName,
1560 WIN32_FIND_DATAA *lpFindData )
1562 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1563 FindExSearchNameMatch, NULL, 0);
1566 /*************************************************************************
1567 * FindFirstFileExW (KERNEL32)
1569 HANDLE WINAPI FindFirstFileExW(
1570 LPCWSTR lpFileName,
1571 FINDEX_INFO_LEVELS fInfoLevelId,
1572 LPVOID lpFindFileData,
1573 FINDEX_SEARCH_OPS fSearchOp,
1574 LPVOID lpSearchFilter,
1575 DWORD dwAdditionalFlags)
1577 HANDLE handle;
1578 WIN32_FIND_DATAA dataA;
1579 LPVOID _lpFindFileData;
1580 LPSTR pathA;
1582 switch(fInfoLevelId)
1584 case FindExInfoStandard:
1586 _lpFindFileData = &dataA;
1588 break;
1589 default:
1590 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1591 return INVALID_HANDLE_VALUE;
1594 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1595 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1596 HeapFree( GetProcessHeap(), 0, pathA );
1597 if (handle == INVALID_HANDLE_VALUE) return handle;
1599 switch(fInfoLevelId)
1601 case FindExInfoStandard:
1603 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1604 dataW->dwFileAttributes = dataA.dwFileAttributes;
1605 dataW->ftCreationTime = dataA.ftCreationTime;
1606 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1607 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1608 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1609 dataW->nFileSizeLow = dataA.nFileSizeLow;
1610 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1611 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1612 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1613 dataW->cAlternateFileName,
1614 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1616 break;
1617 default:
1618 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1619 return INVALID_HANDLE_VALUE;
1621 return handle;
1624 /*************************************************************************
1625 * FindFirstFileW (KERNEL32.124)
1627 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1629 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1630 FindExSearchNameMatch, NULL, 0);
1633 /*************************************************************************
1634 * FindNextFileA (KERNEL32.126)
1636 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1638 FIND_FIRST_INFO *info;
1640 if ((handle == INVALID_HANDLE_VALUE) ||
1641 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1643 SetLastError( ERROR_INVALID_HANDLE );
1644 return FALSE;
1646 GlobalUnlock( handle );
1647 if (!info->path || !info->dir)
1649 SetLastError( ERROR_NO_MORE_FILES );
1650 return FALSE;
1652 if (!DOSFS_FindNextEx( info, data ))
1654 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1655 HeapFree( GetProcessHeap(), 0, info->path );
1656 info->path = info->long_mask = NULL;
1657 SetLastError( ERROR_NO_MORE_FILES );
1658 return FALSE;
1660 return TRUE;
1664 /*************************************************************************
1665 * FindNextFileW (KERNEL32.127)
1667 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1669 WIN32_FIND_DATAA dataA;
1670 if (!FindNextFileA( handle, &dataA )) return FALSE;
1671 data->dwFileAttributes = dataA.dwFileAttributes;
1672 data->ftCreationTime = dataA.ftCreationTime;
1673 data->ftLastAccessTime = dataA.ftLastAccessTime;
1674 data->ftLastWriteTime = dataA.ftLastWriteTime;
1675 data->nFileSizeHigh = dataA.nFileSizeHigh;
1676 data->nFileSizeLow = dataA.nFileSizeLow;
1677 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1678 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1679 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1680 data->cAlternateFileName,
1681 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1682 return TRUE;
1685 /*************************************************************************
1686 * FindClose (KERNEL32.119)
1688 BOOL WINAPI FindClose( HANDLE handle )
1690 FIND_FIRST_INFO *info;
1692 if ((handle == INVALID_HANDLE_VALUE) ||
1693 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1695 SetLastError( ERROR_INVALID_HANDLE );
1696 return FALSE;
1698 if (info->dir) DOSFS_CloseDir( info->dir );
1699 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1700 GlobalUnlock( handle );
1701 GlobalFree( handle );
1702 return TRUE;
1705 /***********************************************************************
1706 * DOSFS_UnixTimeToFileTime
1708 * Convert a Unix time to FILETIME format.
1709 * The FILETIME structure is a 64-bit value representing the number of
1710 * 100-nanosecond intervals since January 1, 1601, 0:00.
1711 * 'remainder' is the nonnegative number of 100-ns intervals
1712 * corresponding to the time fraction smaller than 1 second that
1713 * couldn't be stored in the time_t value.
1715 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1716 DWORD remainder )
1718 /* NOTES:
1720 CONSTANTS:
1721 The time difference between 1 January 1601, 00:00:00 and
1722 1 January 1970, 00:00:00 is 369 years, plus the leap years
1723 from 1604 to 1968, excluding 1700, 1800, 1900.
1724 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1725 of 134774 days.
1727 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1729 The time difference is 134774 * 86400 * 10000000, which can be written
1730 116444736000000000
1731 27111902 * 2^32 + 3577643008
1732 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1734 If you find that these constants are buggy, please change them in all
1735 instances in both conversion functions.
1737 VERSIONS:
1738 There are two versions, one of them uses long long variables and
1739 is presumably faster but not ISO C. The other one uses standard C
1740 data types and operations but relies on the assumption that negative
1741 numbers are stored as 2's complement (-1 is 0xffff....). If this
1742 assumption is violated, dates before 1970 will not convert correctly.
1743 This should however work on any reasonable architecture where WINE
1744 will run.
1746 DETAILS:
1748 Take care not to remove the casts. I have tested these functions
1749 (in both versions) for a lot of numbers. I would be interested in
1750 results on other compilers than GCC.
1752 The operations have been designed to account for the possibility
1753 of 64-bit time_t in future UNICES. Even the versions without
1754 internal long long numbers will work if time_t only is 64 bit.
1755 A 32-bit shift, which was necessary for that operation, turned out
1756 not to work correctly in GCC, besides giving the warning. So I
1757 used a double 16-bit shift instead. Numbers are in the ISO version
1758 represented by three limbs, the most significant with 32 bit, the
1759 other two with 16 bit each.
1761 As the modulo-operator % is not well-defined for negative numbers,
1762 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1764 There might be quicker ways to do this in C. Certainly so in
1765 assembler.
1767 Claus Fischer, fischer@iue.tuwien.ac.at
1770 #if SIZEOF_LONG_LONG >= 8
1771 # define USE_LONG_LONG 1
1772 #else
1773 # define USE_LONG_LONG 0
1774 #endif
1776 #if USE_LONG_LONG /* gcc supports long long type */
1778 long long int t = unix_time;
1779 t *= 10000000;
1780 t += 116444736000000000LL;
1781 t += remainder;
1782 filetime->dwLowDateTime = (UINT)t;
1783 filetime->dwHighDateTime = (UINT)(t >> 32);
1785 #else /* ISO version */
1787 UINT a0; /* 16 bit, low bits */
1788 UINT a1; /* 16 bit, medium bits */
1789 UINT a2; /* 32 bit, high bits */
1791 /* Copy the unix time to a2/a1/a0 */
1792 a0 = unix_time & 0xffff;
1793 a1 = (unix_time >> 16) & 0xffff;
1794 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1795 Do not replace this by >> 32, it gives a compiler warning and it does
1796 not work. */
1797 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1798 ~((~unix_time >> 16) >> 16));
1800 /* Multiply a by 10000000 (a = a2/a1/a0)
1801 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1802 a0 *= 10000;
1803 a1 = a1 * 10000 + (a0 >> 16);
1804 a2 = a2 * 10000 + (a1 >> 16);
1805 a0 &= 0xffff;
1806 a1 &= 0xffff;
1808 a0 *= 1000;
1809 a1 = a1 * 1000 + (a0 >> 16);
1810 a2 = a2 * 1000 + (a1 >> 16);
1811 a0 &= 0xffff;
1812 a1 &= 0xffff;
1814 /* Add the time difference and the remainder */
1815 a0 += 32768 + (remainder & 0xffff);
1816 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1817 a2 += 27111902 + (a1 >> 16);
1818 a0 &= 0xffff;
1819 a1 &= 0xffff;
1821 /* Set filetime */
1822 filetime->dwLowDateTime = (a1 << 16) + a0;
1823 filetime->dwHighDateTime = a2;
1824 #endif
1828 /***********************************************************************
1829 * DOSFS_FileTimeToUnixTime
1831 * Convert a FILETIME format to Unix time.
1832 * If not NULL, 'remainder' contains the fractional part of the filetime,
1833 * in the range of [0..9999999] (even if time_t is negative).
1835 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1837 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1838 #if USE_LONG_LONG
1840 long long int t = filetime->dwHighDateTime;
1841 t <<= 32;
1842 t += (UINT)filetime->dwLowDateTime;
1843 t -= 116444736000000000LL;
1844 if (t < 0)
1846 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1847 return -1 - ((-t - 1) / 10000000);
1849 else
1851 if (remainder) *remainder = t % 10000000;
1852 return t / 10000000;
1855 #else /* ISO version */
1857 UINT a0; /* 16 bit, low bits */
1858 UINT a1; /* 16 bit, medium bits */
1859 UINT a2; /* 32 bit, high bits */
1860 UINT r; /* remainder of division */
1861 unsigned int carry; /* carry bit for subtraction */
1862 int negative; /* whether a represents a negative value */
1864 /* Copy the time values to a2/a1/a0 */
1865 a2 = (UINT)filetime->dwHighDateTime;
1866 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1867 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1869 /* Subtract the time difference */
1870 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1871 else a0 += (1 << 16) - 32768 , carry = 1;
1873 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1874 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1876 a2 -= 27111902 + carry;
1878 /* If a is negative, replace a by (-1-a) */
1879 negative = (a2 >= ((UINT)1) << 31);
1880 if (negative)
1882 /* Set a to -a - 1 (a is a2/a1/a0) */
1883 a0 = 0xffff - a0;
1884 a1 = 0xffff - a1;
1885 a2 = ~a2;
1888 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1889 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1890 a1 += (a2 % 10000) << 16;
1891 a2 /= 10000;
1892 a0 += (a1 % 10000) << 16;
1893 a1 /= 10000;
1894 r = a0 % 10000;
1895 a0 /= 10000;
1897 a1 += (a2 % 1000) << 16;
1898 a2 /= 1000;
1899 a0 += (a1 % 1000) << 16;
1900 a1 /= 1000;
1901 r += (a0 % 1000) * 10000;
1902 a0 /= 1000;
1904 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1905 if (negative)
1907 /* Set a to -a - 1 (a is a2/a1/a0) */
1908 a0 = 0xffff - a0;
1909 a1 = 0xffff - a1;
1910 a2 = ~a2;
1912 r = 9999999 - r;
1915 if (remainder) *remainder = r;
1917 /* Do not replace this by << 32, it gives a compiler warning and it does
1918 not work. */
1919 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1920 #endif
1924 /***********************************************************************
1925 * MulDiv (KERNEL32.391)
1926 * RETURNS
1927 * Result of multiplication and division
1928 * -1: Overflow occurred or Divisor was 0
1930 INT WINAPI MulDiv(
1931 INT nMultiplicand,
1932 INT nMultiplier,
1933 INT nDivisor)
1935 #if SIZEOF_LONG_LONG >= 8
1936 long long ret;
1938 if (!nDivisor) return -1;
1940 /* We want to deal with a positive divisor to simplify the logic. */
1941 if (nDivisor < 0)
1943 nMultiplicand = - nMultiplicand;
1944 nDivisor = -nDivisor;
1947 /* If the result is positive, we "add" to round. else, we subtract to round. */
1948 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1949 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1950 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1951 else
1952 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1954 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
1955 return ret;
1956 #else
1957 if (!nDivisor) return -1;
1959 /* We want to deal with a positive divisor to simplify the logic. */
1960 if (nDivisor < 0)
1962 nMultiplicand = - nMultiplicand;
1963 nDivisor = -nDivisor;
1966 /* If the result is positive, we "add" to round. else, we subtract to round. */
1967 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1968 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1969 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1971 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1973 #endif
1977 /***********************************************************************
1978 * DosDateTimeToFileTime (KERNEL32.76)
1980 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1982 struct tm newtm;
1984 newtm.tm_sec = (fattime & 0x1f) * 2;
1985 newtm.tm_min = (fattime >> 5) & 0x3f;
1986 newtm.tm_hour = (fattime >> 11);
1987 newtm.tm_mday = (fatdate & 0x1f);
1988 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1989 newtm.tm_year = (fatdate >> 9) + 80;
1990 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
1991 return TRUE;
1995 /***********************************************************************
1996 * FileTimeToDosDateTime (KERNEL32.111)
1998 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1999 LPWORD fattime )
2001 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2002 struct tm *tm = localtime( &unixtime );
2003 if (fattime)
2004 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2005 if (fatdate)
2006 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2007 + tm->tm_mday;
2008 return TRUE;
2012 /***********************************************************************
2013 * LocalFileTimeToFileTime (KERNEL32.373)
2015 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2016 LPFILETIME utcft )
2018 struct tm *xtm;
2019 DWORD remainder;
2021 /* convert from local to UTC. Perhaps not correct. FIXME */
2022 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2023 xtm = gmtime( &unixtime );
2024 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2025 return TRUE;
2029 /***********************************************************************
2030 * FileTimeToLocalFileTime (KERNEL32.112)
2032 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2033 LPFILETIME localft )
2035 DWORD remainder;
2036 /* convert from UTC to local. Perhaps not correct. FIXME */
2037 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2038 #ifdef HAVE_TIMEGM
2039 struct tm *xtm = localtime( &unixtime );
2040 time_t localtime;
2042 localtime = timegm(xtm);
2043 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2045 #else
2046 struct tm *xtm,*gtm;
2047 time_t time1,time2;
2049 xtm = localtime( &unixtime );
2050 gtm = gmtime( &unixtime );
2051 time1 = mktime(xtm);
2052 time2 = mktime(gtm);
2053 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2054 #endif
2055 return TRUE;
2059 /***********************************************************************
2060 * FileTimeToSystemTime (KERNEL32.113)
2062 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2064 struct tm *xtm;
2065 DWORD remainder;
2066 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2067 xtm = gmtime(&xtime);
2068 syst->wYear = xtm->tm_year+1900;
2069 syst->wMonth = xtm->tm_mon + 1;
2070 syst->wDayOfWeek = xtm->tm_wday;
2071 syst->wDay = xtm->tm_mday;
2072 syst->wHour = xtm->tm_hour;
2073 syst->wMinute = xtm->tm_min;
2074 syst->wSecond = xtm->tm_sec;
2075 syst->wMilliseconds = remainder / 10000;
2076 return TRUE;
2079 /***********************************************************************
2080 * QueryDosDeviceA (KERNEL32.413)
2082 * returns array of strings terminated by \0, terminated by \0
2084 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2086 LPSTR s;
2087 char buffer[200];
2089 TRACE("(%s,...)\n", devname ? devname : "<null>");
2090 if (!devname) {
2091 /* return known MSDOS devices */
2092 strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
2093 while ((s=strchr(buffer,' ')))
2094 *s='\0';
2096 lstrcpynA(target,buffer,bufsize);
2097 return strlen(buffer);
2099 strcpy(buffer,"\\DEV\\");
2100 strcat(buffer,devname);
2101 if ((s=strchr(buffer,':'))) *s='\0';
2102 lstrcpynA(target,buffer,bufsize);
2103 return strlen(buffer);
2107 /***********************************************************************
2108 * QueryDosDeviceW (KERNEL32.414)
2110 * returns array of strings terminated by \0, terminated by \0
2112 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2114 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2115 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2116 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2118 lstrcpynAtoW(target,targetA,bufsize);
2119 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2120 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2121 return ret;
2125 /***********************************************************************
2126 * SystemTimeToFileTime (KERNEL32.526)
2128 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2130 #ifdef HAVE_TIMEGM
2131 struct tm xtm;
2132 time_t utctime;
2133 #else
2134 struct tm xtm,*local_tm,*utc_tm;
2135 time_t localtim,utctime;
2136 #endif
2138 xtm.tm_year = syst->wYear-1900;
2139 xtm.tm_mon = syst->wMonth - 1;
2140 xtm.tm_wday = syst->wDayOfWeek;
2141 xtm.tm_mday = syst->wDay;
2142 xtm.tm_hour = syst->wHour;
2143 xtm.tm_min = syst->wMinute;
2144 xtm.tm_sec = syst->wSecond; /* this is UTC */
2145 xtm.tm_isdst = -1;
2146 #ifdef HAVE_TIMEGM
2147 utctime = timegm(&xtm);
2148 DOSFS_UnixTimeToFileTime( utctime, ft,
2149 syst->wMilliseconds * 10000 );
2150 #else
2151 localtim = mktime(&xtm); /* now we've got local time */
2152 local_tm = localtime(&localtim);
2153 utc_tm = gmtime(&localtim);
2154 utctime = mktime(utc_tm);
2155 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2156 syst->wMilliseconds * 10000 );
2157 #endif
2158 return TRUE;
2161 /***********************************************************************
2162 * DefineDosDeviceA (KERNEL32.182)
2164 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2165 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2166 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2167 return FALSE;
2171 --- 16 bit functions ---
2174 /*************************************************************************
2175 * FindFirstFile16 (KERNEL.413)
2177 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2179 DOS_FULL_NAME full_name;
2180 HGLOBAL16 handle;
2181 FIND_FIRST_INFO *info;
2183 data->dwReserved0 = data->dwReserved1 = 0x0;
2184 if (!path) return 0;
2185 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2186 return INVALID_HANDLE_VALUE16;
2187 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2188 return INVALID_HANDLE_VALUE16;
2189 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2190 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
2191 info->long_mask = strrchr( info->path, '/' );
2192 if (info->long_mask )
2193 *(info->long_mask++) = '\0';
2194 info->short_mask = NULL;
2195 info->attr = 0xff;
2196 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
2197 else info->drive = DRIVE_GetCurrentDrive();
2198 info->cur_pos = 0;
2200 info->dir = DOSFS_OpenDir( info->path );
2202 GlobalUnlock16( handle );
2203 if (!FindNextFile16( handle, data ))
2205 FindClose16( handle );
2206 SetLastError( ERROR_NO_MORE_FILES );
2207 return INVALID_HANDLE_VALUE16;
2209 return handle;
2212 /*************************************************************************
2213 * FindNextFile16 (KERNEL.414)
2215 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2217 FIND_FIRST_INFO *info;
2219 if ((handle == INVALID_HANDLE_VALUE16) ||
2220 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2222 SetLastError( ERROR_INVALID_HANDLE );
2223 return FALSE;
2225 GlobalUnlock16( handle );
2226 if (!info->path || !info->dir)
2228 SetLastError( ERROR_NO_MORE_FILES );
2229 return FALSE;
2231 if (!DOSFS_FindNextEx( info, data ))
2233 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2234 HeapFree( SystemHeap, 0, info->path );
2235 info->path = info->long_mask = NULL;
2236 SetLastError( ERROR_NO_MORE_FILES );
2237 return FALSE;
2239 return TRUE;
2242 /*************************************************************************
2243 * FindClose16 (KERNEL.415)
2245 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2247 FIND_FIRST_INFO *info;
2249 if ((handle == INVALID_HANDLE_VALUE16) ||
2250 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2252 SetLastError( ERROR_INVALID_HANDLE );
2253 return FALSE;
2255 if (info->dir) DOSFS_CloseDir( info->dir );
2256 if (info->path) HeapFree( SystemHeap, 0, info->path );
2257 GlobalUnlock16( handle );
2258 GlobalFree16( handle );
2259 return TRUE;