Assorted spelling fixes.
[wine] / dlls / kernel / path.c
1 /*
2  * File handling functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  * Copyright 2003 Eric Pouech
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30
31 #define NONAMELESSUNION
32 #define NONAMELESSSTRUCT
33 #include "winerror.h"
34 #include "ntstatus.h"
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winreg.h"
38 #include "winternl.h"
39
40 #include "kernel_private.h"
41 #include "file.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(file);
46
47 #define MAX_PATHNAME_LEN        1024
48
49
50 /* check if a file name is for an executable file (.exe or .com) */
51 inline static BOOL is_executable( const WCHAR *name )
52 {
53     static const WCHAR exeW[] = {'.','e','x','e',0};
54     static const WCHAR comW[] = {'.','c','o','m',0};
55     int len = strlenW(name);
56
57     if (len < 4) return FALSE;
58     return (!strcmpiW( name + len - 4, exeW ) || !strcmpiW( name + len - 4, comW ));
59 }
60
61
62 /***********************************************************************
63  *           add_boot_rename_entry
64  *
65  * Adds an entry to the registry that is loaded when windows boots and
66  * checks if there are some files to be removed or renamed/moved.
67  * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
68  * non-NULL then the file is moved, otherwise it is deleted.  The
69  * entry of the registrykey is always appended with two zero
70  * terminated strings. If <fn2> is NULL then the second entry is
71  * simply a single 0-byte. Otherwise the second filename goes
72  * there. The entries are prepended with \??\ before the path and the
73  * second filename gets also a '!' as the first character if
74  * MOVEFILE_REPLACE_EXISTING is set. After the final string another
75  * 0-byte follows to indicate the end of the strings.
76  * i.e.:
77  * \??\D:\test\file1[0]
78  * !\??\D:\test\file1_renamed[0]
79  * \??\D:\Test|delete[0]
80  * [0]                        <- file is to be deleted, second string empty
81  * \??\D:\test\file2[0]
82  * !\??\D:\test\file2_renamed[0]
83  * [0]                        <- indicates end of strings
84  *
85  * or:
86  * \??\D:\test\file1[0]
87  * !\??\D:\test\file1_renamed[0]
88  * \??\D:\Test|delete[0]
89  * [0]                        <- file is to be deleted, second string empty
90  * [0]                        <- indicates end of strings
91  *
92  */
93 static BOOL add_boot_rename_entry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
94 {
95     static const WCHAR PreString[] = {'\\','?','?','\\',0};
96     static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
97                                       'F','i','l','e','R','e','n','a','m','e',
98                                       'O','p','e','r','a','t','i','o','n','s',0};
99     static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
100                                      'S','y','s','t','e','m','\\',
101                                      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
102                                      'C','o','n','t','r','o','l','\\',
103                                      'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
104     static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
105
106     OBJECT_ATTRIBUTES attr;
107     UNICODE_STRING nameW;
108     KEY_VALUE_PARTIAL_INFORMATION *info;
109     BOOL rc = FALSE;
110     HKEY Reboot = 0;
111     DWORD len0, len1, len2;
112     DWORD DataSize = 0;
113     BYTE *Buffer = NULL;
114     WCHAR *p;
115
116     attr.Length = sizeof(attr);
117     attr.RootDirectory = 0;
118     attr.ObjectName = &nameW;
119     attr.Attributes = 0;
120     attr.SecurityDescriptor = NULL;
121     attr.SecurityQualityOfService = NULL;
122     RtlInitUnicodeString( &nameW, SessionW );
123
124     if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
125     {
126         WARN("Error creating key for reboot managment [%s]\n",
127              "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
128         return FALSE;
129     }
130
131     len0 = strlenW(PreString);
132     len1 = strlenW(fn1) + len0 + 1;
133     if (fn2)
134     {
135         len2 = strlenW(fn2) + len0 + 1;
136         if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
137     }
138     else len2 = 1; /* minimum is the 0 characters for the empty second string */
139
140     /* convert characters to bytes */
141     len0 *= sizeof(WCHAR);
142     len1 *= sizeof(WCHAR);
143     len2 *= sizeof(WCHAR);
144
145     RtlInitUnicodeString( &nameW, ValueName );
146
147     /* First we check if the key exists and if so how many bytes it already contains. */
148     if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
149                          NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
150     {
151         if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
152             goto Quit;
153         if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
154                              Buffer, DataSize, &DataSize )) goto Quit;
155         info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
156         if (info->Type != REG_MULTI_SZ) goto Quit;
157         if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR);  /* remove terminating null (will be added back later) */
158     }
159     else
160     {
161         DataSize = info_size;
162         if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
163             goto Quit;
164     }
165
166     p = (WCHAR *)(Buffer + DataSize);
167     strcpyW( p, PreString );
168     strcatW( p, fn1 );
169     DataSize += len1;
170     if (fn2)
171     {
172         p = (WCHAR *)(Buffer + DataSize);
173         if (flags & MOVEFILE_REPLACE_EXISTING)
174             *p++ = '!';
175         strcpyW( p, PreString );
176         strcatW( p, fn2 );
177         DataSize += len2;
178     }
179     else
180     {
181         p = (WCHAR *)(Buffer + DataSize);
182         *p = 0;
183         DataSize += sizeof(WCHAR);
184     }
185
186     /* add final null */
187     p = (WCHAR *)(Buffer + DataSize);
188     *p = 0;
189     DataSize += sizeof(WCHAR);
190
191     rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
192
193  Quit:
194     if (Reboot) NtClose(Reboot);
195     if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
196     return(rc);
197 }
198
199
200 /***********************************************************************
201  *           GetFullPathNameW   (KERNEL32.@)
202  * NOTES
203  *   if the path closed with '\', *lastpart is 0
204  */
205 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
206                                LPWSTR *lastpart )
207 {
208     return RtlGetFullPathName_U(name, len * sizeof(WCHAR), buffer, lastpart) / sizeof(WCHAR);
209 }
210
211 /***********************************************************************
212  *           GetFullPathNameA   (KERNEL32.@)
213  * NOTES
214  *   if the path closed with '\', *lastpart is 0
215  */
216 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
217                                LPSTR *lastpart )
218 {
219     UNICODE_STRING nameW;
220     WCHAR bufferW[MAX_PATH];
221     DWORD ret, retW;
222
223     if (!name)
224     {
225         SetLastError(ERROR_INVALID_PARAMETER);
226         return 0;
227     }
228
229     if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
230     {
231         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
232         return 0;
233     }
234
235     retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
236
237     if (!retW)
238         ret = 0;
239     else if (retW > MAX_PATH)
240     {
241         SetLastError(ERROR_FILENAME_EXCED_RANGE);
242         ret = 0;
243     }
244     else
245     {
246         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
247         if (ret && ret <= len)
248         {
249             WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
250             ret--; /* length without 0 */
251
252             if (lastpart)
253             {
254                 LPSTR p = buffer + strlen(buffer) - 1;
255
256                 if (*p != '\\')
257                 {
258                     while ((p > buffer + 2) && (*p != '\\')) p--;
259                     *lastpart = p + 1;
260                 }
261                 else *lastpart = NULL;
262             }
263         }
264     }
265
266     RtlFreeUnicodeString(&nameW);
267     return ret;
268 }
269
270
271 /***********************************************************************
272  *           GetLongPathNameW   (KERNEL32.@)
273  *
274  * NOTES
275  *  observed (Win2000):
276  *  shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
277  *  shortpath="":   LastError=ERROR_PATH_NOT_FOUND, ret=0
278  */
279 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
280 {
281     WCHAR               tmplongpath[MAX_PATHNAME_LEN];
282     LPCWSTR             p;
283     DWORD               sp = 0, lp = 0;
284     DWORD               tmplen;
285     BOOL                unixabsolute = (shortpath[0] == '/');
286     WIN32_FIND_DATAW    wfd;
287     HANDLE              goit;
288
289     if (!shortpath)
290     {
291         SetLastError(ERROR_INVALID_PARAMETER);
292         return 0;
293     }
294     if (!shortpath[0])
295     {
296         SetLastError(ERROR_PATH_NOT_FOUND);
297         return 0;
298     }
299
300     TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
301
302     if (shortpath[0] == '\\' && shortpath[1] == '\\')
303     {
304         ERR("UNC pathname %s\n", debugstr_w(shortpath));
305         lstrcpynW( longpath, shortpath, longlen );
306         return strlenW(longpath);
307     }
308
309     /* check for drive letter */
310     if (!unixabsolute && shortpath[1] == ':' )
311     {
312         tmplongpath[0] = shortpath[0];
313         tmplongpath[1] = ':';
314         lp = sp = 2;
315     }
316
317     while (shortpath[sp])
318     {
319         /* check for path delimiters and reproduce them */
320         if (shortpath[sp] == '\\' || shortpath[sp] == '/')
321         {
322             if (!lp || tmplongpath[lp-1] != '\\')
323             {
324                 /* strip double "\\" */
325                 tmplongpath[lp++] = '\\';
326             }
327             tmplongpath[lp] = 0; /* terminate string */
328             sp++;
329             continue;
330         }
331
332         p = shortpath + sp;
333         if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
334         {
335             tmplongpath[lp++] = *p++;
336             tmplongpath[lp++] = *p++;
337         }
338         for (; *p && *p != '/' && *p != '\\'; p++);
339         tmplen = p - (shortpath + sp);
340         lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
341         /* Check if the file exists and use the existing file name */
342         goit = FindFirstFileW(tmplongpath, &wfd);
343         if (goit == INVALID_HANDLE_VALUE)
344         {
345             TRACE("not found %s!\n", debugstr_w(tmplongpath));
346             SetLastError ( ERROR_FILE_NOT_FOUND );
347             return 0;
348         }
349         FindClose(goit);
350         strcpyW(tmplongpath + lp, wfd.cFileName);
351         lp += strlenW(tmplongpath + lp);
352         sp += tmplen;
353     }
354     tmplen = strlenW(shortpath) - 1;
355     if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
356         (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
357         tmplongpath[lp++] = shortpath[tmplen];
358     tmplongpath[lp] = 0;
359
360     tmplen = strlenW(tmplongpath) + 1;
361     if (tmplen <= longlen)
362     {
363         strcpyW(longpath, tmplongpath);
364         TRACE("returning %s\n", debugstr_w(longpath));
365         tmplen--; /* length without 0 */
366     }
367
368     return tmplen;
369 }
370
371 /***********************************************************************
372  *           GetLongPathNameA   (KERNEL32.@)
373  */
374 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
375 {
376     UNICODE_STRING shortpathW;
377     WCHAR longpathW[MAX_PATH];
378     DWORD ret, retW;
379
380     if (!shortpath)
381     {
382         SetLastError(ERROR_INVALID_PARAMETER);
383         return 0;
384     }
385
386     TRACE("%s\n", debugstr_a(shortpath));
387
388     if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
389     {
390         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
391         return 0;
392     }
393
394     retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
395
396     if (!retW)
397         ret = 0;
398     else if (retW > MAX_PATH)
399     {
400         SetLastError(ERROR_FILENAME_EXCED_RANGE);
401         ret = 0;
402     }
403     else
404     {
405         ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
406         if (ret <= longlen)
407         {
408             WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
409             ret--; /* length without 0 */
410         }
411     }
412
413     RtlFreeUnicodeString(&shortpathW);
414     return ret;
415 }
416
417
418 /***********************************************************************
419  *           GetShortPathNameW   (KERNEL32.@)
420  *
421  * NOTES
422  *  observed:
423  *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
424  *  longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
425  *
426  * more observations ( with NT 3.51 (WinDD) ):
427  * longpath <= 8.3 -> just copy longpath to shortpath
428  * longpath > 8.3  ->
429  *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
430  *             b) file does exist     -> set the short filename.
431  * - trailing slashes are reproduced in the short name, even if the
432  *   file is not a directory
433  * - the absolute/relative path of the short name is reproduced like found
434  *   in the long name
435  * - longpath and shortpath may have the same address
436  * Peter Ganten, 1999
437  */
438 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
439 {
440     WCHAR               tmpshortpath[MAX_PATHNAME_LEN];
441     LPCWSTR             p;
442     DWORD               sp = 0, lp = 0;
443     DWORD               tmplen;
444     BOOL                unixabsolute = (longpath[0] == '/');
445     WIN32_FIND_DATAW    wfd;
446     HANDLE              goit;
447     UNICODE_STRING      ustr;
448     WCHAR               ustr_buf[8+1+3+1];
449
450     TRACE("%s\n", debugstr_w(longpath));
451
452     if (!longpath)
453     {
454         SetLastError(ERROR_INVALID_PARAMETER);
455         return 0;
456     }
457     if (!longpath[0])
458     {
459         SetLastError(ERROR_BAD_PATHNAME);
460         return 0;
461     }
462
463     /* check for drive letter */
464     if (!unixabsolute && longpath[1] == ':' )
465     {
466         tmpshortpath[0] = longpath[0];
467         tmpshortpath[1] = ':';
468         sp = lp = 2;
469     }
470
471     ustr.Buffer = ustr_buf;
472     ustr.Length = 0;
473     ustr.MaximumLength = sizeof(ustr_buf);
474
475     while (longpath[lp])
476     {
477         /* check for path delimiters and reproduce them */
478         if (longpath[lp] == '\\' || longpath[lp] == '/')
479         {
480             if (!sp || tmpshortpath[sp-1] != '\\')
481             {
482                 /* strip double "\\" */
483                 tmpshortpath[sp] = '\\';
484                 sp++;
485             }
486             tmpshortpath[sp] = 0; /* terminate string */
487             lp++;
488             continue;
489         }
490
491         for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
492         tmplen = p - (longpath + lp);
493         lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
494         /* Check, if the current element is a valid dos name */
495         if (tmplen <= 8+1+3+1)
496         {
497             BOOLEAN spaces;
498             memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
499             ustr_buf[tmplen] = '\0';
500             ustr.Length = tmplen * sizeof(WCHAR);
501             if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
502             {
503                 sp += tmplen;
504                 lp += tmplen;
505                 continue;
506             }
507         }
508
509         /* Check if the file exists and use the existing short file name */
510         goit = FindFirstFileW(tmpshortpath, &wfd);
511         if (goit == INVALID_HANDLE_VALUE) goto notfound;
512         FindClose(goit);
513         strcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
514         sp += strlenW(tmpshortpath + sp);
515         lp += tmplen;
516     }
517     tmpshortpath[sp] = 0;
518
519     tmplen = strlenW(tmpshortpath) + 1;
520     if (tmplen <= shortlen)
521     {
522         strcpyW(shortpath, tmpshortpath);
523         TRACE("returning %s\n", debugstr_w(shortpath));
524         tmplen--; /* length without 0 */
525     }
526
527     return tmplen;
528
529  notfound:
530     TRACE("not found!\n" );
531     SetLastError ( ERROR_FILE_NOT_FOUND );
532     return 0;
533 }
534
535 /***********************************************************************
536  *           GetShortPathNameA   (KERNEL32.@)
537  */
538 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
539 {
540     UNICODE_STRING longpathW;
541     WCHAR shortpathW[MAX_PATH];
542     DWORD ret, retW;
543
544     if (!longpath)
545     {
546         SetLastError(ERROR_INVALID_PARAMETER);
547         return 0;
548     }
549
550     TRACE("%s\n", debugstr_a(longpath));
551
552     if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
553     {
554         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
555         return 0;
556     }
557
558     retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
559
560     if (!retW)
561         ret = 0;
562     else if (retW > MAX_PATH)
563     {
564         SetLastError(ERROR_FILENAME_EXCED_RANGE);
565         ret = 0;
566     }
567     else
568     {
569         ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
570         if (ret <= shortlen)
571         {
572             WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
573             ret--; /* length without 0 */
574         }
575     }
576
577     RtlFreeUnicodeString(&longpathW);
578     return ret;
579 }
580
581
582 /***********************************************************************
583  *           GetTempPathA   (KERNEL32.@)
584  */
585 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
586 {
587     WCHAR pathW[MAX_PATH];
588     UINT ret;
589
590     ret = GetTempPathW(MAX_PATH, pathW);
591
592     if (!ret)
593         return 0;
594
595     if (ret > MAX_PATH)
596     {
597         SetLastError(ERROR_FILENAME_EXCED_RANGE);
598         return 0;
599     }
600
601     ret = WideCharToMultiByte(CP_ACP, 0, pathW, -1, NULL, 0, NULL, NULL);
602     if (ret <= count)
603     {
604         WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, count, NULL, NULL);
605         ret--; /* length without 0 */
606     }
607     return ret;
608 }
609
610
611 /***********************************************************************
612  *           GetTempPathW   (KERNEL32.@)
613  */
614 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
615 {
616     static const WCHAR tmp[]  = { 'T', 'M', 'P', 0 };
617     static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
618     WCHAR tmp_path[MAX_PATH];
619     UINT ret;
620
621     TRACE("%u,%p\n", count, path);
622
623     if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )))
624         if (!(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )))
625             if (!(ret = GetCurrentDirectoryW( MAX_PATH, tmp_path )))
626                 return 0;
627
628     if (ret > MAX_PATH)
629     {
630         SetLastError(ERROR_FILENAME_EXCED_RANGE);
631         return 0;
632     }
633
634     ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
635     if (!ret) return 0;
636
637     if (ret > MAX_PATH - 2)
638     {
639         SetLastError(ERROR_FILENAME_EXCED_RANGE);
640         return 0;
641     }
642
643     if (tmp_path[ret-1] != '\\')
644     {
645         tmp_path[ret++] = '\\';
646         tmp_path[ret]   = '\0';
647     }
648
649     ret++; /* add space for terminating 0 */
650
651     if (count)
652     {
653         lstrcpynW(path, tmp_path, count);
654         if (count >= ret)
655             ret--; /* return length without 0 */
656         else if (count < 4)
657             path[0] = 0; /* avoid returning ambiguous "X:" */
658     }
659
660     TRACE("returning %u, %s\n", ret, debugstr_w(path));
661     return ret;
662 }
663
664
665 /***********************************************************************
666  *           contains_pathW
667  *
668  * Check if the file name contains a path; helper for SearchPathW.
669  * A relative path is not considered a path unless it starts with ./ or ../
670  */
671 inline static BOOL contains_pathW (LPCWSTR name)
672 {
673     if (RtlDetermineDosPathNameType_U( name ) != RELATIVE_PATH) return TRUE;
674     if (name[0] != '.') return FALSE;
675     if (name[1] == '/' || name[1] == '\\') return TRUE;
676     return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
677 }
678
679
680 /***********************************************************************
681  * SearchPathW [KERNEL32.@]
682  *
683  * Searches for a specified file in the search path.
684  *
685  * PARAMS
686  *    path      [I] Path to search
687  *    name      [I] Filename to search for.
688  *    ext       [I] File extension to append to file name. The first
689  *                  character must be a period. This parameter is
690  *                  specified only if the filename given does not
691  *                  contain an extension.
692  *    buflen    [I] size of buffer, in characters
693  *    buffer    [O] buffer for found filename
694  *    lastpart  [O] address of pointer to last used character in
695  *                  buffer (the final '\')
696  *
697  * RETURNS
698  *    Success: length of string copied into buffer, not including
699  *             terminating null character. If the filename found is
700  *             longer than the length of the buffer, the length of the
701  *             filename is returned.
702  *    Failure: Zero
703  *
704  * NOTES
705  *    If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
706  *    (tested on NT 4.0)
707  */
708 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
709                           LPWSTR buffer, LPWSTR *lastpart )
710 {
711     DWORD ret = 0;
712
713     /* If the name contains an explicit path, ignore the path */
714
715     if (contains_pathW(name))
716     {
717         /* try first without extension */
718         if (RtlDoesFileExists_U( name ))
719             return GetFullPathNameW( name, buflen, buffer, lastpart );
720
721         if (ext)
722         {
723             LPCWSTR p = strrchrW( name, '.' );
724             if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
725                 ext = NULL;  /* Ignore the specified extension */
726         }
727
728         /* Allocate a buffer for the file name and extension */
729         if (ext)
730         {
731             LPWSTR tmp;
732             DWORD len = strlenW(name) + strlenW(ext);
733
734             if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
735             {
736                 SetLastError( ERROR_OUTOFMEMORY );
737                 return 0;
738             }
739             strcpyW( tmp, name );
740             strcatW( tmp, ext );
741             if (RtlDoesFileExists_U( tmp ))
742                 ret = GetFullPathNameW( tmp, buflen, buffer, lastpart );
743             HeapFree( GetProcessHeap(), 0, tmp );
744         }
745     }
746     else if (path && path[0])  /* search in the specified path */
747     {
748         ret = RtlDosSearchPath_U( path, name, ext, buflen * sizeof(WCHAR),
749                                   buffer, lastpart ) / sizeof(WCHAR);
750     }
751     else  /* search in the default path */
752     {
753         WCHAR *dll_path = MODULE_get_dll_load_path( NULL );
754
755         if (dll_path)
756         {
757             ret = RtlDosSearchPath_U( dll_path, name, ext, buflen * sizeof(WCHAR),
758                                       buffer, lastpart ) / sizeof(WCHAR);
759             HeapFree( GetProcessHeap(), 0, dll_path );
760         }
761         else
762         {
763             SetLastError( ERROR_OUTOFMEMORY );
764             return 0;
765         }
766     }
767
768     if (!ret) SetLastError( ERROR_FILE_NOT_FOUND );
769     else TRACE( "found %s\n", debugstr_w(buffer) );
770     return ret;
771 }
772
773
774 /***********************************************************************
775  *           SearchPathA   (KERNEL32.@)
776  */
777 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext,
778                           DWORD buflen, LPSTR buffer, LPSTR *lastpart )
779 {
780     UNICODE_STRING pathW, nameW, extW;
781     WCHAR bufferW[MAX_PATH];
782     DWORD ret, retW;
783
784     if (path) RtlCreateUnicodeStringFromAsciiz(&pathW, path);
785     else pathW.Buffer = NULL;
786     if (name) RtlCreateUnicodeStringFromAsciiz(&nameW, name);
787     else nameW.Buffer = NULL;
788     if (ext) RtlCreateUnicodeStringFromAsciiz(&extW, ext);
789     else extW.Buffer = NULL;
790
791     retW = SearchPathW(pathW.Buffer, nameW.Buffer, extW.Buffer, MAX_PATH, bufferW, NULL);
792
793     if (!retW)
794         ret = 0;
795     else if (retW > MAX_PATH)
796     {
797         SetLastError(ERROR_FILENAME_EXCED_RANGE);
798         ret = 0;
799     }
800     else
801     {
802         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
803         if (buflen >= ret)
804         {
805             WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buflen, NULL, NULL);
806             ret--; /* length without 0 */
807             if (lastpart) *lastpart = strrchr(buffer, '\\') + 1;
808         }
809     }
810
811     RtlFreeUnicodeString(&pathW);
812     RtlFreeUnicodeString(&nameW);
813     RtlFreeUnicodeString(&extW);
814     return ret;
815 }
816
817
818 /**************************************************************************
819  *           MoveFileExW   (KERNEL32.@)
820  */
821 BOOL WINAPI MoveFileExW( LPCWSTR source, LPCWSTR dest, DWORD flag )
822 {
823     FILE_BASIC_INFORMATION info;
824     UNICODE_STRING nt_name;
825     OBJECT_ATTRIBUTES attr;
826     IO_STATUS_BLOCK io;
827     NTSTATUS status;
828     HANDLE source_handle = 0, dest_handle;
829     ANSI_STRING source_unix, dest_unix;
830
831     TRACE("(%s,%s,%04lx)\n", debugstr_w(source), debugstr_w(dest), flag);
832
833     if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
834         return add_boot_rename_entry( source, dest, flag );
835
836     if (!dest)
837         return DeleteFileW( source );
838
839     /* check if we are allowed to rename the source */
840
841     if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL ))
842     {
843         SetLastError( ERROR_PATH_NOT_FOUND );
844         return FALSE;
845     }
846     source_unix.Buffer = NULL;
847     dest_unix.Buffer = NULL;
848     attr.Length = sizeof(attr);
849     attr.RootDirectory = 0;
850     attr.Attributes = OBJ_CASE_INSENSITIVE;
851     attr.ObjectName = &nt_name;
852     attr.SecurityDescriptor = NULL;
853     attr.SecurityQualityOfService = NULL;
854
855     status = NtOpenFile( &source_handle, 0, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT );
856     if (status == STATUS_SUCCESS)
857         status = wine_nt_to_unix_file_name( &nt_name, &source_unix, FILE_OPEN, FALSE );
858     RtlFreeUnicodeString( &nt_name );
859     if (status != STATUS_SUCCESS)
860     {
861         SetLastError( RtlNtStatusToDosError(status) );
862         goto error;
863     }
864     status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation );
865     if (status != STATUS_SUCCESS)
866     {
867         SetLastError( RtlNtStatusToDosError(status) );
868         goto error;
869     }
870
871     if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
872     {
873         if (flag & MOVEFILE_REPLACE_EXISTING)  /* cannot replace directory */
874         {
875             SetLastError( ERROR_INVALID_PARAMETER );
876             goto error;
877         }
878     }
879
880     /* we must have write access to the destination, and it must */
881     /* not exist except if MOVEFILE_REPLACE_EXISTING is set */
882
883     if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL ))
884     {
885         SetLastError( ERROR_PATH_NOT_FOUND );
886         goto error;
887     }
888     status = NtOpenFile( &dest_handle, GENERIC_READ | GENERIC_WRITE, &attr, &io, 0,
889                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
890     if (status == STATUS_SUCCESS)
891     {
892         NtClose( dest_handle );
893         if (!(flag & MOVEFILE_REPLACE_EXISTING))
894         {
895             SetLastError( ERROR_ALREADY_EXISTS );
896             RtlFreeUnicodeString( &nt_name );
897             goto error;
898         }
899     }
900     else if (status != STATUS_OBJECT_NAME_NOT_FOUND)
901     {
902         SetLastError( RtlNtStatusToDosError(status) );
903         RtlFreeUnicodeString( &nt_name );
904         goto error;
905     }
906
907     status = wine_nt_to_unix_file_name( &nt_name, &dest_unix, FILE_OPEN_IF, FALSE );
908     RtlFreeUnicodeString( &nt_name );
909     if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
910     {
911         SetLastError( RtlNtStatusToDosError(status) );
912         goto error;
913     }
914
915     /* now perform the rename */
916
917     if (rename( source_unix.Buffer, dest_unix.Buffer ) == -1)
918     {
919         if (errno == EXDEV && (flag & MOVEFILE_COPY_ALLOWED))
920         {
921             NtClose( source_handle );
922             RtlFreeAnsiString( &source_unix );
923             RtlFreeAnsiString( &dest_unix );
924             return (CopyFileW( source, dest, TRUE ) && DeleteFileW( source ));
925         }
926         FILE_SetDosError();
927         /* if we created the destination, remove it */
928         if (io.Information == FILE_CREATED) unlink( dest_unix.Buffer );
929         goto error;
930     }
931
932     /* fixup executable permissions */
933
934     if (is_executable( source ) != is_executable( dest ))
935     {
936         struct stat fstat;
937         if (stat( dest_unix.Buffer, &fstat ) != -1)
938         {
939             if (is_executable( dest ))
940                 /* set executable bit where read bit is set */
941                 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
942             else
943                 fstat.st_mode &= ~0111;
944             chmod( dest_unix.Buffer, fstat.st_mode );
945         }
946     }
947
948     NtClose( source_handle );
949     RtlFreeAnsiString( &source_unix );
950     RtlFreeAnsiString( &dest_unix );
951     return TRUE;
952
953 error:
954     if (source_handle) NtClose( source_handle );
955     RtlFreeAnsiString( &source_unix );
956     RtlFreeAnsiString( &dest_unix );
957     return FALSE;
958 }
959
960 /**************************************************************************
961  *           MoveFileExA   (KERNEL32.@)
962  */
963 BOOL WINAPI MoveFileExA( LPCSTR source, LPCSTR dest, DWORD flag )
964 {
965     UNICODE_STRING sourceW, destW;
966     BOOL ret;
967
968     if (!source)
969     {
970         SetLastError(ERROR_INVALID_PARAMETER);
971         return FALSE;
972     }
973
974     RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
975     if (dest) RtlCreateUnicodeStringFromAsciiz(&destW, dest);
976     else destW.Buffer = NULL;
977
978     ret = MoveFileExW( sourceW.Buffer, destW.Buffer, flag );
979
980     RtlFreeUnicodeString(&sourceW);
981     RtlFreeUnicodeString(&destW);
982     return ret;
983 }
984
985
986 /**************************************************************************
987  *           MoveFileW   (KERNEL32.@)
988  *
989  *  Move file or directory
990  */
991 BOOL WINAPI MoveFileW( LPCWSTR source, LPCWSTR dest )
992 {
993     return MoveFileExW( source, dest, MOVEFILE_COPY_ALLOWED );
994 }
995
996
997 /**************************************************************************
998  *           MoveFileA   (KERNEL32.@)
999  */
1000 BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest )
1001 {
1002     return MoveFileExA( source, dest, MOVEFILE_COPY_ALLOWED );
1003 }
1004
1005
1006 /***********************************************************************
1007  *           CreateDirectoryW   (KERNEL32.@)
1008  * RETURNS:
1009  *      TRUE : success
1010  *      FALSE : failure
1011  *              ERROR_DISK_FULL:        on full disk
1012  *              ERROR_ALREADY_EXISTS:   if directory name exists (even as file)
1013  *              ERROR_ACCESS_DENIED:    on permission problems
1014  *              ERROR_FILENAME_EXCED_RANGE: too long filename(s)
1015  */
1016 BOOL WINAPI CreateDirectoryW( LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1017 {
1018     OBJECT_ATTRIBUTES attr;
1019     UNICODE_STRING nt_name;
1020     IO_STATUS_BLOCK io;
1021     NTSTATUS status;
1022     HANDLE handle;
1023     BOOL ret = FALSE;
1024
1025     TRACE( "%s\n", debugstr_w(path) );
1026
1027     if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1028     {
1029         SetLastError( ERROR_PATH_NOT_FOUND );
1030         return FALSE;
1031     }
1032     attr.Length = sizeof(attr);
1033     attr.RootDirectory = 0;
1034     attr.Attributes = OBJ_CASE_INSENSITIVE;
1035     attr.ObjectName = &nt_name;
1036     attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL;
1037     attr.SecurityQualityOfService = NULL;
1038
1039     status = NtCreateFile( &handle, GENERIC_READ, &attr, &io, NULL,
1040                            FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_CREATE,
1041                            FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
1042
1043     if (status == STATUS_SUCCESS)
1044     {
1045         NtClose( handle );
1046         ret = TRUE;
1047     }
1048     else SetLastError( RtlNtStatusToDosError(status) );
1049
1050     RtlFreeUnicodeString( &nt_name );
1051     return ret;
1052 }
1053
1054
1055 /***********************************************************************
1056  *           CreateDirectoryA   (KERNEL32.@)
1057  */
1058 BOOL WINAPI CreateDirectoryA( LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1059 {
1060     UNICODE_STRING pathW;
1061     BOOL ret;
1062
1063     if (path)
1064     {
1065         if (!RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1066         {
1067             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1068             return FALSE;
1069         }
1070     }
1071     else pathW.Buffer = NULL;
1072     ret = CreateDirectoryW( pathW.Buffer, sa );
1073     RtlFreeUnicodeString( &pathW );
1074     return ret;
1075 }
1076
1077
1078 /***********************************************************************
1079  *           CreateDirectoryExA   (KERNEL32.@)
1080  */
1081 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1082 {
1083     UNICODE_STRING pathW, templateW;
1084     BOOL ret;
1085
1086     if (path)
1087     {
1088         if (!RtlCreateUnicodeStringFromAsciiz( &pathW, path ))
1089         {
1090             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1091             return FALSE;
1092         }
1093     }
1094     else pathW.Buffer = NULL;
1095
1096     if (template)
1097     {
1098         if (!RtlCreateUnicodeStringFromAsciiz( &templateW, template ))
1099         {
1100             RtlFreeUnicodeString( &pathW );
1101             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1102             return FALSE;
1103         }
1104     }
1105     else templateW.Buffer = NULL;
1106
1107     ret = CreateDirectoryExW( templateW.Buffer, pathW.Buffer, sa );
1108     RtlFreeUnicodeString( &pathW );
1109     RtlFreeUnicodeString( &templateW );
1110     return ret;
1111 }
1112
1113
1114 /***********************************************************************
1115  *           CreateDirectoryExW   (KERNEL32.@)
1116  */
1117 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1118 {
1119     return CreateDirectoryW( path, sa );
1120 }
1121
1122
1123 /***********************************************************************
1124  *           RemoveDirectoryW   (KERNEL32.@)
1125  */
1126 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
1127 {
1128     OBJECT_ATTRIBUTES attr;
1129     UNICODE_STRING nt_name;
1130     ANSI_STRING unix_name;
1131     IO_STATUS_BLOCK io;
1132     NTSTATUS status;
1133     HANDLE handle;
1134     BOOL ret = FALSE;
1135
1136     TRACE( "%s\n", debugstr_w(path) );
1137
1138     if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1139     {
1140         SetLastError( ERROR_PATH_NOT_FOUND );
1141         return FALSE;
1142     }
1143     attr.Length = sizeof(attr);
1144     attr.RootDirectory = 0;
1145     attr.Attributes = OBJ_CASE_INSENSITIVE;
1146     attr.ObjectName = &nt_name;
1147     attr.SecurityDescriptor = NULL;
1148     attr.SecurityQualityOfService = NULL;
1149
1150     status = NtOpenFile( &handle, GENERIC_READ, &attr, &io,
1151                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1152                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1153     if (status == STATUS_SUCCESS)
1154         status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
1155     RtlFreeUnicodeString( &nt_name );
1156
1157     if (status != STATUS_SUCCESS)
1158     {
1159         SetLastError( RtlNtStatusToDosError(status) );
1160         return FALSE;
1161     }
1162
1163     if (!(ret = (rmdir( unix_name.Buffer ) != -1))) FILE_SetDosError();
1164     RtlFreeAnsiString( &unix_name );
1165     NtClose( handle );
1166     return ret;
1167 }
1168
1169
1170 /***********************************************************************
1171  *           RemoveDirectoryA   (KERNEL32.@)
1172  */
1173 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
1174 {
1175     UNICODE_STRING pathW;
1176     BOOL ret = FALSE;
1177
1178     if (!path)
1179     {
1180         SetLastError(ERROR_INVALID_PARAMETER);
1181         return FALSE;
1182     }
1183
1184     if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1185     {
1186         ret = RemoveDirectoryW(pathW.Buffer);
1187         RtlFreeUnicodeString(&pathW);
1188     }
1189     else
1190         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1191     return ret;
1192 }
1193
1194
1195 /***********************************************************************
1196  *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1197  *
1198  * Return the full Unix file name for a given path.
1199  * Returned buffer must be freed by caller.
1200  */
1201 char *wine_get_unix_file_name( LPCWSTR dosW )
1202 {
1203     UNICODE_STRING nt_name;
1204     ANSI_STRING unix_name;
1205     NTSTATUS status;
1206
1207     if (!RtlDosPathNameToNtPathName_U( dosW, &nt_name, NULL, NULL )) return NULL;
1208     status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
1209     RtlFreeUnicodeString( &nt_name );
1210     if (status && status != STATUS_NO_SUCH_FILE) return NULL;
1211     return unix_name.Buffer;
1212 }