Merged msacm and msacm32 dlls.
[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     chdir("/"); /* every other directory operations are done by WINE */ 
94
95     if (!(DIR_GetPath( "windows", "c:\\windows", &DIR_Windows )) ||
96         !(DIR_GetPath( "system", "c:\\windows\\system", &DIR_System )) ||
97         !(DIR_GetPath( "temp", "c:\\windows", &tmp_dir )))
98     {
99         PROFILE_UsageWineIni();
100         return 0;
101     }
102     if (-1 == access( tmp_dir.long_name, W_OK ))
103     {
104         if (errno==EACCES)
105         {
106                 MESSAGE("Warning: The Temporary Directory (as specified in your configuration file) is NOT writeable.\n");
107                 PROFILE_UsageWineIni();
108         }
109         else
110                 MESSAGE("Warning: Access to Temporary Directory failed (%s).\n",
111                     strerror(errno));
112     }
113
114     if (drive == -1)
115     {
116         drive = DIR_Windows.drive;
117         DRIVE_SetCurrentDrive( drive );
118         DRIVE_Chdir( drive, DIR_Windows.short_name + 2 );
119     }
120
121     PROFILE_GetWineIniString("wine", "path", "c:\\windows;c:\\windows\\system",
122                              path, sizeof(path) );
123     if (strchr(path, '/'))
124     {
125         MESSAGE("No '/' allowed in [wine] 'Path=' statement of wine.conf !\n");
126         PROFILE_UsageWineIni();
127         ExitProcess(1);
128     }
129
130     /* Set the environment variables */
131
132     SetEnvironmentVariableA( "PATH", path );
133     SetEnvironmentVariableA( "COMSPEC", "c:\\command.com" );
134     SetEnvironmentVariableA( "TEMP", tmp_dir.short_name );
135     SetEnvironmentVariableA( "windir", DIR_Windows.short_name );
136     SetEnvironmentVariableA( "winsysdir", DIR_System.short_name );
137
138     TRACE("WindowsDir = %s (%s)\n",
139           DIR_Windows.short_name, DIR_Windows.long_name );
140     TRACE("SystemDir  = %s (%s)\n",
141           DIR_System.short_name, DIR_System.long_name );
142     TRACE("TempDir    = %s (%s)\n",
143           tmp_dir.short_name, tmp_dir.long_name );
144     TRACE("Path       = %s\n", path );
145     TRACE("Cwd        = %c:\\%s\n",
146           'A' + drive, DRIVE_GetDosCwd( drive ) );
147
148     if (DIR_GetPath( "profile", "", &profile_dir ))
149     {
150         TRACE("USERPROFILE= %s\n", profile_dir.short_name );
151         SetEnvironmentVariableA( "USERPROFILE", profile_dir.short_name );
152     }   
153
154     TRACE("SYSTEMROOT = %s\n", DIR_Windows.short_name );
155     SetEnvironmentVariableA( "SYSTEMROOT", DIR_Windows.short_name );
156
157     return 1;
158 }
159
160
161 /***********************************************************************
162  *           GetTempPathA   (KERNEL32.292)
163  */
164 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
165 {
166     UINT ret;
167     if (!(ret = GetEnvironmentVariableA( "TMP", path, count )))
168         if (!(ret = GetEnvironmentVariableA( "TEMP", path, count )))
169             if (!(ret = GetCurrentDirectoryA( count, path )))
170                 return 0;
171     if (count && (ret < count - 1) && (path[ret-1] != '\\'))
172     {
173         path[ret++] = '\\';
174         path[ret]   = '\0';
175     }
176     return ret;
177 }
178
179
180 /***********************************************************************
181  *           GetTempPathW   (KERNEL32.293)
182  */
183 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
184 {
185     static const WCHAR tmp[]  = { 'T', 'M', 'P', 0 };
186     static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
187     UINT ret;
188     if (!(ret = GetEnvironmentVariableW( tmp, path, count )))
189         if (!(ret = GetEnvironmentVariableW( temp, path, count )))
190             if (!(ret = GetCurrentDirectoryW( count, path )))
191                 return 0;
192     if (count && (ret < count - 1) && (path[ret-1] != '\\'))
193     {
194         path[ret++] = '\\';
195         path[ret]   = '\0';
196     }
197     return ret;
198 }
199
200
201 /***********************************************************************
202  *           DIR_GetWindowsUnixDir
203  */
204 UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count )
205 {
206     if (path) lstrcpynA( path, DIR_Windows.long_name, count );
207     return strlen( DIR_Windows.long_name );
208 }
209
210
211 /***********************************************************************
212  *           DIR_GetSystemUnixDir
213  */
214 UINT DIR_GetSystemUnixDir( LPSTR path, UINT count )
215 {
216     if (path) lstrcpynA( path, DIR_System.long_name, count );
217     return strlen( DIR_System.long_name );
218 }
219
220
221 /***********************************************************************
222  *           GetTempDrive   (KERNEL.92)
223  */
224 BYTE WINAPI GetTempDrive( BYTE ignored )
225 {
226     char *buffer;
227     BYTE ret;
228     UINT len = GetTempPathA( 0, NULL );
229
230     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len + 1 )) )
231       return DRIVE_GetCurrentDrive() + 'A';
232
233     /* FIXME: apparently Windows does something with the ignored byte */
234     if (!GetTempPathA( len, buffer )) buffer[0] = 'C';
235     ret = buffer[0];
236     HeapFree( GetProcessHeap(), 0, buffer );
237     return toupper(ret);
238 }
239
240
241 UINT WINAPI WIN16_GetTempDrive( BYTE ignored )
242 {
243     /* A closer look at krnl386.exe shows what the SDK doesn't mention:
244      *
245      * returns:
246      *   AL: driveletter
247      *   AH: ':'                - yes, some kernel code even does stosw with
248      *                            the returned AX.
249      *   DX: 1 for success
250      */
251     return MAKELONG( GetTempDrive(ignored) | (':' << 8), 1 );
252 }
253
254
255 /***********************************************************************
256  *           GetWindowsDirectory16   (KERNEL.134)
257  */
258 UINT16 WINAPI GetWindowsDirectory16( LPSTR path, UINT16 count )
259 {
260     return (UINT16)GetWindowsDirectoryA( path, count );
261 }
262
263
264 /***********************************************************************
265  *           GetWindowsDirectoryA   (KERNEL32.311)
266  */
267 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
268 {
269     if (path) lstrcpynA( path, DIR_Windows.short_name, count );
270     return strlen( DIR_Windows.short_name );
271 }
272
273
274 /***********************************************************************
275  *           GetWindowsDirectoryW   (KERNEL32.312)
276  */
277 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
278 {
279     if (path) lstrcpynAtoW( path, DIR_Windows.short_name, count );
280     return strlen( DIR_Windows.short_name );
281 }
282
283
284 /***********************************************************************
285  *           GetSystemDirectory16   (KERNEL.135)
286  */
287 UINT16 WINAPI GetSystemDirectory16( LPSTR path, UINT16 count )
288 {
289     return (UINT16)GetSystemDirectoryA( path, count );
290 }
291
292
293 /***********************************************************************
294  *           GetSystemDirectoryA   (KERNEL32.282)
295  */
296 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
297 {
298     if (path) lstrcpynA( path, DIR_System.short_name, count );
299     return strlen( DIR_System.short_name );
300 }
301
302
303 /***********************************************************************
304  *           GetSystemDirectoryW   (KERNEL32.283)
305  */
306 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
307 {
308     if (path) lstrcpynAtoW( path, DIR_System.short_name, count );
309     return strlen( DIR_System.short_name );
310 }
311
312
313 /***********************************************************************
314  *           CreateDirectory16   (KERNEL.144)
315  */
316 BOOL16 WINAPI CreateDirectory16( LPCSTR path, LPVOID dummy )
317 {
318     TRACE_(file)("(%s,%p)\n", path, dummy );
319     return (BOOL16)CreateDirectoryA( path, NULL );
320 }
321
322
323 /***********************************************************************
324  *           CreateDirectoryA   (KERNEL32.39)
325  * RETURNS:
326  *      TRUE : success
327  *      FALSE : failure
328  *              ERROR_DISK_FULL:        on full disk
329  *              ERROR_ALREADY_EXISTS:   if directory name exists (even as file)
330  *              ERROR_ACCESS_DENIED:    on permission problems
331  *              ERROR_FILENAME_EXCED_RANGE: too long filename(s)
332  */
333 BOOL WINAPI CreateDirectoryA( LPCSTR path,
334                                   LPSECURITY_ATTRIBUTES lpsecattribs )
335 {
336     DOS_FULL_NAME full_name;
337
338     TRACE_(file)("(%s,%p)\n", path, lpsecattribs );
339     if (DOSFS_GetDevice( path ))
340     {
341         TRACE_(file)("cannot use device '%s'!\n",path);
342         SetLastError( ERROR_ACCESS_DENIED );
343         return FALSE;
344     }
345     if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
346     if (mkdir( full_name.long_name, 0777 ) == -1) {
347         WARN_(file)("Errno %i trying to create directory %s.\n", errno, full_name.long_name);
348         /* the FILE_SetDosError() generated error codes don't match the 
349          * CreateDirectory ones for some errnos */
350         switch (errno) {
351         case EEXIST: SetLastError(ERROR_ALREADY_EXISTS); break;
352         case ENOSPC: SetLastError(ERROR_DISK_FULL); break;
353         default: FILE_SetDosError();break;
354         }
355         return FALSE;
356     }
357     return TRUE;
358 }
359
360
361 /***********************************************************************
362  *           CreateDirectoryW   (KERNEL32.42)
363  */
364 BOOL WINAPI CreateDirectoryW( LPCWSTR path,
365                                   LPSECURITY_ATTRIBUTES lpsecattribs )
366 {
367     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
368     BOOL ret = CreateDirectoryA( xpath, lpsecattribs );
369     HeapFree( GetProcessHeap(), 0, xpath );
370     return ret;
371 }
372
373
374 /***********************************************************************
375  *           CreateDirectoryExA   (KERNEL32.40)
376  */
377 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path,
378                                     LPSECURITY_ATTRIBUTES lpsecattribs)
379 {
380     return CreateDirectoryA(path,lpsecattribs);
381 }
382
383
384 /***********************************************************************
385  *           CreateDirectoryExW   (KERNEL32.41)
386  */
387 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path,
388                                     LPSECURITY_ATTRIBUTES lpsecattribs)
389 {
390     return CreateDirectoryW(path,lpsecattribs);
391 }
392
393
394 /***********************************************************************
395  *           RemoveDirectory16   (KERNEL)
396  */
397 BOOL16 WINAPI RemoveDirectory16( LPCSTR path )
398 {
399     return (BOOL16)RemoveDirectoryA( path );
400 }
401
402
403 /***********************************************************************
404  *           RemoveDirectoryA   (KERNEL32.437)
405  */
406 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
407 {
408     DOS_FULL_NAME full_name;
409
410     TRACE_(file)("'%s'\n", path );
411
412     if (DOSFS_GetDevice( path ))
413     {
414         TRACE_(file)("cannot remove device '%s'!\n", path);
415         SetLastError( ERROR_FILE_NOT_FOUND );
416         return FALSE;
417     }
418     if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
419     if (rmdir( full_name.long_name ) == -1)
420     {
421         FILE_SetDosError();
422         return FALSE;
423     }
424     return TRUE;
425 }
426
427
428 /***********************************************************************
429  *           RemoveDirectoryW   (KERNEL32.438)
430  */
431 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
432 {
433     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
434     BOOL ret = RemoveDirectoryA( xpath );
435     HeapFree( GetProcessHeap(), 0, xpath );
436     return ret;
437 }
438
439
440 /***********************************************************************
441  *           DIR_TryPath
442  *
443  * Helper function for DIR_SearchPath.
444  */
445 static BOOL DIR_TryPath( const DOS_FULL_NAME *dir, LPCSTR name,
446                            DOS_FULL_NAME *full_name )
447 {
448     LPSTR p_l = full_name->long_name + strlen(dir->long_name) + 1;
449     LPSTR p_s = full_name->short_name + strlen(dir->short_name) + 1;
450
451     if ((p_s >= full_name->short_name + sizeof(full_name->short_name) - 14) ||
452         (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
453     {
454         SetLastError( ERROR_PATH_NOT_FOUND );
455         return FALSE;
456     }
457     if (!DOSFS_FindUnixName( dir->long_name, name, p_l,
458                    sizeof(full_name->long_name) - (p_l - full_name->long_name),
459                    p_s, !(DRIVE_GetFlags(dir->drive) & DRIVE_CASE_SENSITIVE) ))
460         return FALSE;
461     strcpy( full_name->long_name, dir->long_name );
462     p_l[-1] = '/';
463     strcpy( full_name->short_name, dir->short_name );
464     p_s[-1] = '\\';
465     return TRUE;
466 }
467
468
469 /***********************************************************************
470  *           DIR_TryEnvironmentPath
471  *
472  * Helper function for DIR_SearchPath.
473  */
474 static BOOL DIR_TryEnvironmentPath( LPCSTR name, DOS_FULL_NAME *full_name )
475 {
476     LPSTR path, next, buffer;
477     BOOL ret = FALSE;
478     INT len = strlen(name);
479     DWORD size = GetEnvironmentVariableA( "PATH", NULL, 0 );
480
481     if (!size) return FALSE;
482     if (!(path = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
483     if (!GetEnvironmentVariableA( "PATH", path, size )) goto done;
484     next = path;
485     while (!ret && next)
486     {
487         LPSTR cur = next;
488         while (*cur == ';') cur++;
489         if (!*cur) break;
490         next = strchr( cur, ';' );
491         if (next) *next++ = '\0';
492         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(cur) + len + 2)))
493             goto done;
494         strcpy( buffer, cur );
495         strcat( buffer, "\\" );
496         strcat( buffer, name );
497         ret = DOSFS_GetFullName( buffer, TRUE, full_name );
498         HeapFree( GetProcessHeap(), 0, buffer );
499     }
500
501 done:
502     HeapFree( GetProcessHeap(), 0, path );
503     return ret;
504 }
505
506
507 /***********************************************************************
508  *           DIR_TryModulePath
509  *
510  * Helper function for DIR_SearchPath.
511  */
512 static BOOL DIR_TryModulePath( LPCSTR name, DOS_FULL_NAME *full_name )
513 {
514     PDB *pdb = PROCESS_Current();
515
516     /* FIXME: for now, GetModuleFileNameA can't return more */
517     /* than OFS_MAXPATHNAME. This may change with Win32. */
518
519     char buffer[OFS_MAXPATHNAME];
520     LPSTR p;
521
522     if (pdb->flags & PDB32_WIN16_PROC) {
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 )) 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 )) 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