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