Reimplemented CreateDirectoryW and RemoveDirectoryW using ntdll
[wine/hacks.git] / files / drive.c
blobdcfe97fbdefb3d6d7d089fa11c4d14dfe24965ab
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "ntstatus.h"
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winreg.h"
49 #include "winternl.h"
50 #include "wine/winbase16.h" /* for GetCurrentTask */
51 #include "winerror.h"
52 #include "winioctl.h"
53 #include "ntddstor.h"
54 #include "ntddcdrm.h"
55 #include "file.h"
56 #include "wine/unicode.h"
57 #include "wine/library.h"
58 #include "wine/server.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
62 WINE_DECLARE_DEBUG_CHANNEL(file);
64 typedef struct
66 char *root; /* root dir in Unix format without trailing / */
67 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
68 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
69 char *device; /* raw device path */
70 dev_t dev; /* unix device number */
71 ino_t ino; /* unix inode number */
72 } DOSDRIVE;
75 #define MAX_DOS_DRIVES 26
77 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
78 static int DRIVE_CurDrive = -1;
80 static HTASK16 DRIVE_LastTask = 0;
82 /* strdup on the process heap */
83 inline static char *heap_strdup( const char *str )
85 INT len = strlen(str) + 1;
86 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
87 if (p) memcpy( p, str, len );
88 return p;
91 /***********************************************************************
92 * DRIVE_Init
94 int DRIVE_Init(void)
96 int i, len, symlink_count = 0, count = 0;
97 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
98 'W','i','n','e','\\','W','i','n','e','\\',
99 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
100 WCHAR drive_env[] = {'=','A',':',0};
101 WCHAR path[MAX_PATHNAME_LEN];
102 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
103 struct stat drive_stat_buffer;
104 WCHAR *p;
105 DOSDRIVE *drive;
106 HKEY hkey;
107 DWORD dummy;
108 OBJECT_ATTRIBUTES attr;
109 UNICODE_STRING nameW;
110 char *root;
111 const char *config_dir = wine_get_config_dir();
113 static const WCHAR PathW[] = {'P','a','t','h',0};
114 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
116 attr.Length = sizeof(attr);
117 attr.RootDirectory = 0;
118 attr.ObjectName = &nameW;
119 attr.Attributes = 0;
120 attr.SecurityDescriptor = NULL;
121 attr.SecurityQualityOfService = NULL;
123 /* get the root of the drives from symlinks */
125 root = NULL;
126 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
128 if (!root)
130 root = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") );
131 strcpy( root, config_dir );
132 strcat( root, "/dosdevices/a:" );
134 root[strlen(root)-2] = 'a' + i;
135 if (stat( root, &drive_stat_buffer ))
137 if (!lstat( root, &drive_stat_buffer))
138 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
139 root, strerror(errno), 'a' + i);
140 continue;
142 if (!S_ISDIR(drive_stat_buffer.st_mode))
144 MESSAGE("%s is not a directory, ignoring drive %c:\n", root, 'a' + i );
145 continue;
147 drive->root = root;
148 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
149 drive->unix_cwd = heap_strdup( "" );
150 drive->device = NULL;
151 drive->dev = drive_stat_buffer.st_dev;
152 drive->ino = drive_stat_buffer.st_ino;
153 root = NULL;
154 symlink_count++;
156 if (root) HeapFree( GetProcessHeap(), 0, root );
158 /* now get the parameters from the config file */
160 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
162 RtlInitUnicodeString( &nameW, driveW );
163 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
164 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
166 /* Get the root path */
167 if (!symlink_count)
169 RtlInitUnicodeString( &nameW, PathW );
170 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
172 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
173 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
175 p = path + strlenW(path) - 1;
176 while ((p > path) && (*p == '/')) *p-- = '\0';
178 if (path[0] == '/')
180 len = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
181 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
182 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, drive->root, len, NULL, NULL);
184 else
186 /* relative paths are relative to config dir */
187 const char *config = wine_get_config_dir();
188 len = strlen(config);
189 len += WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL) + 2;
190 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
191 len -= sprintf( drive->root, "%s/", config );
192 WideCharToMultiByte(CP_UNIXCP, 0, path, -1,
193 drive->root + strlen(drive->root), len, NULL, NULL);
196 if (stat( drive->root, &drive_stat_buffer ))
198 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
199 drive->root, strerror(errno), 'A' + i);
200 HeapFree( GetProcessHeap(), 0, drive->root );
201 drive->root = NULL;
202 goto next;
204 if (!S_ISDIR(drive_stat_buffer.st_mode))
206 MESSAGE("%s is not a directory, ignoring drive %c:\n",
207 drive->root, 'A' + i );
208 HeapFree( GetProcessHeap(), 0, drive->root );
209 drive->root = NULL;
210 goto next;
213 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
214 drive->unix_cwd = heap_strdup( "" );
215 drive->device = NULL;
216 drive->dev = drive_stat_buffer.st_dev;
217 drive->ino = drive_stat_buffer.st_ino;
221 if (drive->root)
223 /* Get the device */
224 RtlInitUnicodeString( &nameW, DeviceW );
225 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
227 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
228 len = WideCharToMultiByte(CP_UNIXCP, 0, data, -1, NULL, 0, NULL, NULL);
229 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
230 WideCharToMultiByte(CP_UNIXCP, 0, data, -1, drive->device, len, NULL, NULL);
233 count++;
234 TRACE("Drive %c: path=%s dev=%x ino=%x\n",
235 'A' + i, drive->root, (int)drive->dev, (int)drive->ino );
238 next:
239 NtClose( hkey );
242 if (!count && !symlink_count)
244 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
245 /* Create a C drive pointing to Unix root dir */
246 DOSDrives[2].root = heap_strdup( "/" );
247 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
248 DOSDrives[2].unix_cwd = heap_strdup( "" );
249 DOSDrives[2].device = NULL;
250 DRIVE_CurDrive = 2;
253 /* Make sure the current drive is valid */
254 if (DRIVE_CurDrive == -1)
256 for (i = 2, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
258 if (drive->root)
260 DRIVE_CurDrive = i;
261 break;
266 /* get current working directory info for all drives */
267 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
269 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
270 /* sanity check */
271 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
272 DRIVE_Chdir( i, path + 2 );
274 return 1;
278 /***********************************************************************
279 * DRIVE_IsValid
281 int DRIVE_IsValid( int drive )
283 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
284 return (DOSDrives[drive].root != NULL);
288 /***********************************************************************
289 * DRIVE_GetCurrentDrive
291 int DRIVE_GetCurrentDrive(void)
293 TDB *pTask = GlobalLock16(GetCurrentTask());
294 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
295 return DRIVE_CurDrive;
299 /***********************************************************************
300 * DRIVE_SetCurrentDrive
302 static int DRIVE_SetCurrentDrive( int drive )
304 TDB *pTask = GlobalLock16(GetCurrentTask());
305 if (!DRIVE_IsValid( drive ))
307 SetLastError( ERROR_INVALID_DRIVE );
308 return 0;
310 TRACE("%c:\n", 'A' + drive );
311 DRIVE_CurDrive = drive;
312 if (pTask) pTask->curdrive = drive | 0x80;
313 return 1;
317 /***********************************************************************
318 * DRIVE_FindDriveRoot
320 * Find a drive for which the root matches the beginning of the given path.
321 * This can be used to translate a Unix path into a drive + DOS path.
322 * Return value is the drive, or -1 on error. On success, path is modified
323 * to point to the beginning of the DOS path.
325 * Note: path must be in the encoding of the underlying Unix file system.
327 int DRIVE_FindDriveRoot( const char **path )
329 /* Starting with the full path, check if the device and inode match any of
330 * the wine 'drives'. If not then remove the last path component and try
331 * again. If the last component was a '..' then skip a normal component
332 * since it's a directory that's ascended back out of.
334 int drive, level, len;
335 char buffer[MAX_PATHNAME_LEN];
336 char *p;
337 struct stat st;
339 strcpy( buffer, *path );
340 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
341 len = p - buffer;
343 /* strip off trailing slashes */
344 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
346 for (;;)
348 /* Find the drive */
349 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
351 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
353 if (!DOSDrives[drive].root) continue;
355 if ((DOSDrives[drive].dev == st.st_dev) &&
356 (DOSDrives[drive].ino == st.st_ino))
358 if (len == 1) len = 0; /* preserve root slash in returned path */
359 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
360 *path, 'A' + drive, buffer, *path + len);
361 *path += len;
362 if (!**path) *path = "\\";
363 return drive;
367 if (len <= 1) return -1; /* reached root */
369 level = 0;
370 while (level < 1)
372 /* find start of the last path component */
373 while (len > 1 && buffer[len - 1] != '/') len--;
374 if (!buffer[len]) break; /* empty component -> reached root */
375 /* does removing it take us up a level? */
376 if (strcmp( buffer + len, "." ) != 0)
377 level += strcmp( buffer + len, ".." ) ? 1 : -1;
378 buffer[len] = 0;
379 /* strip off trailing slashes */
380 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
386 /***********************************************************************
387 * DRIVE_FindDriveRootW
389 * Unicode version of DRIVE_FindDriveRoot.
391 int DRIVE_FindDriveRootW( LPCWSTR *path )
393 int drive, level, len;
394 WCHAR buffer[MAX_PATHNAME_LEN];
395 WCHAR *p;
396 struct stat st;
398 strcpyW( buffer, *path );
399 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
400 len = p - buffer;
402 /* strip off trailing slashes */
403 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
405 for (;;)
407 char buffA[MAX_PATHNAME_LEN];
409 WideCharToMultiByte( CP_UNIXCP, 0, buffer, -1, buffA, sizeof(buffA), NULL, NULL );
410 if (stat( buffA, &st ) == 0 && S_ISDIR( st.st_mode ))
412 /* Find the drive */
413 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
415 if (!DOSDrives[drive].root) continue;
417 if ((DOSDrives[drive].dev == st.st_dev) &&
418 (DOSDrives[drive].ino == st.st_ino))
420 static const WCHAR rootW[] = {'\\',0};
422 if (len == 1) len = 0; /* preserve root slash in returned path */
423 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
424 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
425 *path += len;
426 if (!**path) *path = rootW;
427 return drive;
431 if (len <= 1) return -1; /* reached root */
433 level = 0;
434 while (level < 1)
436 static const WCHAR dotW[] = {'.',0};
437 static const WCHAR dotdotW[] = {'.','.',0};
439 /* find start of the last path component */
440 while (len > 1 && buffer[len - 1] != '/') len--;
441 if (!buffer[len]) break; /* empty component -> reached root */
442 /* does removing it take us up a level? */
443 if (strcmpW( buffer + len, dotW ) != 0)
444 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
445 buffer[len] = 0;
446 /* strip off trailing slashes */
447 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
453 /***********************************************************************
454 * DRIVE_GetRoot
456 const char * DRIVE_GetRoot( int drive )
458 if (!DRIVE_IsValid( drive )) return NULL;
459 return DOSDrives[drive].root;
463 /***********************************************************************
464 * DRIVE_GetDosCwd
466 LPCWSTR DRIVE_GetDosCwd( int drive )
468 TDB *pTask = GlobalLock16(GetCurrentTask());
469 if (!DRIVE_IsValid( drive )) return NULL;
471 /* Check if we need to change the directory to the new task. */
472 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
473 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
474 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
476 static const WCHAR rootW[] = {'\\',0};
477 WCHAR curdirW[MAX_PATH];
478 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
479 /* Perform the task-switch */
480 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
481 DRIVE_LastTask = GetCurrentTask();
483 return DOSDrives[drive].dos_cwd;
487 /***********************************************************************
488 * DRIVE_GetUnixCwd
490 const char * DRIVE_GetUnixCwd( int drive )
492 TDB *pTask = GlobalLock16(GetCurrentTask());
493 if (!DRIVE_IsValid( drive )) return NULL;
495 /* Check if we need to change the directory to the new task. */
496 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
497 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
498 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
500 static const WCHAR rootW[] = {'\\',0};
501 WCHAR curdirW[MAX_PATH];
502 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
503 /* Perform the task-switch */
504 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
505 DRIVE_LastTask = GetCurrentTask();
507 return DOSDrives[drive].unix_cwd;
511 /***********************************************************************
512 * DRIVE_GetDevice
514 const char * DRIVE_GetDevice( int drive )
516 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
519 /***********************************************************************
520 * DRIVE_Chdir
522 int DRIVE_Chdir( int drive, LPCWSTR path )
524 DOS_FULL_NAME full_name;
525 WCHAR buffer[MAX_PATHNAME_LEN];
526 LPSTR unix_cwd;
527 BY_HANDLE_FILE_INFORMATION info;
528 TDB *pTask = GlobalLock16(GetCurrentTask());
530 buffer[0] = 'A' + drive;
531 buffer[1] = ':';
532 buffer[2] = 0;
533 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
534 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
535 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
537 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
538 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
539 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
541 SetLastError( ERROR_FILE_NOT_FOUND );
542 return 0;
544 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
545 while (*unix_cwd == '/') unix_cwd++;
547 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
548 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
550 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
551 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
552 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
553 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
554 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
556 if (drive == DRIVE_CurDrive)
558 UNICODE_STRING dirW;
560 RtlInitUnicodeString( &dirW, full_name.short_name );
561 RtlSetCurrentDirectory_U( &dirW );
564 if (pTask && (pTask->curdrive & 0x80) &&
565 ((pTask->curdrive & ~0x80) == drive))
567 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
568 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
569 DRIVE_LastTask = GetCurrentTask();
571 return 1;
575 /***********************************************************************
576 * DRIVE_GetCurrentDirectory
577 * Returns "X:\\path\\etc\\".
579 * Despite the API description, return required length including the
580 * terminating null when buffer too small. This is the real behaviour.
582 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
584 UINT ret;
585 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
586 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
588 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
589 if (ret >= buflen) return ret + 1;
591 strcpyW( buf, driveA_rootW );
592 buf[0] += DRIVE_GetCurrentDrive();
593 strcatW( buf, dos_cwd );
594 return ret;
598 /***********************************************************************
599 * DRIVE_BuildEnv
601 * Build the environment array containing the drives' current directories.
602 * Resulting pointer must be freed with HeapFree.
604 WCHAR *DRIVE_BuildEnv(void)
606 int i, length = 0;
607 LPCWSTR cwd[MAX_DOS_DRIVES];
608 WCHAR *env, *p;
610 for (i = 0; i < MAX_DOS_DRIVES; i++)
612 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
613 length += strlenW(cwd[i]) + 8;
615 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
616 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
618 if (cwd[i] && cwd[i][0])
620 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
621 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
622 strcpyW( p, cwd[i] );
623 p += strlenW(p) + 1;
626 *p = 0;
627 return env;
631 /***********************************************************************
632 * GetCurrentDirectory (KERNEL.411)
634 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
636 WCHAR cur_dirW[MAX_PATH];
638 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
639 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
643 /***********************************************************************
644 * GetCurrentDirectoryW (KERNEL32.@)
646 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
648 UINT ret;
649 WCHAR longname[MAX_PATHNAME_LEN];
650 WCHAR shortname[MAX_PATHNAME_LEN];
652 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
653 if ( ret > MAX_PATHNAME_LEN ) {
654 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
655 return ret;
657 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
658 ret = strlenW( longname ) + 1;
659 if (ret > buflen) return ret;
660 strcpyW(buf, longname);
661 return ret - 1;
664 /***********************************************************************
665 * GetCurrentDirectoryA (KERNEL32.@)
667 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
669 WCHAR bufferW[MAX_PATH];
670 DWORD ret, retW;
672 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
674 if (!retW)
675 ret = 0;
676 else if (retW > MAX_PATH)
678 SetLastError(ERROR_FILENAME_EXCED_RANGE);
679 ret = 0;
681 else
683 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
684 if (buflen >= ret)
686 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
687 ret--; /* length without 0 */
690 return ret;
694 /***********************************************************************
695 * SetCurrentDirectoryW (KERNEL32.@)
697 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
699 int drive, olddrive = DRIVE_GetCurrentDrive();
701 if (!dir)
703 SetLastError(ERROR_INVALID_PARAMETER);
704 return FALSE;
706 if (dir[0] && (dir[1]==':'))
708 drive = toupperW( *dir ) - 'A';
709 dir += 2;
711 else
712 drive = olddrive;
714 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
715 sets pTask->curdir only if pTask->curdrive is drive */
716 if (!(DRIVE_SetCurrentDrive( drive )))
717 return FALSE;
719 /* FIXME: what about empty strings? Add a \\ ? */
720 if (!DRIVE_Chdir( drive, dir )) {
721 DRIVE_SetCurrentDrive(olddrive);
722 return FALSE;
724 return TRUE;
728 /***********************************************************************
729 * SetCurrentDirectoryA (KERNEL32.@)
731 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
733 UNICODE_STRING dirW;
734 BOOL ret = FALSE;
736 if (!dir)
738 SetLastError(ERROR_INVALID_PARAMETER);
739 return FALSE;
742 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
744 ret = SetCurrentDirectoryW(dirW.Buffer);
745 RtlFreeUnicodeString(&dirW);
747 else
748 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
749 return ret;