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