- Add support for WM_NOTIFYFORMAT and support both ANSI and UNICODE
[wine] / files / directory.c
1 /*
2  * DOS directories functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include "config.h"
8
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #ifdef HAVE_SYS_ERRNO_H
18 #include <sys/errno.h>
19 #endif
20
21 #include "winbase.h"
22 #include "wine/winbase16.h"
23 #include "windef.h"
24 #include "wingdi.h"
25 #include "wine/winuser16.h"
26 #include "winerror.h"
27 #include "drive.h"
28 #include "file.h"
29 #include "heap.h"
30 #include "msdos.h"
31 #include "options.h"
32 #include "debugtools.h"
33
34 DEFAULT_DEBUG_CHANNEL(dosfs);
35 DECLARE_DEBUG_CHANNEL(file);
36
37 static DOS_FULL_NAME DIR_Windows;
38 static DOS_FULL_NAME DIR_System;
39
40
41 /***********************************************************************
42  *           DIR_GetPath
43  *
44  * Get a path name from the wine.ini file and make sure it is valid.
45  */
46 static int DIR_GetPath( const char *keyname, const char *defval,
47                         DOS_FULL_NAME *full_name, BOOL warn )
48 {
49     char path[MAX_PATHNAME_LEN];
50     BY_HANDLE_FILE_INFORMATION info;
51     const char *mess = "does not exist";
52
53     PROFILE_GetWineIniString( "wine", keyname, defval, path, sizeof(path) );
54     if (!DOSFS_GetFullName( path, TRUE, full_name ) ||
55         (!FILE_Stat( full_name->long_name, &info ) && (mess=strerror(errno)))||
56         (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (mess="not a directory")))
57     {
58         if (warn)
59            MESSAGE("Invalid path '%s' for %s directory: %s\n", path, keyname, mess);
60         return 0;
61     }
62     return 1;
63 }
64
65
66 /***********************************************************************
67  *           DIR_Init
68  */
69 int DIR_Init(void)
70 {
71     char path[MAX_PATHNAME_LEN];
72     DOS_FULL_NAME tmp_dir, profile_dir;
73     int drive;
74     const char *cwd;
75
76     if (!getcwd( path, MAX_PATHNAME_LEN ))
77     {
78         perror( "Could not get current directory" );
79         return 0;
80     }
81     cwd = path;
82     if ((drive = DRIVE_FindDriveRoot( &cwd )) == -1)
83     {
84         MESSAGE("Warning: could not find wine config [Drive x] entry "
85             "for current working directory %s; "
86             "starting in windows directory.\n", cwd );
87     }
88     else
89     {
90         DRIVE_SetCurrentDrive( drive );
91         DRIVE_Chdir( drive, cwd );
92     }
93
94     if (!(DIR_GetPath( "windows", "c:\\windows", &DIR_Windows, TRUE )) ||
95         !(DIR_GetPath( "system", "c:\\windows\\system", &DIR_System, TRUE )) ||
96         !(DIR_GetPath( "temp", "c:\\windows", &tmp_dir, TRUE )))
97     {
98         PROFILE_UsageWineIni();
99         return 0;
100     }
101     if (-1 == access( tmp_dir.long_name, W_OK ))
102     {
103         if (errno==EACCES)
104         {
105                 MESSAGE("Warning: the temporary directory '%s' (specified in wine configuration file) is not writeable.\n", tmp_dir.long_name);
106                 PROFILE_UsageWineIni();
107         }
108         else
109                 MESSAGE("Warning: access to temporary directory '%s' failed (%s).\n",
110                     tmp_dir.long_name, strerror(errno));
111     }
112
113     if (drive == -1)
114     {
115         drive = DIR_Windows.drive;
116         DRIVE_SetCurrentDrive( drive );
117         DRIVE_Chdir( drive, DIR_Windows.short_name + 2 );
118     }
119
120     PROFILE_GetWineIniString("wine", "path", "c:\\windows;c:\\windows\\system",
121                              path, sizeof(path) );
122     if (strchr(path, '/'))
123     {
124         MESSAGE("No '/' allowed in [wine] 'Path=' statement of wine config!\n");
125         PROFILE_UsageWineIni();
126         ExitProcess(1);
127     }
128
129     /* Set the environment variables */
130
131     SetEnvironmentVariableA( "PATH", path );
132     SetEnvironmentVariableA( "TEMP", tmp_dir.short_name );
133     SetEnvironmentVariableA( "TMP", tmp_dir.short_name );
134     SetEnvironmentVariableA( "windir", DIR_Windows.short_name );
135     SetEnvironmentVariableA( "winsysdir", DIR_System.short_name );
136
137     /* set COMSPEC only if it doesn't exist already */
138     if (!GetEnvironmentVariableA( "COMSPEC", NULL, 0 ))
139         SetEnvironmentVariableA( "COMSPEC", "c:\\command.com" );
140
141     TRACE("WindowsDir = %s (%s)\n",
142           DIR_Windows.short_name, DIR_Windows.long_name );
143     TRACE("SystemDir  = %s (%s)\n",
144           DIR_System.short_name, DIR_System.long_name );
145     TRACE("TempDir    = %s (%s)\n",
146           tmp_dir.short_name, tmp_dir.long_name );
147     TRACE("Path       = %s\n", path );
148     TRACE("Cwd        = %c:\\%s\n",
149           'A' + drive, DRIVE_GetDosCwd( drive ) );
150
151     if (DIR_GetPath( "profile", "", &profile_dir, FALSE ))
152     {
153         TRACE("USERPROFILE= %s\n", profile_dir.short_name );
154         SetEnvironmentVariableA( "USERPROFILE", profile_dir.short_name );
155     }   
156
157     TRACE("SYSTEMROOT = %s\n", DIR_Windows.short_name );
158     SetEnvironmentVariableA( "SYSTEMROOT", DIR_Windows.short_name );
159
160     return 1;
161 }
162
163
164 /***********************************************************************
165  *           GetTempPathA   (KERNEL32.@)
166  */
167 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
168 {
169     UINT ret;
170     if (!(ret = GetEnvironmentVariableA( "TMP", path, count )))
171         if (!(ret = GetEnvironmentVariableA( "TEMP", path, count )))
172             if (!(ret = GetCurrentDirectoryA( count, path )))
173                 return 0;
174     if (count && (ret < count - 1) && (path[ret-1] != '\\'))
175     {
176         path[ret++] = '\\';
177         path[ret]   = '\0';
178     }
179     return ret;
180 }
181
182
183 /***********************************************************************
184  *           GetTempPathW   (KERNEL32.@)
185  */
186 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
187 {
188     static const WCHAR tmp[]  = { 'T', 'M', 'P', 0 };
189     static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
190     UINT ret;
191     if (!(ret = GetEnvironmentVariableW( tmp, path, count )))
192         if (!(ret = GetEnvironmentVariableW( temp, path, count )))
193             if (!(ret = GetCurrentDirectoryW( count, path )))
194                 return 0;
195     if (count && (ret < count - 1) && (path[ret-1] != '\\'))
196     {
197         path[ret++] = '\\';
198         path[ret]   = '\0';
199     }
200     return ret;
201 }
202
203
204 /***********************************************************************
205  *           DIR_GetWindowsUnixDir
206  */
207 UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count )
208 {
209     if (path) lstrcpynA( path, DIR_Windows.long_name, count );
210     return strlen( DIR_Windows.long_name );
211 }
212
213
214 /***********************************************************************
215  *           DIR_GetSystemUnixDir
216  */
217 UINT DIR_GetSystemUnixDir( LPSTR path, UINT count )
218 {
219     if (path) lstrcpynA( path, DIR_System.long_name, count );
220     return strlen( DIR_System.long_name );
221 }
222
223
224 /***********************************************************************
225  *           GetTempDrive   (KERNEL.92)
226  * A closer look at krnl386.exe shows what the SDK doesn't mention:
227  *
228  * returns:
229  *   AL: driveletter
230  *   AH: ':'            - yes, some kernel code even does stosw with
231  *                            the returned AX.
232  *   DX: 1 for success
233  */
234 UINT WINAPI GetTempDrive( BYTE ignored )
235 {
236     char *buffer;
237     BYTE ret;
238     UINT len = GetTempPathA( 0, NULL );
239
240     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len + 1 )) )
241         ret = DRIVE_GetCurrentDrive() + 'A';
242     else
243     {
244         /* FIXME: apparently Windows does something with the ignored byte */
245         if (!GetTempPathA( len, buffer )) buffer[0] = 'C';
246         ret = toupper(buffer[0]);
247         HeapFree( GetProcessHeap(), 0, buffer );
248     }
249     return MAKELONG( ret | (':' << 8), 1 );
250 }
251
252
253 /***********************************************************************
254  *           GetWindowsDirectory16   (KERNEL.134)
255  */
256 UINT16 WINAPI GetWindowsDirectory16( LPSTR path, UINT16 count )
257 {
258     return (UINT16)GetWindowsDirectoryA( path, count );
259 }
260
261
262 /***********************************************************************
263  *           GetWindowsDirectoryA   (KERNEL32.@)
264  */
265 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
266 {
267     if (path) lstrcpynA( path, DIR_Windows.short_name, count );
268     return strlen( DIR_Windows.short_name );
269 }
270
271
272 /***********************************************************************
273  *           GetWindowsDirectoryW   (KERNEL32.@)
274  */
275 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
276 {
277     UINT len = MultiByteToWideChar( CP_ACP, 0, DIR_Windows.short_name, -1, NULL, 0 );
278     if (path && count)
279     {
280         if (!MultiByteToWideChar( CP_ACP, 0, DIR_Windows.short_name, -1, path, count ))
281             path[count-1] = 0;
282     }
283     return len;
284 }
285
286
287 /***********************************************************************
288  *           GetSystemWindowsDirectoryA   (KERNEL32.@) W2K, TS4.0SP4
289  */
290 UINT WINAPI GetSystemWindowsDirectoryA( LPSTR path, UINT count )
291 {
292     return GetWindowsDirectoryA( path, count );
293 }
294
295
296 /***********************************************************************
297  *           GetSystemWindowsDirectoryW   (KERNEL32.@) W2K, TS4.0SP4
298  */
299 UINT WINAPI GetSystemWindowsDirectoryW( LPWSTR path, UINT count )
300 {
301     return GetWindowsDirectoryW( path, count );
302 }
303
304
305 /***********************************************************************
306  *           GetSystemDirectory16   (KERNEL.135)
307  */
308 UINT16 WINAPI GetSystemDirectory16( LPSTR path, UINT16 count )
309 {
310     return (UINT16)GetSystemDirectoryA( path, count );
311 }
312
313
314 /***********************************************************************
315  *           GetSystemDirectoryA   (KERNEL32.@)
316  */
317 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
318 {
319     if (path) lstrcpynA( path, DIR_System.short_name, count );
320     return strlen( DIR_System.short_name );
321 }
322
323
324 /***********************************************************************
325  *           GetSystemDirectoryW   (KERNEL32.@)
326  */
327 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
328 {
329     UINT len = MultiByteToWideChar( CP_ACP, 0, DIR_System.short_name, -1, NULL, 0 );
330     if (path && count)
331     {
332         if (!MultiByteToWideChar( CP_ACP, 0, DIR_System.short_name, -1, path, count ))
333             path[count-1] = 0;
334     }
335     return len;
336 }
337
338
339 /***********************************************************************
340  *           CreateDirectory16   (KERNEL.144)
341  */
342 BOOL16 WINAPI CreateDirectory16( LPCSTR path, LPVOID dummy )
343 {
344     TRACE_(file)("(%s,%p)\n", path, dummy );
345     return (BOOL16)CreateDirectoryA( path, NULL );
346 }
347
348
349 /***********************************************************************
350  *           CreateDirectoryA   (KERNEL32.@)
351  * RETURNS:
352  *      TRUE : success
353  *      FALSE : failure
354  *              ERROR_DISK_FULL:        on full disk
355  *              ERROR_ALREADY_EXISTS:   if directory name exists (even as file)
356  *              ERROR_ACCESS_DENIED:    on permission problems
357  *              ERROR_FILENAME_EXCED_RANGE: too long filename(s)
358  */
359 BOOL WINAPI CreateDirectoryA( LPCSTR path,
360                                   LPSECURITY_ATTRIBUTES lpsecattribs )
361 {
362     DOS_FULL_NAME full_name;
363
364     TRACE_(file)("(%s,%p)\n", path, lpsecattribs );
365     if (DOSFS_GetDevice( path ))
366     {
367         TRACE_(file)("cannot use device '%s'!\n",path);
368         SetLastError( ERROR_ACCESS_DENIED );
369         return FALSE;
370     }
371     if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
372     if (mkdir( full_name.long_name, 0777 ) == -1) {
373         WARN_(file)("Error '%s' trying to create directory '%s'\n", strerror(errno), full_name.long_name);
374         /* the FILE_SetDosError() generated error codes don't match the 
375          * CreateDirectory ones for some errnos */
376         switch (errno) {
377         case EEXIST: SetLastError(ERROR_ALREADY_EXISTS); break;
378         case ENOSPC: SetLastError(ERROR_DISK_FULL); break;
379         default: FILE_SetDosError();break;
380         }
381         return FALSE;
382     }
383     return TRUE;
384 }
385
386
387 /***********************************************************************
388  *           CreateDirectoryW   (KERNEL32.@)
389  */
390 BOOL WINAPI CreateDirectoryW( LPCWSTR path,
391                                   LPSECURITY_ATTRIBUTES lpsecattribs )
392 {
393     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
394     BOOL ret = CreateDirectoryA( xpath, lpsecattribs );
395     HeapFree( GetProcessHeap(), 0, xpath );
396     return ret;
397 }
398
399
400 /***********************************************************************
401  *           CreateDirectoryExA   (KERNEL32.@)
402  */
403 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path,
404                                     LPSECURITY_ATTRIBUTES lpsecattribs)
405 {
406     return CreateDirectoryA(path,lpsecattribs);
407 }
408
409
410 /***********************************************************************
411  *           CreateDirectoryExW   (KERNEL32.@)
412  */
413 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path,
414                                     LPSECURITY_ATTRIBUTES lpsecattribs)
415 {
416     return CreateDirectoryW(path,lpsecattribs);
417 }
418
419
420 /***********************************************************************
421  *           RemoveDirectory16   (KERNEL)
422  */
423 BOOL16 WINAPI RemoveDirectory16( LPCSTR path )
424 {
425     return (BOOL16)RemoveDirectoryA( path );
426 }
427
428
429 /***********************************************************************
430  *           RemoveDirectoryA   (KERNEL32.@)
431  */
432 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
433 {
434     DOS_FULL_NAME full_name;
435
436     TRACE_(file)("'%s'\n", path );
437
438     if (DOSFS_GetDevice( path ))
439     {
440         TRACE_(file)("cannot remove device '%s'!\n", path);
441         SetLastError( ERROR_FILE_NOT_FOUND );
442         return FALSE;
443     }
444     if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
445     if (rmdir( full_name.long_name ) == -1)
446     {
447         FILE_SetDosError();
448         return FALSE;
449     }
450     return TRUE;
451 }
452
453
454 /***********************************************************************
455  *           RemoveDirectoryW   (KERNEL32.@)
456  */
457 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
458 {
459     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
460     BOOL ret = RemoveDirectoryA( xpath );
461     HeapFree( GetProcessHeap(), 0, xpath );
462     return ret;
463 }
464
465
466 /***********************************************************************
467  *           DIR_TryPath
468  *
469  * Helper function for DIR_SearchPath.
470  */
471 static BOOL DIR_TryPath( const DOS_FULL_NAME *dir, LPCSTR name,
472                            DOS_FULL_NAME *full_name )
473 {
474     LPSTR p_l = full_name->long_name + strlen(dir->long_name) + 1;
475     LPSTR p_s = full_name->short_name + strlen(dir->short_name) + 1;
476
477     if ((p_s >= full_name->short_name + sizeof(full_name->short_name) - 14) ||
478         (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
479     {
480         SetLastError( ERROR_PATH_NOT_FOUND );
481         return FALSE;
482     }
483     if (!DOSFS_FindUnixName( dir->long_name, name, p_l,
484                    sizeof(full_name->long_name) - (p_l - full_name->long_name),
485                    p_s, !(DRIVE_GetFlags(dir->drive) & DRIVE_CASE_SENSITIVE) ))
486         return FALSE;
487     strcpy( full_name->long_name, dir->long_name );
488     p_l[-1] = '/';
489     strcpy( full_name->short_name, dir->short_name );
490     p_s[-1] = '\\';
491     return TRUE;
492 }
493
494
495 /***********************************************************************
496  *           DIR_TryEnvironmentPath
497  *
498  * Helper function for DIR_SearchPath.
499  * Search in the specified path, or in $PATH if NULL.
500  */
501 static BOOL DIR_TryEnvironmentPath( LPCSTR name, DOS_FULL_NAME *full_name, LPCSTR envpath )
502 {
503     LPSTR path, next, buffer;
504     BOOL ret = FALSE;
505     INT len = strlen(name);
506     DWORD size;
507
508     size = envpath ? strlen(envpath)+1 : GetEnvironmentVariableA( "PATH", NULL, 0 );
509     if (!size) return FALSE;
510     if (!(path = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
511     if (envpath) strcpy( path, envpath );
512     else if (!GetEnvironmentVariableA( "PATH", path, size )) goto done;
513     next = path;
514     while (!ret && next)
515     {
516         LPSTR cur = next;
517         while (*cur == ';') cur++;
518         if (!*cur) break;
519         next = strchr( cur, ';' );
520         if (next) *next++ = '\0';
521         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(cur) + len + 2)))
522             goto done;
523         strcpy( buffer, cur );
524         strcat( buffer, "\\" );
525         strcat( buffer, name );
526         ret = DOSFS_GetFullName( buffer, TRUE, full_name );
527         HeapFree( GetProcessHeap(), 0, buffer );
528     }
529
530 done:
531     HeapFree( GetProcessHeap(), 0, path );
532     return ret;
533 }
534
535
536 /***********************************************************************
537  *           DIR_TryModulePath
538  *
539  * Helper function for DIR_SearchPath.
540  */
541 static BOOL DIR_TryModulePath( LPCSTR name, DOS_FULL_NAME *full_name, BOOL win32 )
542 {
543     /* FIXME: for now, GetModuleFileNameA can't return more */
544     /* than OFS_MAXPATHNAME. This may change with Win32. */
545
546     char buffer[OFS_MAXPATHNAME];
547     LPSTR p;
548
549     if (!win32)
550     {
551         if (!GetCurrentTask()) return FALSE;
552         if (!GetModuleFileName16( GetCurrentTask(), buffer, sizeof(buffer) ))
553                 buffer[0]='\0';
554     } else {
555         if (!GetModuleFileNameA( 0, buffer, sizeof(buffer) ))
556                 buffer[0]='\0';
557     }
558     if (!(p = strrchr( buffer, '\\' ))) return FALSE;
559     if (sizeof(buffer) - (++p - buffer) <= strlen(name)) return FALSE;
560     strcpy( p, name );
561     return DOSFS_GetFullName( buffer, TRUE, full_name );
562 }
563
564
565 /***********************************************************************
566  *           DIR_SearchPath
567  *
568  * Implementation of SearchPathA. 'win32' specifies whether the search
569  * order is Win16 (module path last) or Win32 (module path first).
570  *
571  * FIXME: should return long path names.
572  */
573 DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext,
574                       DOS_FULL_NAME *full_name, BOOL win32 )
575 {
576     LPCSTR p;
577     LPSTR tmp = NULL;
578     BOOL ret = TRUE;
579
580     /* First check the supplied parameters */
581
582     p = strrchr( name, '.' );
583     if (p && !strchr( p, '/' ) && !strchr( p, '\\' ))
584         ext = NULL;  /* Ignore the specified extension */
585     if ((*name && (name[1] == ':')) ||
586         strchr( name, '/' ) || strchr( name, '\\' ))
587         path = NULL;  /* Ignore path if name already contains a path */
588     if (path && !*path) path = NULL;  /* Ignore empty path */
589
590     /* Allocate a buffer for the file name and extension */
591
592     if (ext)
593     {
594         DWORD len = strlen(name) + strlen(ext);
595         if (!(tmp = HeapAlloc( GetProcessHeap(), 0, len + 1 )))
596         {
597             SetLastError( ERROR_OUTOFMEMORY );
598             return 0;
599         }
600         strcpy( tmp, name );
601         strcat( tmp, ext );
602         name = tmp;
603     }
604
605     /* If the name contains an explicit path, everything's easy */
606
607     if ((*name && (name[1] == ':')) || strchr( name, '/' ) || strchr( name, '\\' ))
608     {
609         ret = DOSFS_GetFullName( name, TRUE, full_name );
610         goto done;
611     }
612
613     /* Search in the specified path */
614
615     if (path)
616     {
617         ret = DIR_TryEnvironmentPath( name, full_name, path );
618         goto done;
619     }
620
621     /* Try the path of the current executable (for Win32 search order) */
622
623     if (win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
624
625     /* Try the current directory */
626
627     if (DOSFS_GetFullName( name, TRUE, full_name )) goto done;
628
629     /* Try the Windows system directory */
630
631     if (DIR_TryPath( &DIR_System, name, full_name ))
632         goto done;
633
634     /* Try the Windows directory */
635
636     if (DIR_TryPath( &DIR_Windows, name, full_name ))
637         goto done;
638
639     /* Try the path of the current executable (for Win16 search order) */
640
641     if (!win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
642
643     /* Try all directories in path */
644
645     ret = DIR_TryEnvironmentPath( name, full_name, NULL );
646
647 done:
648     if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
649     return ret;
650 }
651
652
653 /***********************************************************************
654  * SearchPathA [KERNEL32.@]
655  *
656  * Searches for a specified file in the search path.
657  *
658  * PARAMS
659  *    path      [I] Path to search
660  *    name      [I] Filename to search for.
661  *    ext       [I] File extension to append to file name. The first
662  *                  character must be a period. This parameter is
663  *                  specified only if the filename given does not
664  *                  contain an extension.
665  *    buflen    [I] size of buffer, in characters
666  *    buffer    [O] buffer for found filename
667  *    lastpart  [O] address of pointer to last used character in
668  *                  buffer (the final '\')
669  *
670  * RETURNS
671  *    Success: length of string copied into buffer, not including
672  *             terminating null character. If the filename found is
673  *             longer than the length of the buffer, the length of the
674  *             filename is returned.
675  *    Failure: Zero
676  * 
677  * NOTES
678  *    If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
679  *    (tested on NT 4.0)
680  */
681 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext, DWORD buflen,
682                             LPSTR buffer, LPSTR *lastpart )
683 {
684     LPSTR p, res;
685     DOS_FULL_NAME full_name;
686
687     if (!DIR_SearchPath( path, name, ext, &full_name, TRUE ))
688     {
689         SetLastError(ERROR_FILE_NOT_FOUND);
690         return 0;
691     }
692     lstrcpynA( buffer, full_name.short_name, buflen );
693     res = full_name.long_name +
694               strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
695     while (*res == '/') res++;
696     if (buflen)
697     {
698         if (buflen > 3) lstrcpynA( buffer + 3, res, buflen - 3 );
699         for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
700         if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1;
701     }
702     TRACE("Returning %d\n", strlen(res) + 3 );
703     return strlen(res) + 3;
704 }
705
706
707 /***********************************************************************
708  *           SearchPathW   (KERNEL32.@)
709  */
710 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext,
711                             DWORD buflen, LPWSTR buffer, LPWSTR *lastpart )
712 {
713     LPWSTR p;
714     LPSTR res;
715     DOS_FULL_NAME full_name;
716
717     LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
718     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
719     LPSTR extA  = HEAP_strdupWtoA( GetProcessHeap(), 0, ext );
720     DWORD ret = DIR_SearchPath( pathA, nameA, extA, &full_name, TRUE );
721     HeapFree( GetProcessHeap(), 0, extA );
722     HeapFree( GetProcessHeap(), 0, nameA );
723     HeapFree( GetProcessHeap(), 0, pathA );
724     if (!ret) return 0;
725
726     if (buflen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, buffer, buflen ))
727         buffer[buflen-1] = 0;
728     res = full_name.long_name +
729               strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
730     while (*res == '/') res++;
731     if (buflen)
732     {
733         if (buflen > 3)
734         {
735             if (!MultiByteToWideChar( CP_ACP, 0, res, -1, buffer+3, buflen-3 ))
736                 buffer[buflen-1] = 0;
737         }
738         for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
739         if (lastpart)
740         {
741             for (p = *lastpart = buffer; *p; p++)
742                 if (*p == '\\') *lastpart = p + 1;
743         }
744     }
745     return strlen(res) + 3;
746 }
747
748