1 #include "rar.hpp"
2
FileCreate(RAROptions * Cmd,File * NewFile,char * Name,wchar * NameW,OVERWRITE_MODE Mode,bool * UserReject,int64 FileSize,uint FileTime)3 bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
4 OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize,
5 uint FileTime)
6 {
7 if (UserReject!=NULL)
8 *UserReject=false;
9 #if defined(_WIN_ALL) && !defined(_WIN_CE)
10 bool ShortNameChanged=false;
11 #endif
12 while (FileExist(Name,NameW))
13 {
14 #if defined(_WIN_ALL) && !defined(_WIN_CE)
15 if (!ShortNameChanged)
16 {
17 // Avoid the infinite loop if UpdateExistingShortName returns
18 // the same name.
19 ShortNameChanged=true;
20
21 // Maybe our long name matches the short name of existing file.
22 // Let's check if we can change the short name.
23 wchar WideName[NM];
24 GetWideName(Name,NameW,WideName,ASIZE(WideName));
25 if (UpdateExistingShortName(WideName))
26 {
27 if (Name!=NULL && *Name!=0)
28 WideToChar(WideName,Name);
29 if (NameW!=NULL && *NameW!=0)
30 wcscpy(NameW,WideName);
31 continue;
32 }
33 }
34 // Allow short name check again. It is necessary, because rename and
35 // autorename below can change the name, so we need to check it again.
36 ShortNameChanged=false;
37 #endif
38 if (Mode==OVERWRITE_NONE)
39 {
40 if (UserReject!=NULL)
41 *UserReject=true;
42 return(false);
43 }
44
45 // Must be before Cmd->AllYes check or -y switch would override -or.
46 if (Mode==OVERWRITE_AUTORENAME)
47 {
48 if (!GetAutoRenamedName(Name,NameW))
49 Mode=OVERWRITE_DEFAULT;
50 continue;
51 }
52
53 #ifdef SILENT
54 Mode=OVERWRITE_ALL;
55 #endif
56
57 // This check must be after OVERWRITE_AUTORENAME processing or -y switch
58 // would override -or.
59 if (Cmd->AllYes || Mode==OVERWRITE_ALL)
60 break;
61
62 if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK)
63 {
64 char NewName[NM];
65 wchar NewNameW[NM];
66 *NewNameW=0;
67 eprintf(St(MFileExists),Name);
68 int Choice=Ask(St(MYesNoAllRenQ));
69 if (Choice==1)
70 break;
71 if (Choice==2)
72 {
73 if (UserReject!=NULL)
74 *UserReject=true;
75 return(false);
76 }
77 if (Choice==3)
78 {
79 Cmd->Overwrite=OVERWRITE_ALL;
80 break;
81 }
82 if (Choice==4)
83 {
84 if (UserReject!=NULL)
85 *UserReject=true;
86 Cmd->Overwrite=OVERWRITE_NONE;
87 return(false);
88 }
89 if (Choice==5)
90 {
91 #ifndef GUI
92 mprintf(St(MAskNewName));
93
94 #ifdef _WIN_ALL
95 File SrcFile;
96 SrcFile.SetHandleType(FILE_HANDLESTD);
97 int Size=SrcFile.Read(NewName,sizeof(NewName)-1);
98 NewName[Size]=0;
99 OemToCharA(NewName,NewName);
100 #else
101 if (fgets(NewName,sizeof(NewName),stdin)==NULL)
102 {
103 // Process fgets failure as if user answered 'No'.
104 if (UserReject!=NULL)
105 *UserReject=true;
106 return(false);
107 }
108 #endif
109 RemoveLF(NewName);
110 #endif
111 if (PointToName(NewName)==NewName)
112 strcpy(PointToName(Name),NewName);
113 else
114 strcpy(Name,NewName);
115
116 if (NameW!=NULL)
117 if (PointToName(NewNameW)==NewNameW)
118 wcscpy(PointToName(NameW),NewNameW);
119 else
120 wcscpy(NameW,NewNameW);
121 continue;
122 }
123 if (Choice==6)
124 ErrHandler.Exit(USER_BREAK);
125 }
126 }
127 if (NewFile!=NULL && NewFile->Create(Name,NameW))
128 return(true);
129 PrepareToDelete(Name,NameW);
130 CreatePath(Name,NameW,true);
131 return(NewFile!=NULL ? NewFile->Create(Name,NameW):DelFile(Name,NameW));
132 }
133
134
GetAutoRenamedName(char * Name,wchar * NameW)135 bool GetAutoRenamedName(char *Name,wchar *NameW)
136 {
137 char NewName[NM];
138 wchar NewNameW[NM];
139
140 if (Name!=NULL && strlen(Name)>ASIZE(NewName)-10 ||
141 NameW!=NULL && wcslen(NameW)>ASIZE(NewNameW)-10)
142 return(false);
143 char *Ext=NULL;
144 if (Name!=NULL && *Name!=0)
145 {
146 Ext=GetExt(Name);
147 if (Ext==NULL)
148 Ext=Name+strlen(Name);
149 }
150 wchar *ExtW=NULL;
151 if (NameW!=NULL && *NameW!=0)
152 {
153 ExtW=GetExt(NameW);
154 if (ExtW==NULL)
155 ExtW=NameW+wcslen(NameW);
156 }
157 *NewName=0;
158 *NewNameW=0;
159 for (int FileVer=1;;FileVer++)
160 {
161 if (Name!=NULL && *Name!=0)
162 sprintf(NewName,"%.*s(%d)%s",int(Ext-Name),Name,FileVer,Ext);
163 if (NameW!=NULL && *NameW!=0)
164 sprintfw(NewNameW,ASIZE(NewNameW),L"%.*s(%d)%s",int(ExtW-NameW),NameW,FileVer,ExtW);
165 if (!FileExist(NewName,NewNameW))
166 {
167 if (Name!=NULL && *Name!=0)
168 strcpy(Name,NewName);
169 if (NameW!=NULL && *NameW!=0)
170 wcscpy(NameW,NewNameW);
171 break;
172 }
173 if (FileVer>=1000000)
174 return(false);
175 }
176 return(true);
177 }
178
179
180 #if defined(_WIN_ALL) && !defined(_WIN_CE)
181 // If we find a file, which short name is equal to 'Name', we try to change
182 // its short name, while preserving the long name. It helps when unpacking
183 // an archived file, which long name is equal to short name of already
184 // existing file. Otherwise we would overwrite the already existing file,
185 // even though its long name does not match the name of unpacking file.
UpdateExistingShortName(wchar * Name)186 bool UpdateExistingShortName(wchar *Name)
187 {
188 // 'Name' is the name of file which we want to create. Let's check
189 // if file with such name is exist. If it does not, we return.
190 FindData fd;
191 if (!FindFile::FastFind(NULL,Name,&fd))
192 return(false);
193
194 // We continue only if file has a short name, which does not match its
195 // long name, and this short name is equal to name of file which we need
196 // to create.
197 if (*fd.ShortName==0 || wcsicomp(PointToName(fd.NameW),fd.ShortName)==0 ||
198 wcsicomp(PointToName(Name),fd.ShortName)!=0)
199 return(false);
200
201 // Generate the temporary new name for existing file.
202 wchar NewName[NM];
203 *NewName=0;
204 for (int I=0;I<10000 && *NewName==0;I+=123)
205 {
206 // Here we copy the path part of file to create. We'll make the temporary
207 // file in the same folder.
208 wcsncpyz(NewName,Name,ASIZE(NewName));
209
210 // Here we set the random name part.
211 sprintfw(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
212
213 // If such file is already exist, try next random name.
214 if (FileExist(NULL,NewName))
215 *NewName=0;
216 }
217
218 // If we could not generate the name not used by any other file, we return.
219 if (*NewName==0)
220 return(false);
221
222 // FastFind returns the name without path, but we need the fully qualified
223 // name for renaming, so we use the path from file to create and long name
224 // from existing file.
225 wchar FullName[NM];
226 wcsncpyz(FullName,Name,ASIZE(FullName));
227 wcscpy(PointToName(FullName),PointToName(fd.NameW));
228
229 // Rename the existing file to randomly generated name. Normally it changes
230 // the short name too.
231 if (!MoveFileW(FullName,NewName))
232 return(false);
233
234 // Now we need to create the temporary empty file with same name as
235 // short name of our already existing file. We do it to occupy its previous
236 // short name and not allow to use it again when renaming the file back to
237 // its original long name.
238 File KeepShortFile;
239 bool Created=false;
240 if (!FileExist(NULL,Name))
241 Created=KeepShortFile.Create(NULL,Name);
242
243 // Now we rename the existing file from temporary name to original long name.
244 // Since its previous short name is occupied by another file, it should
245 // get another short name.
246 MoveFileW(NewName,FullName);
247
248 if (Created)
249 {
250 // Delete the temporary zero length file occupying the short name,
251 KeepShortFile.Close();
252 KeepShortFile.Delete();
253 }
254 // We successfully changed the short name. Maybe sometimes we'll simplify
255 // this function by use of SetFileShortName Windows API call.
256 // But SetFileShortName is not available in older Windows.
257 return(true);
258 }
259 #endif
260