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