blob: c79133a734aa699e127241a1207effb72ea9b18f [file] [log] [blame]
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001/*
2 * DOS file system functions
3 *
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
7
Alexandre Julliardc7c217b1998-04-13 12:21:30 +00008#include "config.h"
Patrik Stridvall33929be2001-07-18 21:04:23 +00009
Alexandre Julliard0c126c71996-02-18 18:44:41 +000010#include <sys/types.h>
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000011#include <ctype.h>
12#include <dirent.h>
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000013#include <errno.h>
Howard Abrams13277481999-07-10 13:16:29 +000014#ifdef HAVE_SYS_ERRNO_H
Alexandre Julliardd30dfd21998-09-27 18:28:36 +000015#include <sys/errno.h>
Howard Abrams13277481999-07-10 13:16:29 +000016#endif
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000017#include <fcntl.h>
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000018#include <string.h>
19#include <stdlib.h>
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000020#include <sys/stat.h>
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000021#include <sys/ioctl.h>
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000022#include <time.h>
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000023#include <unistd.h>
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000024
Marcus Meissner317af321999-02-17 13:51:06 +000025#include "windef.h"
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000026#include "winerror.h"
Patrik Stridvall33929be2001-07-18 21:04:23 +000027#include "wingdi.h"
28
29#include "wine/unicode.h"
30#include "wine/winbase16.h"
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000031#include "drive.h"
32#include "file.h"
Alexandre Julliard7ebe1a41996-12-22 18:27:48 +000033#include "heap.h"
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000034#include "msdos.h"
Patrik Stridvall33929be2001-07-18 21:04:23 +000035#include "ntddk.h"
Marcus Meissner6b9dd2e1999-03-18 17:39:57 +000036#include "options.h"
Alexandre Julliard37e95032001-07-19 00:39:09 +000037#include "wine/server.h"
Patrik Stridvall33929be2001-07-18 21:04:23 +000038
Alexandre Julliard06c275a1999-05-02 14:32:27 +000039#include "debugtools.h"
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000040
Alexandre Julliard5ab9d862000-08-09 22:35:05 +000041DEFAULT_DEBUG_CHANNEL(dosfs);
42DECLARE_DEBUG_CHANNEL(file);
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000043
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000044/* Define the VFAT ioctl to get both short and long file names */
Alexandre Julliardc6c09441997-01-12 18:32:19 +000045/* FIXME: is it possible to get this to work on other systems? */
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000046#ifdef linux
Alexandre Julliarddf2673b1997-03-29 17:20:20 +000047/* We want the real kernel dirent structure, not the libc one */
48typedef struct
49{
50 long d_ino;
51 long d_off;
52 unsigned short d_reclen;
53 char d_name[256];
54} KERNEL_DIRENT;
55
Peter Ganten0bac5e91999-09-27 11:46:27 +000056#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
57
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000058#else /* linux */
59#undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
60#endif /* linux */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000061
62/* Chars we don't want to see in DOS file names */
Alexandre Julliard7e56f681996-01-31 19:02:28 +000063#define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000064
Alexandre Julliard829fe321998-07-26 14:27:39 +000065static const DOS_DEVICE DOSFS_Devices[] =
66/* name, device flags (see Int 21/AX=0x4400) */
67{
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 }
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000082};
83
84#define GET_DRIVE(path) \
Aric Stewarte4d09322000-12-03 03:14:29 +000085 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +000086
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000087/* Directory info for DOSFS_ReadDir */
88typedef struct
89{
90 DIR *dir;
91#ifdef VFAT_IOCTL_READDIR_BOTH
92 int fd;
93 char short_name[12];
Alexandre Julliarddf2673b1997-03-29 17:20:20 +000094 KERNEL_DIRENT dirent[2];
Alexandre Julliard9ea19e51997-01-01 17:29:55 +000095#endif
96} DOS_DIR;
97
Alexandre Julliard85ed45e1998-08-22 19:03:56 +000098/* Info structure for FindFirstFile handle */
99typedef struct
100{
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;
109
110
Dominik Strasser4f46b5d2001-04-20 18:38:41 +0000111static WINE_EXCEPTION_FILTER(page_fault)
112{
113 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
114 return EXCEPTION_EXECUTE_HANDLER;
115 return EXCEPTION_CONTINUE_SEARCH;
116}
117
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000118
119/***********************************************************************
120 * DOSFS_ValidDOSName
121 *
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 '/'.
125 */
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000126static int DOSFS_ValidDOSName( const char *name, int ignore_case )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000127{
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000128 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000129 const char *p = name;
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000130 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000131 int len = 0;
132
133 if (*p == '.')
134 {
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));
140 }
141 while (!IS_END_OF_NAME(*p))
142 {
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000143 if (strchr( invalid, *p )) return 0; /* Invalid char */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000144 if (*p == '.') break; /* Start of the extension */
145 if (++len > 8) return 0; /* Name too long */
146 p++;
147 }
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))
153 {
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000154 if (strchr( invalid, *p )) return 0; /* Invalid char */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000155 if (*p == '.') return 0; /* Second extension not allowed */
156 if (++len > 3) return 0; /* Extension too long */
157 p++;
158 }
159 return 1;
160}
161
162
163/***********************************************************************
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000164 * DOSFS_ToDosFCBFormat
165 *
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 '/'.
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000169 * Return FALSE if the name is not a valid DOS name.
170 * 'buffer' must be at least 12 characters long.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000171 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000172BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000173{
174 static const char invalid_chars[] = INVALID_DOS_CHARS;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000175 const char *p = name;
176 int i;
177
178 /* Check for "." and ".." */
179 if (*p == '.')
180 {
181 p++;
182 strcpy( buffer, ". " );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000183 if (*p == '.')
184 {
185 buffer[1] = '.';
186 p++;
187 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000188 return (!*p || (*p == '/') || (*p == '\\'));
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000189 }
190
191 for (i = 0; i < 8; i++)
192 {
193 switch(*p)
194 {
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:
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000208 if (strchr( invalid_chars, *p )) return FALSE;
Aric Stewarte4d09322000-12-03 03:14:29 +0000209 buffer[i] = FILE_toupper(*p);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000210 p++;
211 break;
212 }
213 }
214
215 if (*p == '*')
216 {
217 /* Skip all chars after wildcard up to first dot */
218 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
219 }
220 else
221 {
222 /* Check if name too long */
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000223 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000224 }
225 if (*p == '.') p++; /* Skip dot */
226
227 for (i = 8; i < 11; i++)
228 {
229 switch(*p)
230 {
231 case '\0':
232 case '\\':
233 case '/':
234 buffer[i] = ' ';
235 break;
236 case '.':
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000237 return FALSE; /* Second extension not allowed */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000238 case '?':
239 p++;
240 /* fall through */
241 case '*':
242 buffer[i] = '?';
243 break;
244 default:
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000245 if (strchr( invalid_chars, *p )) return FALSE;
Aric Stewarte4d09322000-12-03 03:14:29 +0000246 buffer[i] = FILE_toupper(*p);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000247 p++;
248 break;
249 }
250 }
251 buffer[11] = '\0';
Stefan Leichter7cc51fa2000-03-26 20:25:59 +0000252
253 /* at most 3 character of the extension are processed
254 * is something behind this ?
255 */
Alexandre Julliard199aeba2000-03-28 13:20:32 +0000256 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
Stefan Leichter7cc51fa2000-03-26 20:25:59 +0000257 return IS_END_OF_NAME(*p);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000258}
259
260
261/***********************************************************************
262 * DOSFS_ToDosDTAFormat
263 *
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 '/'.
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000267 * 'buffer' must be at least 13 characters long.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000268 */
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000269static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000270{
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000271 char *p;
272
273 memcpy( buffer, name, 8 );
Alexandre Julliard566a52a2001-03-05 19:34:17 +0000274 p = buffer + 8;
275 while ((p > buffer) && (p[-1] == ' ')) p--;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000276 *p++ = '.';
277 memcpy( p, name + 8, 3 );
Alexandre Julliard566a52a2001-03-05 19:34:17 +0000278 p += 3;
279 while (p[-1] == ' ') p--;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000280 if (p[-1] == '.') p--;
281 *p = '\0';
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000282}
283
284
285/***********************************************************************
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000286 * DOSFS_MatchShort
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000287 *
288 * Check a DOS file name against a mask (both in FCB format).
289 */
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000290static int DOSFS_MatchShort( const char *mask, const char *name )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000291{
292 int i;
293 for (i = 11; i > 0; i--, mask++, name++)
294 if ((*mask != '?') && (*mask != *name)) return 0;
295 return 1;
296}
297
298
299/***********************************************************************
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000300 * DOSFS_MatchLong
301 *
302 * Check a long file name against a mask.
Andreas Mohr07291052000-09-07 21:03:02 +0000303 *
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 *
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000314 */
315static int DOSFS_MatchLong( const char *mask, const char *name,
316 int case_sensitive )
317{
Andreas Mohr07291052000-09-07 21:03:02 +0000318 const char *lastjoker = NULL;
319 const char *next_to_retry = NULL;
320
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000321 if (!strcmp( mask, "*.*" )) return 1;
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000322 while (*name && *mask)
323 {
324 if (*mask == '*')
325 {
326 mask++;
327 while (*mask == '*') mask++; /* Skip consecutive '*' */
Andreas Mohr07291052000-09-07 21:03:02 +0000328 lastjoker = mask;
329 if (!*mask) return 1; /* end of mask is all '*', so match */
330
331 /* skip to the next match after the joker(s) */
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000332 if (case_sensitive) while (*name && (*name != *mask)) name++;
Aric Stewarte4d09322000-12-03 03:14:29 +0000333 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
Andreas Mohr07291052000-09-07 21:03:02 +0000334
Robert W Hall9132a781999-04-18 14:38:17 +0000335 if (!*name) break;
Andreas Mohr07291052000-09-07 21:03:02 +0000336 next_to_retry = name;
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000337 }
338 else if (*mask != '?')
339 {
Andreas Mohr07291052000-09-07 21:03:02 +0000340 int mismatch = 0;
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000341 if (case_sensitive)
342 {
Andreas Mohr07291052000-09-07 21:03:02 +0000343 if (*mask != *name) mismatch = 1;
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000344 }
Andreas Mohr07291052000-09-07 21:03:02 +0000345 else
346 {
Aric Stewarte4d09322000-12-03 03:14:29 +0000347 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
Andreas Mohr07291052000-09-07 21:03:02 +0000348 }
349 if (!mismatch)
350 {
351 mask++;
352 name++;
353 if (*mask == '\0')
354 {
355 if (*name == '\0')
356 return 1;
357 if (lastjoker)
358 mask = lastjoker;
359 }
360 }
361 else /* mismatch ! */
362 {
363 if (lastjoker) /* we had an '*', so we can try unlimitedly */
364 {
365 mask = lastjoker;
366
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;
371 }
372 else
373 return 0; /* bad luck */
374 }
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000375 }
Andreas Mohr07291052000-09-07 21:03:02 +0000376 else /* '?' */
377 {
378 mask++;
379 name++;
380 }
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000381 }
Andreas Mohr07291052000-09-07 21:03:02 +0000382 while ((*mask == '.') || (*mask == '*'))
383 mask++; /* Ignore trailing '.' or '*' in mask */
Alexandre Julliard139a4b11996-11-02 14:24:07 +0000384 return (!*name && !*mask);
385}
386
387
388/***********************************************************************
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000389 * DOSFS_OpenDir
390 */
391static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
392{
Alexandre Julliard90476d62000-02-16 22:47:24 +0000393 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000394 if (!dir)
395 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +0000396 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000397 return NULL;
398 }
399
Alexandre Julliarda0d77311998-09-13 16:32:00 +0000400 /* Treat empty path as root directory. This simplifies path split into
401 directory and mask in several other places */
402 if (!*path) path = "/";
403
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000404#ifdef VFAT_IOCTL_READDIR_BOTH
405
406 /* Check if the VFAT ioctl is supported on this directory */
407
408 if ((dir->fd = open( path, O_RDONLY )) != -1)
409 {
410 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
411 {
412 close( dir->fd );
413 dir->fd = -1;
414 }
415 else
416 {
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;
421 }
422 }
423#endif /* VFAT_IOCTL_READDIR_BOTH */
424
425 /* Now use the standard opendir/readdir interface */
426
427 if (!(dir->dir = opendir( path )))
428 {
Alexandre Julliard90476d62000-02-16 22:47:24 +0000429 HeapFree( GetProcessHeap(), 0, dir );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000430 return NULL;
431 }
432 return dir;
433}
434
435
436/***********************************************************************
437 * DOSFS_CloseDir
438 */
439static void DOSFS_CloseDir( DOS_DIR *dir )
440{
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 );
Alexandre Julliard90476d62000-02-16 22:47:24 +0000445 HeapFree( GetProcessHeap(), 0, dir );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000446}
447
448
449/***********************************************************************
450 * DOSFS_ReadDir
451 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000452static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000453 LPCSTR *short_name )
454{
455 struct dirent *dirent;
456
457#ifdef VFAT_IOCTL_READDIR_BOTH
458 if (dir->fd != -1)
459 {
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000460 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;
468 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000469 }
470#endif /* VFAT_IOCTL_READDIR_BOTH */
471
472 if (!(dirent = readdir( dir->dir ))) return FALSE;
473 *long_name = dirent->d_name;
474 *short_name = NULL;
475 return TRUE;
476}
477
478
479/***********************************************************************
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000480 * DOSFS_Hash
481 *
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 '/'.
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000486 * 'buffer' must be at least 13 characters long.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000487 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000488static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
489 BOOL ignore_case )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000490{
491 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
492 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
493
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000494 const char *p, *ext;
495 char *dst;
496 unsigned short hash;
497 int i;
498
499 if (dir_format) strcpy( buffer, " " );
500
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000501 if (DOSFS_ValidDOSName( name, ignore_case ))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000502 {
503 /* Check for '.' and '..' */
504 if (*name == '.')
505 {
506 buffer[0] = '.';
507 if (!dir_format) buffer[1] = buffer[2] = '\0';
508 if (name[1] == '.') buffer[1] = '.';
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000509 return;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000510 }
511
512 /* Simply copy the name, converting to uppercase */
513
514 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
Aric Stewarte4d09322000-12-03 03:14:29 +0000515 *dst++ = FILE_toupper(*name);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000516 if (*name == '.')
517 {
518 if (dir_format) dst = buffer + 8;
519 else *dst++ = '.';
520 for (name++; !IS_END_OF_NAME(*name); name++)
Aric Stewarte4d09322000-12-03 03:14:29 +0000521 *dst++ = FILE_toupper(*name);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000522 }
523 if (!dir_format) *dst = '\0';
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000524 return;
525 }
526
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)
531 {
532 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
Aric Stewarte4d09322000-12-03 03:14:29 +0000533 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
534 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000535 }
536 else
537 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000538 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 */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000541 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000542
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 */
548
549 /* Copy first 4 chars, replacing invalid chars with '_' */
550 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
551 {
552 if (IS_END_OF_NAME(*p) || (p == ext)) break;
Aric Stewarte4d09322000-12-03 03:14:29 +0000553 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000554 }
555 /* Pad to 5 chars with '~' */
556 while (i-- >= 0) *dst++ = '~';
557
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];
562
563 /* Copy the first 3 chars of the extension (if any) */
564 if (ext)
565 {
566 if (!dir_format) *dst++ = '.';
567 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
Aric Stewarte4d09322000-12-03 03:14:29 +0000568 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000569 }
570 if (!dir_format) *dst = '\0';
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000571}
572
573
574/***********************************************************************
575 * DOSFS_FindUnixName
576 *
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 '/'.
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000580 * Return TRUE if OK, FALSE if no file name matches.
581 *
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.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000585 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000586BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
587 INT long_len, LPSTR short_buf, BOOL ignore_case)
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000588{
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000589 DOS_DIR *dir;
590 LPCSTR long_name, short_name;
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000591 char dos_name[12], tmp_buf[13];
Alexandre Julliarda3960291999-02-26 11:11:13 +0000592 BOOL ret;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000593
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000594 const char *p = strchr( name, '/' );
595 int len = p ? (int)(p - name) : strlen(name);
Francois Gouget6d77d3a2000-03-25 21:44:35 +0000596 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
Dave Hawkesbb9e66e2000-06-25 12:46:40 +0000597 /* Ignore trailing dots and spaces */
598 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000599 if (long_len < len + 1) return FALSE;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000600
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000601 TRACE("%s,%s\n", path, name );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000602
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000603 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000604
605 if (!(dir = DOSFS_OpenDir( path )))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000606 {
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000607 WARN("(%s,%s): can't open dir: %s\n",
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000608 path, name, strerror(errno) );
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000609 return FALSE;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000610 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000611
612 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000613 {
614 /* Check against Unix name */
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000615 if (len == strlen(long_name))
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000616 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000617 if (!ignore_case)
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000618 {
Alexandre Julliard89f079b1999-08-08 18:54:47 +0000619 if (!strncmp( long_name, name, len )) break;
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000620 }
621 else
622 {
Aric Stewarte4d09322000-12-03 03:14:29 +0000623 if (!FILE_strncasecmp( long_name, name, len )) break;
Alexandre Julliard1e37a181996-08-18 16:21:52 +0000624 }
625 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000626 if (dos_name[0])
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000627 {
628 /* Check against hashed DOS name */
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000629 if (!short_name)
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000630 {
631 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
632 short_name = tmp_buf;
633 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000634 if (!strcmp( dos_name, short_name )) break;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000635 }
636 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000637 if (ret)
638 {
639 if (long_buf) strcpy( long_buf, long_name );
640 if (short_buf)
641 {
642 if (short_name)
643 DOSFS_ToDosDTAFormat( short_name, short_buf );
644 else
645 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
646 }
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000647 TRACE("(%s,%s) -> %s (%s)\n",
648 path, name, long_name, short_buf ? short_buf : "***");
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000649 }
650 else
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000651 WARN("'%s' not found in '%s'\n", name, path);
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000652 DOSFS_CloseDir( dir );
653 return ret;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000654}
655
656
657/***********************************************************************
Alexandre Julliard829fe321998-07-26 14:27:39 +0000658 * DOSFS_GetDevice
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000659 *
Alexandre Julliard829fe321998-07-26 14:27:39 +0000660 * Check if a DOS file name represents a DOS device and return the device.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000661 */
Alexandre Julliard829fe321998-07-26 14:27:39 +0000662const DOS_DEVICE *DOSFS_GetDevice( const char *name )
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000663{
664 int i;
665 const char *p;
666
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000667 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000668 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++)
672 {
Alexandre Julliard829fe321998-07-26 14:27:39 +0000673 const char *dev = DOSFS_Devices[i].name;
Aric Stewarte4d09322000-12-03 03:14:29 +0000674 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000675 {
676 p = name + strlen( dev );
Lawson Whitneye3178f92000-12-27 03:28:13 +0000677 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000678 }
679 }
Alexandre Julliard829fe321998-07-26 14:27:39 +0000680 return NULL;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000681}
682
Alexandre Julliard62a8b431999-01-19 17:48:23 +0000683
684/***********************************************************************
685 * DOSFS_GetDeviceByHandle
686 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000687const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
Alexandre Julliard62a8b431999-01-19 17:48:23 +0000688{
Alexandre Julliard92643002000-08-31 01:59:51 +0000689 const DOS_DEVICE *ret = NULL;
Alexandre Julliard67a74992001-02-27 02:09:16 +0000690 SERVER_START_REQ( get_file_info )
Alexandre Julliard62a8b431999-01-19 17:48:23 +0000691 {
Alexandre Julliard92643002000-08-31 01:59:51 +0000692 req->handle = hFile;
Alexandre Julliard67a74992001-02-27 02:09:16 +0000693 if (!SERVER_CALL() && (req->type == FILE_TYPE_UNKNOWN))
Alexandre Julliard92643002000-08-31 01:59:51 +0000694 {
695 if ((req->attr >= 0) &&
696 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
697 ret = &DOSFS_Devices[req->attr];
698 }
Alexandre Julliard62a8b431999-01-19 17:48:23 +0000699 }
Alexandre Julliard92643002000-08-31 01:59:51 +0000700 SERVER_END_REQ;
701 return ret;
Alexandre Julliard62a8b431999-01-19 17:48:23 +0000702}
703
704
Mike McCormack11776c12000-10-13 17:11:30 +0000705/**************************************************************************
706 * DOSFS_CreateCommPort
707 */
Eric Pouech3bbeb722001-10-14 16:08:45 +0000708static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
Mike McCormack11776c12000-10-13 17:11:30 +0000709{
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000710 HANDLE ret;
Mike McCormack11776c12000-10-13 17:11:30 +0000711 char devname[40];
Alexandre Julliard67a74992001-02-27 02:09:16 +0000712 size_t len;
Mike McCormack11776c12000-10-13 17:11:30 +0000713
Mike McCormack568c67e2001-10-08 20:40:57 +0000714 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
Mike McCormack11776c12000-10-13 17:11:30 +0000715
716 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
717 if(!devname[0])
718 return 0;
719
720 TRACE("opening %s as %s\n", devname, name);
721
Alexandre Julliard67a74992001-02-27 02:09:16 +0000722 len = strlen(devname);
723 SERVER_START_VAR_REQ( create_serial, len )
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000724 {
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000725 req->access = access;
Eric Pouech3bbeb722001-10-14 16:08:45 +0000726 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
Mike McCormack568c67e2001-10-08 20:40:57 +0000727 req->attributes = attributes;
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000728 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
729 memcpy( server_data_ptr(req), devname, len );
730 SetLastError(0);
Alexandre Julliard67a74992001-02-27 02:09:16 +0000731 SERVER_CALL_ERR();
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000732 ret = req->handle;
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000733 }
Alexandre Julliard67a74992001-02-27 02:09:16 +0000734 SERVER_END_VAR_REQ;
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000735
Rein Klazes0a788572001-02-20 01:52:41 +0000736 if(!ret)
Andreas Mohre15badb2001-10-21 15:18:15 +0000737 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
Rein Klazes0a788572001-02-20 01:52:41 +0000738 else
739 TRACE("return %08X\n", ret );
Alexandre Julliard57f05e12000-10-15 00:40:25 +0000740 return ret;
Mike McCormack11776c12000-10-13 17:11:30 +0000741}
742
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000743/***********************************************************************
744 * DOSFS_OpenDevice
745 *
746 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000747 * Returns 0 on failure.
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000748 */
Eric Pouech3bbeb722001-10-14 16:08:45 +0000749HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000750{
751 int i;
752 const char *p;
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000753 HANDLE handle;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000754
Alexandre Julliard0c126c71996-02-18 18:44:41 +0000755 if (name[0] && (name[1] == ':')) name += 2;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000756 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++)
759 {
Alexandre Julliard829fe321998-07-26 14:27:39 +0000760 const char *dev = DOSFS_Devices[i].name;
Aric Stewarte4d09322000-12-03 03:14:29 +0000761 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000762 {
763 p = name + strlen( dev );
Lawson Whitneye3178f92000-12-27 03:28:13 +0000764 if (!*p || (*p == '.') || (*p == ':')) {
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000765 /* got it */
Alexandre Julliard829fe321998-07-26 14:27:39 +0000766 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
Alexandre Julliard05625391999-01-03 11:55:56 +0000767 return FILE_CreateFile( "/dev/null", access,
Eric Pouech3bbeb722001-10-14 16:08:45 +0000768 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
Ove Kaaven708a8462001-10-24 00:23:25 +0000769 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
Alexandre Julliard829fe321998-07-26 14:27:39 +0000770 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000771 HANDLE to_dup;
Alexandre Julliard05625391999-01-03 11:55:56 +0000772 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
773 case GENERIC_READ:
Alexandre Julliarda0d77311998-09-13 16:32:00 +0000774 to_dup = GetStdHandle( STD_INPUT_HANDLE );
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000775 break;
Alexandre Julliard05625391999-01-03 11:55:56 +0000776 case GENERIC_WRITE:
Alexandre Julliarda0d77311998-09-13 16:32:00 +0000777 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000778 break;
779 default:
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000780 FIXME("can't open CON read/write\n");
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000781 return 0;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000782 }
Alexandre Julliarda0d77311998-09-13 16:32:00 +0000783 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
Eric Pouech3bbeb722001-10-14 16:08:45 +0000784 &handle, 0,
785 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
786 DUPLICATE_SAME_ACCESS ))
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000787 handle = 0;
Alexandre Julliarda0d77311998-09-13 16:32:00 +0000788 return handle;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000789 }
Alexandre Julliardfbe63ad1998-12-30 12:10:06 +0000790 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
791 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
792 {
Eric Pouech3bbeb722001-10-14 16:08:45 +0000793 return FILE_CreateDevice( i, access, sa );
Alexandre Julliardf90efa91998-06-14 15:24:15 +0000794 }
Michael McCormacka8486071999-03-14 12:25:36 +0000795
Eric Pouech3bbeb722001-10-14 16:08:45 +0000796 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
Mike McCormack44b5bf52000-09-07 18:39:51 +0000797 return handle;
Rein Klazes0a788572001-02-20 01:52:41 +0000798 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000799 return 0;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000800 }
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000801 }
802 }
Alexandre Julliard8081e5a2001-01-05 04:08:07 +0000803 return 0;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000804}
805
Alexandre Julliarda11d7b11998-03-01 20:05:02 +0000806
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000807/***********************************************************************
808 * DOSFS_GetPathDrive
809 *
810 * Get the drive specified by a given path name (DOS or Unix format).
811 */
812static int DOSFS_GetPathDrive( const char **name )
813{
814 int drive;
815 const char *p = *name;
816
817 if (*p && (p[1] == ':'))
818 {
Aric Stewarte4d09322000-12-03 03:14:29 +0000819 drive = FILE_toupper(*p) - 'A';
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000820 *name += 2;
821 }
822 else if (*p == '/') /* Absolute Unix path? */
823 {
824 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
825 {
Andreas Mohr7bed6962001-09-19 22:34:38 +0000826 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000827 /* Assume it really was a DOS name */
828 drive = DRIVE_GetCurrentDrive();
829 }
830 }
831 else drive = DRIVE_GetCurrentDrive();
832
833 if (!DRIVE_IsValid(drive))
834 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +0000835 SetLastError( ERROR_INVALID_DRIVE );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000836 return -1;
837 }
838 return drive;
839}
840
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000841
842/***********************************************************************
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000843 * DOSFS_GetFullName
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000844 *
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000845 * 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
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000848 * component is only checked if 'check_last' is non-zero.
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000849 * The buffers pointed to by 'long_buf' and 'short_buf' must be
850 * at least MAX_PATHNAME_LEN long.
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000851 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000852BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000853{
Alexandre Julliarda3960291999-02-26 11:11:13 +0000854 BOOL found;
855 UINT flags;
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000856 char *p_l, *p_s, *root;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000857
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000858 TRACE("%s (last=%d)\n", name, check_last );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000859
Gerard Pateld52e1c42001-02-16 19:05:42 +0000860 if ((!*name) || (*name=='\n'))
861 { /* error code for Win98 */
862 SetLastError(ERROR_BAD_PATHNAME);
863 return FALSE;
864 }
865
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000866 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
867 flags = DRIVE_GetFlags( full->drive );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000868
Alexandre Julliarda3960291999-02-26 11:11:13 +0000869 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000870 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 */
873
874 strcpy( full->short_name, "A:\\" );
875 full->short_name[0] += full->drive;
876
877 if ((*name == '\\') || (*name == '/')) /* Absolute path */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000878 {
879 while ((*name == '\\') || (*name == '/')) name++;
880 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000881 else /* Relative path */
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000882 {
Alexandre Julliarda3960291999-02-26 11:11:13 +0000883 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000884 sizeof(full->long_name) - (root - full->long_name) - 1 );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000885 if (root[1]) *root = '/';
Alexandre Julliarda3960291999-02-26 11:11:13 +0000886 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000887 sizeof(full->short_name) - 3 );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000888 }
889
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000890 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;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +0000894 found = TRUE;
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000895
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000896 while (*name && found)
897 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000898 /* Check for '.' and '..' */
899
900 if (*name == '.')
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000901 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000902 if (IS_END_OF_NAME(name[1]))
903 {
904 name++;
905 while ((*name == '\\') || (*name == '/')) name++;
906 continue;
907 }
908 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
909 {
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;
916 }
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000917 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000918
919 /* Make sure buffers are large enough */
920
921 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
922 (p_l >= full->long_name + sizeof(full->long_name) - 1))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000923 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +0000924 SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000925 return FALSE;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000926 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000927
928 /* Get the long and short name matching the file name */
929
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) )))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000933 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000934 *p_l++ = '/';
935 p_l += strlen(p_l);
936 *p_s++ = '\\';
937 p_s += strlen(p_s);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000938 while (!IS_END_OF_NAME(*name)) name++;
939 }
Alexandre Julliard7e56f681996-01-31 19:02:28 +0000940 else if (!check_last)
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000941 {
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000942 *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))
947 {
Aric Stewarte4d09322000-12-03 03:14:29 +0000948 *p_s++ = FILE_tolower(*name);
Alexandre Julliard829fe321998-07-26 14:27:39 +0000949 /* 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. */
Aric Stewarte4d09322000-12-03 03:14:29 +0000952 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
Alexandre Julliard829fe321998-07-26 14:27:39 +0000953 else *p_l++ = *name;
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000954 name++;
955 }
Dave Hawkesbb9e66e2000-06-25 12:46:40 +0000956 /* Ignore trailing dots and spaces */
957 while(p_l[-1] == '.' || p_l[-1] == ' ') {
958 --p_l;
959 --p_s;
960 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000961 *p_l = *p_s = '\0';
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000962 }
963 while ((*name == '\\') || (*name == '/')) name++;
964 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000965
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000966 if (!found)
967 {
Alexander Larssona8745ea1998-12-02 10:04:52 +0000968 if (check_last)
969 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +0000970 SetLastError( ERROR_FILE_NOT_FOUND );
Alexander Larssona8745ea1998-12-02 10:04:52 +0000971 return FALSE;
972 }
Alexander Larsson2772a671998-12-07 16:23:42 +0000973 if (*name) /* Not last */
974 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +0000975 SetLastError( ERROR_PATH_NOT_FOUND );
Alexander Larsson2772a671998-12-07 16:23:42 +0000976 return FALSE;
977 }
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000978 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000979 if (!full->long_name[0]) strcpy( full->long_name, "/" );
980 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +0000981 TRACE("returning %s = %s\n", full->long_name, full->short_name );
Alexandre Julliardc6c09441997-01-12 18:32:19 +0000982 return TRUE;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +0000983}
984
985
986/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +0000987 * GetShortPathNameA (KERNEL32.@)
Juergen Schmied4658e4d1998-11-22 12:21:05 +0000988 *
989 * NOTES
990 * observed:
991 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
Francois Gouget60a83ef2001-09-14 00:59:58 +0000992 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
Peter Gantenf22bea01999-03-14 15:15:14 +0000993 *
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
Peter Gantend580b831999-12-12 20:44:07 +00001001 * - the absolute/relative path of the short name is reproduced like found
1002 * in the long name
Francois Gouget60a83ef2001-09-14 00:59:58 +00001003 * - longpath and shortpath may have the same address
Peter Gantenf22bea01999-03-14 15:15:14 +00001004 * Peter Ganten, 1999
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001005 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001006DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00001007 DWORD shortlen )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001008{
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001009 DOS_FULL_NAME full_name;
Peter Gantenf22bea01999-03-14 15:15:14 +00001010 LPSTR tmpshortpath;
Peter Gantend580b831999-12-12 20:44:07 +00001011 DWORD sp = 0, lp = 0;
1012 int tmplen, drive;
1013 UINT flags;
1014
Patrik Stridvall422a10a2000-02-26 13:15:51 +00001015 TRACE("%s\n", debugstr_a(longpath));
Peter Gantenf22bea01999-03-14 15:15:14 +00001016
1017 if (!longpath) {
1018 SetLastError(ERROR_INVALID_PARAMETER);
1019 return 0;
1020 }
1021 if (!longpath[0]) {
1022 SetLastError(ERROR_BAD_PATHNAME);
1023 return 0;
1024 }
1025
Peter Gantend580b831999-12-12 20:44:07 +00001026 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
Peter Gantenf22bea01999-03-14 15:15:14 +00001027 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1028 return 0;
1029 }
1030
Peter Gantend580b831999-12-12 20:44:07 +00001031 /* check for drive letter */
Peter Gantenf22bea01999-03-14 15:15:14 +00001032 if ( longpath[1] == ':' ) {
Peter Gantend580b831999-12-12 20:44:07 +00001033 tmpshortpath[0] = longpath[0];
1034 tmpshortpath[1] = ':';
1035 sp = 2;
Peter Gantenf22bea01999-03-14 15:15:14 +00001036 }
1037
Peter Gantend580b831999-12-12 20:44:07 +00001038 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1039 flags = DRIVE_GetFlags ( drive );
Peter Gantenf22bea01999-03-14 15:15:14 +00001040
Peter Gantend580b831999-12-12 20:44:07 +00001041 while ( longpath[lp] ) {
Peter Gantenf22bea01999-03-14 15:15:14 +00001042
Peter Gantend580b831999-12-12 20:44:07 +00001043 /* check for path delimiters and reproduce them */
1044 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
Uwe Bonnes61f572a2000-04-06 19:31:11 +00001045 if (!sp || tmpshortpath[sp-1]!= '\\')
1046 {
1047 /* strip double "\\" */
1048 tmpshortpath[sp] = '\\';
1049 sp++;
1050 }
Uwe Bonnes40249c62000-05-12 21:45:52 +00001051 tmpshortpath[sp]=0;/*terminate string*/
Peter Gantend580b831999-12-12 20:44:07 +00001052 lp++;
Peter Gantenf22bea01999-03-14 15:15:14 +00001053 continue;
1054 }
1055
Peter Gantend580b831999-12-12 20:44:07 +00001056 tmplen = strcspn ( longpath + lp, "\\/" );
1057 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1058
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;
Peter Gantenf22bea01999-03-14 15:15:14 +00001064 }
Peter Gantend580b831999-12-12 20:44:07 +00001065
1066 /* Check if the file exists and use the existing file name */
1067 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
Alexandre Julliarde101f6d2000-08-14 14:42:41 +00001068 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
Alexandre Julliardcb10fda2000-08-06 02:41:16 +00001069 sp += strlen ( tmpshortpath+sp );
Peter Gantend580b831999-12-12 20:44:07 +00001070 lp += tmplen;
1071 continue;
1072 }
1073
1074 TRACE("not found!\n" );
1075 SetLastError ( ERROR_FILE_NOT_FOUND );
1076 return 0;
Peter Gantenf22bea01999-03-14 15:15:14 +00001077 }
Uwe Bonnes61f572a2000-04-06 19:31:11 +00001078 tmpshortpath[sp] = 0;
Peter Gantend580b831999-12-12 20:44:07 +00001079
Peter Gantenf22bea01999-03-14 15:15:14 +00001080 lstrcpynA ( shortpath, tmpshortpath, shortlen );
Patrik Stridvall422a10a2000-02-26 13:15:51 +00001081 TRACE("returning %s\n", debugstr_a(shortpath) );
Alexandre Julliardcb10fda2000-08-06 02:41:16 +00001082 tmplen = strlen ( tmpshortpath );
Peter Gantenf22bea01999-03-14 15:15:14 +00001083 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1084
Peter Gantend580b831999-12-12 20:44:07 +00001085 return tmplen;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001086}
1087
1088
1089/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001090 * GetShortPathNameW (KERNEL32.@)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001091 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001092DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00001093 DWORD shortlen )
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001094{
Peter Gantenf22bea01999-03-14 15:15:14 +00001095 LPSTR longpathA, shortpathA;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001096 DWORD ret = 0;
Juergen Schmied4658e4d1998-11-22 12:21:05 +00001097
Juergen Schmied4658e4d1998-11-22 12:21:05 +00001098 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
Dimitrie O. Paunabdbced2000-04-29 14:20:28 +00001099 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001100
Peter Gantenf22bea01999-03-14 15:15:14 +00001101 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
Alexandre Julliard24a62ab2000-11-28 22:40:56 +00001102 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1103 shortpath[shortlen-1] = 0;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001104 HeapFree( GetProcessHeap(), 0, longpathA );
Peter Gantenf22bea01999-03-14 15:15:14 +00001105 HeapFree( GetProcessHeap(), 0, shortpathA );
1106
1107 return ret;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001108}
1109
1110
1111/***********************************************************************
Patrik Stridvall3ca98232001-06-20 23:03:14 +00001112 * GetLongPathNameA (KERNEL32.@)
Francois Gouget60a83ef2001-09-14 00:59:58 +00001113 *
1114 * NOTES
1115 * observed (Win2000):
1116 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1117 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
Alexandre Julliarde658d821997-11-30 17:45:40 +00001118 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001119DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
Alexandre Julliarde658d821997-11-30 17:45:40 +00001120 DWORD longlen )
1121{
1122 DOS_FULL_NAME full_name;
Petr Tomasek788a9f72000-02-20 19:14:17 +00001123 char *p, *r, *ll, *ss;
Francois Gouget60a83ef2001-09-14 00:59:58 +00001124
1125 if (!shortpath) {
1126 SetLastError(ERROR_INVALID_PARAMETER);
1127 return 0;
1128 }
1129 if (!shortpath[0]) {
1130 SetLastError(ERROR_PATH_NOT_FOUND);
1131 return 0;
1132 }
1133
Alexandre Julliarde658d821997-11-30 17:45:40 +00001134 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
Alexandre Julliarda3960291999-02-26 11:11:13 +00001135 lstrcpynA( longpath, full_name.short_name, longlen );
Petr Tomasek788a9f72000-02-20 19:14:17 +00001136
1137 /* Do some hackery to get the long filename. */
1138
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)
1144 {
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)
1150 {
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)
1155 {
1156 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1157 ,ss ,ll );
1158 return 0;
1159 }
1160 }
1161 }
1162
1163 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1164 if (p && p[2])
1165 {
1166 p+=1;
1167 if ((p-longpath)>0) longlen -= (p-longpath);
1168 lstrcpynA( p, ll , longlen);
1169
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;
1174 }
Alexander Larssonc1190fe1998-10-11 13:57:09 +00001175 }
Petr Tomasek788a9f72000-02-20 19:14:17 +00001176
1177 return strlen(longpath);
Alexandre Julliarde658d821997-11-30 17:45:40 +00001178}
1179
1180
1181/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001182 * GetLongPathNameW (KERNEL32.@)
Alexandre Julliarde658d821997-11-30 17:45:40 +00001183 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001184DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
Alexandre Julliarde658d821997-11-30 17:45:40 +00001185 DWORD longlen )
1186{
1187 DOS_FULL_NAME full_name;
1188 DWORD ret = 0;
1189 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1190
1191 /* FIXME: is it correct to always return a fully qualified short path? */
1192 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1193 {
1194 ret = strlen( full_name.short_name );
Alexandre Julliard24a62ab2000-11-28 22:40:56 +00001195 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1196 longpath, longlen ))
1197 longpath[longlen-1] = 0;
Alexandre Julliarde658d821997-11-30 17:45:40 +00001198 }
1199 HeapFree( GetProcessHeap(), 0, shortpathA );
1200 return ret;
1201}
1202
1203
1204/***********************************************************************
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001205 * DOSFS_DoGetFullPathName
1206 *
Patrik Stridvall2d6457c2000-03-28 20:22:59 +00001207 * Implementation of GetFullPathNameA/W.
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001208 *
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001209 * bon@elektron 000331:
Huw D M Daviesd0f8bfd2000-12-15 20:54:01 +00001210 * A test for GetFullPathName with many pathological cases
1211 * now gives identical output for Wine and OSR2
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001212 */
1213static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
Alexandre Julliarda3960291999-02-26 11:11:13 +00001214 BOOL unicode )
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001215{
Juergen Schmied86407161999-01-03 16:12:01 +00001216 DWORD ret;
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001217 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;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001223
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001224 if ((strlen(name) >1)&& (name[1]==':'))
1225 /*drive letter given */
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001226 {
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001227 driveletter = name[0];
1228 }
1229 if ((strlen(name) >2)&& (name[1]==':') &&
1230 ((name[2]=='\\') || (name[2]=='/')))
Uwe Bonnes594a0dc2000-06-15 00:30:26 +00001231 /*absolute path given */
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001232 {
1233 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
Aric Stewarte4d09322000-12-03 03:14:29 +00001234 drive = (int)FILE_toupper(name[0]) - 'A';
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001235 }
1236 else
1237 {
1238 if (driveletter)
1239 drivecur[0]=driveletter;
1240 else
1241 strcpy(drivecur,".");
1242 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1243 {
1244 FIXME("internal: error getting drive/path\n");
1245 return 0;
1246 }
1247 /* find path that drive letter substitutes*/
Aric Stewarte4d09322000-12-03 03:14:29 +00001248 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001249 root= DRIVE_GetRoot(drive);
Uwe Bonnes594a0dc2000-06-15 00:30:26 +00001250 if (!root)
1251 {
1252 FIXME("internal: error getting DOS Drive Root\n");
1253 return 0;
1254 }
Marcus Meissner7f0490e2000-10-23 00:37:06 +00001255 if (!strcmp(root,"/"))
1256 {
1257 /* we have just the last / and we need it. */
1258 p= full_name.long_name;
1259 }
1260 else
1261 {
1262 p= full_name.long_name +strlen(root);
1263 }
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001264 /* 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)
1272 {
1273 FIXME("internal error: buffer too small\n");
1274 return 0;
1275 }
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);
1279 }
1280 /* reverse all slashes */
1281 for (p=full_name.short_name;
1282 p < full_name.short_name+strlen(full_name.short_name);
1283 p++)
1284 {
1285 if ( *p == '/' )
1286 *p = '\\';
1287 }
1288 /* Use memmove, as areas overlap*/
1289 /* Delete .. */
1290 while ((p = strstr(full_name.short_name,"\\..\\")))
1291 {
1292 if (p > full_name.short_name+2)
1293 {
1294 *p = 0;
1295 q = strrchr(full_name.short_name,'\\');
1296 memmove(q+1,p+4,strlen(p+4)+1);
1297 }
1298 else
1299 {
1300 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1301 }
1302 }
1303 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1304 {
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);
1308 }
1309 /* Delete . */
1310 while ((p = strstr(full_name.short_name,"\\.\\")))
1311 {
1312 *(p+1) = 0;
Uwe Bonnesed343a22000-05-19 03:37:53 +00001313 memmove(p+1,p+3,strlen(p+3)+1);
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001314 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001315 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
Aric Stewarte4d09322000-12-03 03:14:29 +00001316 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001317 namelen=strlen(full_name.short_name);
1318 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1319 {
1320 /* one more starnge 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;
1325 }
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);
1332
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001333 /* 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 win95osr, bon 001118)
1336 . */
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001337 ret = strlen(full_name.short_name);
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001338 if (ret >= len )
1339 {
1340 /* don't touch anything when the buffer is not large enough */
1341 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1342 return ret+1;
1343 }
Alexandre Julliard08b9b4f1999-06-06 17:09:21 +00001344 if (result)
1345 {
1346 if (unicode)
Alexandre Julliard24a62ab2000-11-28 22:40:56 +00001347 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
Alexandre Julliard08b9b4f1999-06-06 17:09:21 +00001348 else
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001349 lstrcpynA( result, full_name.short_name, len );
Alexandre Julliard08b9b4f1999-06-06 17:09:21 +00001350 }
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001351
Uwe Bonnesd3b890f2000-04-28 20:48:54 +00001352 TRACE("returning '%s'\n", full_name.short_name );
Juergen Schmied86407161999-01-03 16:12:01 +00001353 return ret;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001354}
1355
1356
1357/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001358 * GetFullPathNameA (KERNEL32.@)
Juergen Schmied86407161999-01-03 16:12:01 +00001359 * NOTES
1360 * if the path closed with '\', *lastpart is 0
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001361 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001362DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00001363 LPSTR *lastpart )
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001364{
1365 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001366 if (ret && (ret<=len) && buffer && lastpart)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001367 {
1368 LPSTR p = buffer + strlen(buffer);
Juergen Schmied30f503f1998-12-15 17:28:26 +00001369
Juergen Schmied30f503f1998-12-15 17:28:26 +00001370 if (*p != '\\')
1371 {
1372 while ((p > buffer + 2) && (*p != '\\')) p--;
1373 *lastpart = p + 1;
1374 }
1375 else *lastpart = NULL;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001376 }
1377 return ret;
1378}
1379
1380
1381/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001382 * GetFullPathNameW (KERNEL32.@)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001383 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001384DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00001385 LPWSTR *lastpart )
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001386{
1387 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1388 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1389 HeapFree( GetProcessHeap(), 0, nameA );
Uwe Bonnes59b5f782000-02-27 13:58:12 +00001390 if (ret && (ret<=len) && buffer && lastpart)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001391 {
Alexandre Julliarde101f6d2000-08-14 14:42:41 +00001392 LPWSTR p = buffer + strlenW(buffer);
Juergen Schmied86407161999-01-03 16:12:01 +00001393 if (*p != (WCHAR)'\\')
1394 {
1395 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1396 *lastpart = p + 1;
1397 }
1398 else *lastpart = NULL;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001399 }
1400 return ret;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001401}
1402
Alexandre Julliard3a0f8b72000-12-01 20:48:41 +00001403
1404/***********************************************************************
Patrik Stridvall044855c2001-07-11 18:56:41 +00001405 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
Alexandre Julliard3a0f8b72000-12-01 20:48:41 +00001406 *
1407 * Return the full Unix file name for a given path.
1408 */
1409BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1410{
1411 BOOL ret;
1412 DOS_FULL_NAME path;
Alexandre Julliard3879b192001-01-17 01:55:04 +00001413 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
Alexandre Julliard3a0f8b72000-12-01 20:48:41 +00001414 return ret;
1415}
1416
1417
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001418/***********************************************************************
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001419 * DOSFS_FindNextEx
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001420 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001421static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001422{
Andreas Mohr220312e2000-10-19 20:38:38 +00001423 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
Alexandre Julliarda3960291999-02-26 11:11:13 +00001424 UINT flags = DRIVE_GetFlags( info->drive );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001425 char *p, buffer[MAX_PATHNAME_LEN];
1426 const char *drive_path;
1427 int drive_root;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001428 LPCSTR long_name, short_name;
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001429 BY_HANDLE_FILE_INFORMATION fileinfo;
1430 char dos_name[13];
Alexandre Julliard1e37a181996-08-18 16:21:52 +00001431
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001432 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001433 {
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001434 if (info->cur_pos) return 0;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001435 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
Alexandre Julliarde101f6d2000-08-14 14:42:41 +00001436 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1437 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1438 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001439 entry->nFileSizeHigh = 0;
1440 entry->nFileSizeLow = 0;
1441 entry->dwReserved0 = 0;
1442 entry->dwReserved1 = 0;
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001443 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1444 strcpy( entry->cAlternateFileName, entry->cFileName );
1445 info->cur_pos++;
Stefan Leichtereb0ab1b2000-08-18 23:45:46 +00001446 TRACE("returning %s (%s) as label\n",
1447 entry->cFileName, entry->cAlternateFileName);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001448 return 1;
1449 }
1450
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001451 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1452 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1453 drive_root = !*drive_path;
1454
Alexandre Julliarda3960291999-02-26 11:11:13 +00001455 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001456 strcat( buffer, "/" );
1457 p = buffer + strlen(buffer);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001458
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001459 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001460 {
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001461 info->cur_pos++;
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001462
Alexandre Julliard0c126c71996-02-18 18:44:41 +00001463 /* Don't return '.' and '..' in the root of the drive */
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001464 if (drive_root && (long_name[0] == '.') &&
1465 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1466 continue;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001467
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001468 /* Check the long mask */
1469
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001470 if (info->long_mask)
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001471 {
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001472 if (!DOSFS_MatchLong( info->long_mask, long_name,
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001473 flags & DRIVE_CASE_SENSITIVE )) continue;
1474 }
1475
1476 /* Check the short mask */
1477
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001478 if (info->short_mask)
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001479 {
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001480 if (!short_name)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001481 {
1482 DOSFS_Hash( long_name, dos_name, TRUE,
1483 !(flags & DRIVE_CASE_SENSITIVE) );
1484 short_name = dos_name;
1485 }
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001486 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001487 }
1488
1489 /* Check the file attributes */
1490
Alexandre Julliarda3960291999-02-26 11:11:13 +00001491 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001492 if (!FILE_Stat( buffer, &fileinfo ))
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001493 {
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +00001494 WARN("can't stat %s\n", buffer);
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001495 continue;
1496 }
Andreas Mohr220312e2000-10-19 20:38:38 +00001497 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1498 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1499 {
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;
1504 }
1505
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001506 if (fileinfo.dwFileAttributes & ~attr) continue;
Alexandre Julliard139a4b11996-11-02 14:24:07 +00001507
1508 /* We now have a matching entry; fill the result and return */
1509
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001510 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;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001516
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) );
1522
Alexandre Julliarda3960291999-02-26 11:11:13 +00001523 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
Alexandre Julliarddcd247e2000-08-14 17:39:15 +00001524 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +00001525 TRACE("returning %s (%s) %02lx %ld\n",
1526 entry->cFileName, entry->cAlternateFileName,
1527 entry->dwFileAttributes, entry->nFileSizeLow );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001528 return 1;
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001529 }
Alexandre Julliard4f8c37b1996-01-14 18:12:01 +00001530 return 0; /* End of directory */
1531}
Alexandre Julliardca22b331996-07-12 19:02:39 +00001532
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001533/***********************************************************************
1534 * DOSFS_FindNext
1535 *
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.
1540 *
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.
1545 */
1546int DOSFS_FindNext( const char *path, const char *short_mask,
1547 const char *long_mask, int drive, BYTE attr,
Alexandre Julliarda3960291999-02-26 11:11:13 +00001548 int skip, WIN32_FIND_DATAA *entry )
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001549{
Joerg Mayer959d73e2000-10-22 23:56:32 +00001550 static FIND_FIRST_INFO info;
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001551 LPCSTR short_name, long_name;
1552 int count;
1553
Alexandre Julliardab687972000-11-15 23:41:46 +00001554 _EnterWin16Lock();
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001555
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))
1560 {
1561 /* Not in the cache, open it anew */
1562 if (info.dir) DOSFS_CloseDir( info.dir );
1563
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 );
1571 }
1572
1573 /* Skip to desired position */
1574 while (info.cur_pos < skip)
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001575 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001576 info.cur_pos++;
1577 else
1578 break;
1579
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001580 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001581 count = info.cur_pos - skip;
1582 else
1583 count = 0;
1584
1585 if (!count)
1586 {
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001587 if (info.dir) DOSFS_CloseDir( info.dir );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001588 memset( &info, '\0', sizeof(info) );
1589 }
1590
Alexandre Julliardab687972000-11-15 23:41:46 +00001591 _LeaveWin16Lock();
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001592
1593 return count;
1594}
1595
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001596/*************************************************************************
Patrik Stridvall3ca98232001-06-20 23:03:14 +00001597 * FindFirstFileExA (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001598 */
Juergen Schmied2250f122000-06-01 23:17:42 +00001599HANDLE WINAPI FindFirstFileExA(
1600 LPCSTR lpFileName,
1601 FINDEX_INFO_LEVELS fInfoLevelId,
1602 LPVOID lpFindFileData,
1603 FINDEX_SEARCH_OPS fSearchOp,
1604 LPVOID lpSearchFilter,
1605 DWORD dwAdditionalFlags)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001606{
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001607 DOS_FULL_NAME full_name;
Juergen Schmied2250f122000-06-01 23:17:42 +00001608 HGLOBAL handle;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001609 FIND_FIRST_INFO *info;
Juergen Schmied2250f122000-06-01 23:17:42 +00001610
1611 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001612 {
Juergen Schmied2250f122000-06-01 23:17:42 +00001613 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1614 return INVALID_HANDLE_VALUE;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001615 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001616
Juergen Schmied2250f122000-06-01 23:17:42 +00001617 switch(fInfoLevelId)
1618 {
1619 case FindExInfoStandard:
1620 {
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 );
Alexandre Julliard5f728ca2001-07-24 21:45:22 +00001627 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1628 strcpy( info->path, full_name.long_name );
Juergen Schmied2250f122000-06-01 23:17:42 +00001629 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] == ':'))
Aric Stewarte4d09322000-12-03 03:14:29 +00001634 info->drive = FILE_toupper(*lpFileName) - 'A';
Juergen Schmied2250f122000-06-01 23:17:42 +00001635 else info->drive = DRIVE_GetCurrentDrive();
1636 info->cur_pos = 0;
1637
1638 info->dir = DOSFS_OpenDir( info->path );
1639
1640 GlobalUnlock( handle );
1641 if (!FindNextFileA( handle, data ))
1642 {
1643 FindClose( handle );
1644 SetLastError( ERROR_NO_MORE_FILES );
1645 break;
1646 }
1647 return handle;
1648 }
1649 break;
1650 default:
1651 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1652 }
1653 return INVALID_HANDLE_VALUE;
1654}
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001655
1656/*************************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001657 * FindFirstFileA (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001658 */
Juergen Schmied2250f122000-06-01 23:17:42 +00001659HANDLE WINAPI FindFirstFileA(
1660 LPCSTR lpFileName,
1661 WIN32_FIND_DATAA *lpFindData )
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001662{
Juergen Schmied2250f122000-06-01 23:17:42 +00001663 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1664 FindExSearchNameMatch, NULL, 0);
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001665}
1666
Juergen Schmied2250f122000-06-01 23:17:42 +00001667/*************************************************************************
Patrik Stridvall3ca98232001-06-20 23:03:14 +00001668 * FindFirstFileExW (KERNEL32.@)
Juergen Schmied2250f122000-06-01 23:17:42 +00001669 */
1670HANDLE WINAPI FindFirstFileExW(
1671 LPCWSTR lpFileName,
1672 FINDEX_INFO_LEVELS fInfoLevelId,
1673 LPVOID lpFindFileData,
1674 FINDEX_SEARCH_OPS fSearchOp,
1675 LPVOID lpSearchFilter,
1676 DWORD dwAdditionalFlags)
1677{
1678 HANDLE handle;
1679 WIN32_FIND_DATAA dataA;
1680 LPVOID _lpFindFileData;
1681 LPSTR pathA;
1682
1683 switch(fInfoLevelId)
1684 {
1685 case FindExInfoStandard:
1686 {
1687 _lpFindFileData = &dataA;
1688 }
1689 break;
1690 default:
1691 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1692 return INVALID_HANDLE_VALUE;
1693 }
1694
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;
1699
1700 switch(fInfoLevelId)
1701 {
1702 case FindExInfoStandard:
1703 {
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;
Alexandre Julliard072dfb52000-09-25 23:30:56 +00001711 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) );
Juergen Schmied2250f122000-06-01 23:17:42 +00001716 }
1717 break;
1718 default:
1719 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1720 return INVALID_HANDLE_VALUE;
1721 }
1722 return handle;
1723}
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001724
1725/*************************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001726 * FindFirstFileW (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001727 */
Juergen Schmied2250f122000-06-01 23:17:42 +00001728HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001729{
Juergen Schmied2250f122000-06-01 23:17:42 +00001730 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1731 FindExSearchNameMatch, NULL, 0);
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001732}
1733
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001734/*************************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001735 * FindNextFileA (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001736 */
Juergen Schmied2250f122000-06-01 23:17:42 +00001737BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001738{
1739 FIND_FIRST_INFO *info;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001740
Juergen Schmied2250f122000-06-01 23:17:42 +00001741 if ((handle == INVALID_HANDLE_VALUE) ||
1742 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001743 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +00001744 SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001745 return FALSE;
1746 }
Juergen Schmied2250f122000-06-01 23:17:42 +00001747 GlobalUnlock( handle );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001748 if (!info->path || !info->dir)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001749 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +00001750 SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001751 return FALSE;
1752 }
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001753 if (!DOSFS_FindNextEx( info, data ))
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001754 {
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001755 DOSFS_CloseDir( info->dir ); info->dir = NULL;
Alexandre Julliard90476d62000-02-16 22:47:24 +00001756 HeapFree( GetProcessHeap(), 0, info->path );
Alexandre Julliard85ed45e1998-08-22 19:03:56 +00001757 info->path = info->long_mask = NULL;
Alexandre Julliard4ff2a271999-01-31 15:23:45 +00001758 SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001759 return FALSE;
1760 }
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001761 return TRUE;
1762}
1763
1764
1765/*************************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001766 * FindNextFileW (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001767 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001768BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001769{
Alexandre Julliarda3960291999-02-26 11:11:13 +00001770 WIN32_FIND_DATAA dataA;
1771 if (!FindNextFileA( handle, &dataA )) return FALSE;
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001772 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;
Alexandre Julliard072dfb52000-09-25 23:30:56 +00001778 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) );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001783 return TRUE;
1784}
1785
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001786/*************************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00001787 * FindClose (KERNEL32.@)
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001788 */
Juergen Schmied2250f122000-06-01 23:17:42 +00001789BOOL WINAPI FindClose( HANDLE handle )
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001790{
1791 FIND_FIRST_INFO *info;
1792
Juergen Schmied2250f122000-06-01 23:17:42 +00001793 if ((handle == INVALID_HANDLE_VALUE) ||
1794 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001795 {
Alexandre Julliard4ff2a271999-01-31 15:23:45 +00001796 SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001797 return FALSE;
1798 }
Dominik Strasser4f46b5d2001-04-20 18:38:41 +00001799 __TRY
1800 {
1801 if (info->dir) DOSFS_CloseDir( info->dir );
1802 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1803 }
1804 __EXCEPT(page_fault)
1805 {
1806 WARN("Illegal handle %x\n", handle);
1807 SetLastError( ERROR_INVALID_HANDLE );
1808 return FALSE;
1809 }
1810 __ENDTRY
Juergen Schmied2250f122000-06-01 23:17:42 +00001811 GlobalUnlock( handle );
1812 GlobalFree( handle );
Alexandre Julliard9ea19e51997-01-01 17:29:55 +00001813 return TRUE;
1814}
1815
Alexandre Julliardca22b331996-07-12 19:02:39 +00001816/***********************************************************************
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001817 * DOSFS_UnixTimeToFileTime
1818 *
1819 * Convert a Unix time to FILETIME format.
1820 * The FILETIME structure is a 64-bit value representing the number of
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001821 * 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.
Alexandre Julliardca22b331996-07-12 19:02:39 +00001825 */
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001826void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1827 DWORD remainder )
Alexandre Julliardca22b331996-07-12 19:02:39 +00001828{
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001829 /* NOTES:
1830
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.
1837
1838 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1839
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
1844
1845 If you find that these constants are buggy, please change them in all
1846 instances in both conversion functions.
1847
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.
1856
1857 DETAILS:
1858
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.
1862
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.
1871
1872 As the modulo-operator % is not well-defined for negative numbers,
1873 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1874
1875 There might be quicker ways to do this in C. Certainly so in
1876 assembler.
1877
1878 Claus Fischer, fischer@iue.tuwien.ac.at
1879 */
1880
Patrik Stridvall96336321999-10-24 22:13:47 +00001881#if SIZEOF_LONG_LONG >= 8
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001882# define USE_LONG_LONG 1
1883#else
1884# define USE_LONG_LONG 0
1885#endif
1886
1887#if USE_LONG_LONG /* gcc supports long long type */
1888
1889 long long int t = unix_time;
1890 t *= 10000000;
1891 t += 116444736000000000LL;
1892 t += remainder;
Alexandre Julliarda3960291999-02-26 11:11:13 +00001893 filetime->dwLowDateTime = (UINT)t;
1894 filetime->dwHighDateTime = (UINT)(t >> 32);
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001895
1896#else /* ISO version */
1897
Alexandre Julliarda3960291999-02-26 11:11:13 +00001898 UINT a0; /* 16 bit, low bits */
1899 UINT a1; /* 16 bit, medium bits */
1900 UINT a2; /* 32 bit, high bits */
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001901
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));
1910
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;
1918
1919 a0 *= 1000;
1920 a1 = a1 * 1000 + (a0 >> 16);
1921 a2 = a2 * 1000 + (a1 >> 16);
1922 a0 &= 0xffff;
1923 a1 &= 0xffff;
1924
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;
1931
1932 /* Set filetime */
1933 filetime->dwLowDateTime = (a1 << 16) + a0;
1934 filetime->dwHighDateTime = a2;
1935#endif
Alexandre Julliardca22b331996-07-12 19:02:39 +00001936}
1937
1938
1939/***********************************************************************
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001940 * DOSFS_FileTimeToUnixTime
1941 *
1942 * Convert a FILETIME format to Unix time.
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001943 * If not NULL, 'remainder' contains the fractional part of the filetime,
1944 * in the range of [0..9999999] (even if time_t is negative).
Alexandre Julliardca22b331996-07-12 19:02:39 +00001945 */
Alexandre Julliardc6c09441997-01-12 18:32:19 +00001946time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
Alexandre Julliardca22b331996-07-12 19:02:39 +00001947{
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001948 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1949#if USE_LONG_LONG
1950
1951 long long int t = filetime->dwHighDateTime;
1952 t <<= 32;
Alexandre Julliarda3960291999-02-26 11:11:13 +00001953 t += (UINT)filetime->dwLowDateTime;
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001954 t -= 116444736000000000LL;
1955 if (t < 0)
1956 {
1957 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1958 return -1 - ((-t - 1) / 10000000);
1959 }
1960 else
1961 {
1962 if (remainder) *remainder = t % 10000000;
1963 return t / 10000000;
1964 }
1965
1966#else /* ISO version */
1967
Alexandre Julliarda3960291999-02-26 11:11:13 +00001968 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 */
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001972 unsigned int carry; /* carry bit for subtraction */
1973 int negative; /* whether a represents a negative value */
1974
1975 /* Copy the time values to a2/a1/a0 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001976 a2 = (UINT)filetime->dwHighDateTime;
1977 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1978 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001979
1980 /* Subtract the time difference */
1981 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1982 else a0 += (1 << 16) - 32768 , carry = 1;
1983
1984 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1985 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1986
1987 a2 -= 27111902 + carry;
1988
1989 /* If a is negative, replace a by (-1-a) */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001990 negative = (a2 >= ((UINT)1) << 31);
Alexandre Julliardd37eb361997-07-20 16:23:21 +00001991 if (negative)
1992 {
1993 /* Set a to -a - 1 (a is a2/a1/a0) */
1994 a0 = 0xffff - a0;
1995 a1 = 0xffff - a1;
1996 a2 = ~a2;
1997 }
1998
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;
2007
2008 a1 += (a2 % 1000) << 16;
2009 a2 /= 1000;
2010 a0 += (a1 % 1000) << 16;
2011 a1 /= 1000;
2012 r += (a0 % 1000) * 10000;
2013 a0 /= 1000;
2014
2015 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2016 if (negative)
2017 {
2018 /* Set a to -a - 1 (a is a2/a1/a0) */
2019 a0 = 0xffff - a0;
2020 a1 = 0xffff - a1;
2021 a2 = ~a2;
2022
2023 r = 9999999 - r;
2024 }
2025
2026 if (remainder) *remainder = r;
2027
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
Alexandre Julliardb1bac321996-12-15 19:45:59 +00002032}
2033
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002034
2035/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002036 * MulDiv (KERNEL32.@)
Alexandre Julliard15467bf2000-08-01 22:03:18 +00002037 * RETURNS
2038 * Result of multiplication and division
2039 * -1: Overflow occurred or Divisor was 0
2040 */
2041INT WINAPI MulDiv(
2042 INT nMultiplicand,
2043 INT nMultiplier,
2044 INT nDivisor)
2045{
2046#if SIZEOF_LONG_LONG >= 8
2047 long long ret;
2048
2049 if (!nDivisor) return -1;
2050
2051 /* We want to deal with a positive divisor to simplify the logic. */
2052 if (nDivisor < 0)
2053 {
2054 nMultiplicand = - nMultiplicand;
2055 nDivisor = -nDivisor;
2056 }
2057
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;
2064
2065 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2066 return ret;
2067#else
2068 if (!nDivisor) return -1;
2069
2070 /* We want to deal with a positive divisor to simplify the logic. */
2071 if (nDivisor < 0)
2072 {
2073 nMultiplicand = - nMultiplicand;
2074 nDivisor = -nDivisor;
2075 }
2076
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;
2081
2082 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2083
2084#endif
2085}
2086
2087
2088/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002089 * DosDateTimeToFileTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002090 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002091BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002092{
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002093 struct tm newtm;
2094
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;
Alexandre Julliarde101f6d2000-08-14 14:42:41 +00002101 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002102 return TRUE;
2103}
2104
2105
2106/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002107 * FileTimeToDosDateTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002108 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002109BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00002110 LPWORD fattime )
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002111{
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002112 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;
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002119 return TRUE;
2120}
2121
2122
2123/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002124 * LocalFileTimeToFileTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002125 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002126BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00002127 LPFILETIME utcft )
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002128{
2129 struct tm *xtm;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002130 DWORD remainder;
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002131
2132 /* convert from local to UTC. Perhaps not correct. FIXME */
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002133 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2134 xtm = gmtime( &unixtime );
2135 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002136 return TRUE;
2137}
2138
2139
2140/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002141 * FileTimeToLocalFileTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002142 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002143BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
Alexandre Julliard670cdc41997-08-24 16:00:30 +00002144 LPFILETIME localft )
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002145{
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002146 DWORD remainder;
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002147 /* convert from UTC to local. Perhaps not correct. FIXME */
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002148 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002149#ifdef HAVE_TIMEGM
2150 struct tm *xtm = localtime( &unixtime );
2151 time_t localtime;
2152
2153 localtime = timegm(xtm);
2154 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2155
2156#else
2157 struct tm *xtm,*gtm;
2158 time_t time1,time2;
2159
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002160 xtm = localtime( &unixtime );
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002161 gtm = gmtime( &unixtime );
2162 time1 = mktime(xtm);
2163 time2 = mktime(gtm);
2164 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2165#endif
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002166 return TRUE;
2167}
2168
2169
2170/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002171 * FileTimeToSystemTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002172 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002173BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002174{
2175 struct tm *xtm;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002176 DWORD remainder;
2177 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002178 xtm = gmtime(&xtime);
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002179 syst->wYear = xtm->tm_year+1900;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +00002180 syst->wMonth = xtm->tm_mon + 1;
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002181 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;
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002187 return TRUE;
2188}
2189
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002190/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002191 * QueryDosDeviceA (KERNEL32.@)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002192 *
2193 * returns array of strings terminated by \0, terminated by \0
2194 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002195DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002196{
2197 LPSTR s;
2198 char buffer[200];
2199
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +00002200 TRACE("(%s,...)\n", devname ? devname : "<null>");
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002201 if (!devname) {
2202 /* return known MSDOS devices */
Alexandre Julliard24a62ab2000-11-28 22:40:56 +00002203 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));
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002206 }
Marcus Meissner04d5efd2001-05-25 20:39:45 +00002207 /* In theory all that are possible and have been defined.
2208 * Now just those below, since mirc uses it to check for special files.
2209 *
2210 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2211 * but currently we just ignore that.)
2212 */
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);
2224 }
2225 SetLastError(ERROR_DEV_NOT_EXIST);
2226 return 0;
2227 }
2228
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002229}
2230
2231
2232/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002233 * QueryDosDeviceW (KERNEL32.@)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002234 *
2235 * returns array of strings terminated by \0, terminated by \0
2236 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002237DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002238{
2239 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
Dimitrie O. Paunabdbced2000-04-29 14:20:28 +00002240 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
Alexandre Julliarda3960291999-02-26 11:11:13 +00002241 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002242
Alexandre Julliard24a62ab2000-11-28 22:40:56 +00002243 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
Alexandre Julliardc6c09441997-01-12 18:32:19 +00002244 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2245 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2246 return ret;
2247}
2248
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002249
2250/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002251 * SystemTimeToFileTime (KERNEL32.@)
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002252 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002253BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002254{
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002255#ifdef HAVE_TIMEGM
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002256 struct tm xtm;
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002257 time_t utctime;
2258#else
2259 struct tm xtm,*local_tm,*utc_tm;
2260 time_t localtim,utctime;
2261#endif
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002262
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002263 xtm.tm_year = syst->wYear-1900;
Alexandre Julliarda11d7b11998-03-01 20:05:02 +00002264 xtm.tm_mon = syst->wMonth - 1;
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002265 xtm.tm_wday = syst->wDayOfWeek;
2266 xtm.tm_mday = syst->wDay;
2267 xtm.tm_hour = syst->wHour;
2268 xtm.tm_min = syst->wMinute;
Alexandre Julliarda69b88b1998-03-15 20:29:56 +00002269 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
Alexandre Julliard75d86e11996-11-17 18:59:11 +00002283 return TRUE;
2284}
Marcus Meissner575a1651998-10-21 16:47:29 +00002285
Patrik Stridvall3a9c4761999-10-31 02:07:05 +00002286/***********************************************************************
Patrik Stridvalldae8de62001-06-13 20:13:18 +00002287 * DefineDosDeviceA (KERNEL32.@)
Patrik Stridvall3a9c4761999-10-31 02:07:05 +00002288 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00002289BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
Dimitrie O. Paundd03cc11999-12-08 03:56:23 +00002290 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
Marcus Meissner575a1651998-10-21 16:47:29 +00002291 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2292 return FALSE;
2293}
Juergen Schmied2250f122000-06-01 23:17:42 +00002294
2295/*
2296 --- 16 bit functions ---
2297*/
2298
2299/*************************************************************************
Patrik Stridvall01d5e5b2001-07-02 19:59:40 +00002300 * FindFirstFile (KERNEL.413)
Juergen Schmied2250f122000-06-01 23:17:42 +00002301 */
2302HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2303{
2304 DOS_FULL_NAME full_name;
2305 HGLOBAL16 handle;
2306 FIND_FIRST_INFO *info;
2307
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 );
Alexandre Julliard5f728ca2001-07-24 21:45:22 +00002315 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2316 strcpy( info->path, full_name.long_name );
Juergen Schmied2250f122000-06-01 23:17:42 +00002317 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;
Aric Stewarte4d09322000-12-03 03:14:29 +00002322 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
Juergen Schmied2250f122000-06-01 23:17:42 +00002323 else info->drive = DRIVE_GetCurrentDrive();
2324 info->cur_pos = 0;
2325
2326 info->dir = DOSFS_OpenDir( info->path );
2327
2328 GlobalUnlock16( handle );
2329 if (!FindNextFile16( handle, data ))
2330 {
2331 FindClose16( handle );
2332 SetLastError( ERROR_NO_MORE_FILES );
2333 return INVALID_HANDLE_VALUE16;
2334 }
2335 return handle;
2336}
2337
2338/*************************************************************************
Patrik Stridvall01d5e5b2001-07-02 19:59:40 +00002339 * FindNextFile (KERNEL.414)
Juergen Schmied2250f122000-06-01 23:17:42 +00002340 */
2341BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2342{
2343 FIND_FIRST_INFO *info;
2344
2345 if ((handle == INVALID_HANDLE_VALUE16) ||
2346 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2347 {
2348 SetLastError( ERROR_INVALID_HANDLE );
2349 return FALSE;
2350 }
2351 GlobalUnlock16( handle );
2352 if (!info->path || !info->dir)
2353 {
2354 SetLastError( ERROR_NO_MORE_FILES );
2355 return FALSE;
2356 }
2357 if (!DOSFS_FindNextEx( info, data ))
2358 {
2359 DOSFS_CloseDir( info->dir ); info->dir = NULL;
Alexandre Julliard61d32b42001-02-23 01:35:36 +00002360 HeapFree( GetProcessHeap(), 0, info->path );
Juergen Schmied2250f122000-06-01 23:17:42 +00002361 info->path = info->long_mask = NULL;
2362 SetLastError( ERROR_NO_MORE_FILES );
2363 return FALSE;
2364 }
2365 return TRUE;
2366}
2367
2368/*************************************************************************
Patrik Stridvall01d5e5b2001-07-02 19:59:40 +00002369 * FindClose (KERNEL.415)
Juergen Schmied2250f122000-06-01 23:17:42 +00002370 */
2371BOOL16 WINAPI FindClose16( HANDLE16 handle )
2372{
2373 FIND_FIRST_INFO *info;
2374
2375 if ((handle == INVALID_HANDLE_VALUE16) ||
2376 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2377 {
2378 SetLastError( ERROR_INVALID_HANDLE );
2379 return FALSE;
2380 }
2381 if (info->dir) DOSFS_CloseDir( info->dir );
Alexandre Julliard61d32b42001-02-23 01:35:36 +00002382 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
Juergen Schmied2250f122000-06-01 23:17:42 +00002383 GlobalUnlock16( handle );
2384 GlobalFree16( handle );
2385 return TRUE;
2386}
2387