Make rename replace existing destinations on Windows.
[gnulib.git] / lib / rename.c
1 /* Work around rename bugs in some systems.  On SunOS 4.1.1_U1
2    and mips-dec-ultrix4.4, rename fails when the source file has
3    a trailing slash.  On mingw, rename fails when the destination
4    exists.
5
6    Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program 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
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21 /* written by Volker Borchert */
22
23 #include <config.h>
24 #undef rename
25
26 #if RENAME_DEST_EXISTS_BUG
27 /* This replacement must come first, otherwise when cross
28  * compiling to Windows we will guess that it has the trailing
29  * slash bug and entirely miss this one. */
30 #include <errno.h>
31
32 #define WIN32_LEAN_AND_MEAN
33 #include <windows.h>
34
35 /* Rename the file SRC to DST.  This replacement is necessary on
36    Windows, on which the system rename function will not replace
37    an existing DST.  */
38 int
39 rpl_rename (char const *src, char const *dst)
40 {
41   int error;
42
43   /* MoveFileEx works if SRC is a directory without any flags,
44      but fails with MOVEFILE_REPLACE_EXISTING, so try without
45      flags first. */
46   if (MoveFileEx (src, dst, 0))
47     return 0;
48
49   /* Retry with MOVEFILE_REPLACE_EXISTING if the move failed
50    * due to the destination already existing. */
51   error = GetLastError ();
52   if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS)
53     {
54       if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING))
55         return 0;
56
57       error = GetLastError ();
58     }
59
60   switch (error)
61     {
62     case ERROR_FILE_NOT_FOUND:
63     case ERROR_PATH_NOT_FOUND:
64     case ERROR_BAD_PATHNAME:
65     case ERROR_DIRECTORY:
66       errno = ENOENT;
67       break;
68
69     case ERROR_ACCESS_DENIED:
70     case ERROR_SHARING_VIOLATION:
71       errno = EACCES;
72       break;
73
74     case ERROR_OUTOFMEMORY:
75       errno = ENOMEM;
76       break;
77
78     case ERROR_CURRENT_DIRECTORY:
79       errno = EBUSY;
80       break;
81
82     case ERROR_NOT_SAME_DEVICE:
83       errno = EXDEV;
84       break;
85
86     case ERROR_WRITE_PROTECT:
87       errno = EROFS;
88       break;
89
90     case ERROR_WRITE_FAULT:
91     case ERROR_READ_FAULT:
92     case ERROR_GEN_FAILURE:
93       errno = EIO;
94       break;
95
96     case ERROR_HANDLE_DISK_FULL:
97     case ERROR_DISK_FULL:
98     case ERROR_DISK_TOO_FRAGMENTED:
99       errno = ENOSPC;
100       break;
101
102     case ERROR_FILE_EXISTS:
103     case ERROR_ALREADY_EXISTS:
104       errno = EEXIST;
105       break;
106
107     case ERROR_BUFFER_OVERFLOW:
108     case ERROR_FILENAME_EXCED_RANGE:
109       errno = ENAMETOOLONG;
110       break;
111
112     case ERROR_INVALID_NAME:
113     case ERROR_DELETE_PENDING:
114       errno = EPERM;        /* ? */
115       break;
116
117 #ifndef ERROR_FILE_TOO_LARGE
118 /* This value is documented but not defined in all versions of windows.h. */
119 #define ERROR_FILE_TOO_LARGE 223
120 #endif
121     case ERROR_FILE_TOO_LARGE:
122       errno = EFBIG;
123       break;
124
125     default:
126       errno = EINVAL;
127       break;
128     }
129
130   return -1;
131 }
132 #elif RENAME_TRAILING_SLASH_BUG
133 #include <stdio.h>
134 #include <stdlib.h>
135 #include <string.h>
136
137 #include "dirname.h"
138 #include "xalloc.h"
139
140 /* Rename the file SRC to DST, removing any trailing
141    slashes from SRC.  Needed for SunOS 4.1.1_U1.  */
142
143 int
144 rpl_rename (char const *src, char const *dst)
145 {
146   char *src_temp;
147   int ret_val;
148   size_t s_len = strlen (src);
149
150   if (s_len && src[s_len - 1] == '/')
151     {
152       src_temp = xstrdup (src);
153       strip_trailing_slashes (src_temp);
154     }
155   else
156     src_temp = (char *) src;
157
158   ret_val = rename (src_temp, dst);
159
160   if (src_temp != src)
161     free (src_temp);
162
163   return ret_val;
164 }
165 #endif /* RENAME_TRAILING_SLASH_BUG */