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