260 lines
7.5 KiB
C++
260 lines
7.5 KiB
C++
#include "rar.hpp"
|
|
|
|
bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
|
|
OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize,
|
|
uint FileTime,bool WriteOnly)
|
|
{
|
|
if (UserReject!=NULL)
|
|
*UserReject=false;
|
|
#if defined(_WIN_ALL) && !defined(_WIN_CE)
|
|
bool ShortNameChanged=false;
|
|
#endif
|
|
while (FileExist(Name,NameW))
|
|
{
|
|
#if defined(_WIN_ALL) && !defined(_WIN_CE)
|
|
if (!ShortNameChanged)
|
|
{
|
|
// Avoid the infinite loop if UpdateExistingShortName returns
|
|
// the same name.
|
|
ShortNameChanged=true;
|
|
|
|
// Maybe our long name matches the short name of existing file.
|
|
// Let's check if we can change the short name.
|
|
wchar WideName[NM];
|
|
GetWideName(Name,NameW,WideName,ASIZE(WideName));
|
|
if (UpdateExistingShortName(WideName))
|
|
{
|
|
if (Name!=NULL && *Name!=0)
|
|
WideToChar(WideName,Name);
|
|
if (NameW!=NULL && *NameW!=0)
|
|
wcscpy(NameW,WideName);
|
|
continue;
|
|
}
|
|
}
|
|
// Allow short name check again. It is necessary, because rename and
|
|
// autorename below can change the name, so we need to check it again.
|
|
ShortNameChanged=false;
|
|
#endif
|
|
if (Mode==OVERWRITE_NONE)
|
|
{
|
|
if (UserReject!=NULL)
|
|
*UserReject=true;
|
|
return(false);
|
|
}
|
|
|
|
// Must be before Cmd->AllYes check or -y switch would override -or.
|
|
if (Mode==OVERWRITE_AUTORENAME)
|
|
{
|
|
if (!GetAutoRenamedName(Name,NameW))
|
|
Mode=OVERWRITE_DEFAULT;
|
|
continue;
|
|
}
|
|
|
|
#ifdef SILENT
|
|
Mode=OVERWRITE_ALL;
|
|
#endif
|
|
|
|
// This check must be after OVERWRITE_AUTORENAME processing or -y switch
|
|
// would override -or.
|
|
if (Cmd->AllYes || Mode==OVERWRITE_ALL)
|
|
break;
|
|
|
|
if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK)
|
|
{
|
|
char NewName[NM];
|
|
wchar NewNameW[NM];
|
|
*NewNameW=0;
|
|
eprintf(St(MFileExists),Name);
|
|
int Choice=Ask(St(MYesNoAllRenQ));
|
|
if (Choice==1)
|
|
break;
|
|
if (Choice==2)
|
|
{
|
|
if (UserReject!=NULL)
|
|
*UserReject=true;
|
|
return(false);
|
|
}
|
|
if (Choice==3)
|
|
{
|
|
Cmd->Overwrite=OVERWRITE_ALL;
|
|
break;
|
|
}
|
|
if (Choice==4)
|
|
{
|
|
if (UserReject!=NULL)
|
|
*UserReject=true;
|
|
Cmd->Overwrite=OVERWRITE_NONE;
|
|
return(false);
|
|
}
|
|
if (Choice==5)
|
|
{
|
|
#ifndef GUI
|
|
mprintf(St(MAskNewName));
|
|
|
|
#ifdef _WIN_ALL
|
|
File SrcFile;
|
|
SrcFile.SetHandleType(FILE_HANDLESTD);
|
|
int Size=SrcFile.Read(NewName,sizeof(NewName)-1);
|
|
NewName[Size]=0;
|
|
OemToCharA(NewName,NewName);
|
|
#else
|
|
if (fgets(NewName,sizeof(NewName),stdin)==NULL)
|
|
{
|
|
// Process fgets failure as if user answered 'No'.
|
|
if (UserReject!=NULL)
|
|
*UserReject=true;
|
|
return(false);
|
|
}
|
|
#endif
|
|
RemoveLF(NewName);
|
|
#endif
|
|
if (PointToName(NewName)==NewName)
|
|
strcpy(PointToName(Name),NewName);
|
|
else
|
|
strcpy(Name,NewName);
|
|
|
|
if (NameW!=NULL)
|
|
if (PointToName(NewNameW)==NewNameW)
|
|
wcscpy(PointToName(NameW),NewNameW);
|
|
else
|
|
wcscpy(NameW,NewNameW);
|
|
continue;
|
|
}
|
|
if (Choice==6)
|
|
ErrHandler.Exit(RARX_USERBREAK);
|
|
}
|
|
}
|
|
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
|
|
if (NewFile!=NULL && NewFile->Create(Name,NameW,FileMode))
|
|
return(true);
|
|
PrepareToDelete(Name,NameW);
|
|
CreatePath(Name,NameW,true);
|
|
return(NewFile!=NULL ? NewFile->Create(Name,NameW,FileMode):DelFile(Name,NameW));
|
|
}
|
|
|
|
|
|
bool GetAutoRenamedName(char *Name,wchar *NameW)
|
|
{
|
|
char NewName[NM];
|
|
wchar NewNameW[NM];
|
|
|
|
if (Name!=NULL && strlen(Name)>ASIZE(NewName)-10 ||
|
|
NameW!=NULL && wcslen(NameW)>ASIZE(NewNameW)-10)
|
|
return(false);
|
|
char *Ext=NULL;
|
|
if (Name!=NULL && *Name!=0)
|
|
{
|
|
Ext=GetExt(Name);
|
|
if (Ext==NULL)
|
|
Ext=Name+strlen(Name);
|
|
}
|
|
wchar *ExtW=NULL;
|
|
if (NameW!=NULL && *NameW!=0)
|
|
{
|
|
ExtW=GetExt(NameW);
|
|
if (ExtW==NULL)
|
|
ExtW=NameW+wcslen(NameW);
|
|
}
|
|
*NewName=0;
|
|
*NewNameW=0;
|
|
for (int FileVer=1;;FileVer++)
|
|
{
|
|
if (Name!=NULL && *Name!=0)
|
|
sprintf(NewName,"%.*s(%d)%s",int(Ext-Name),Name,FileVer,Ext);
|
|
if (NameW!=NULL && *NameW!=0)
|
|
sprintfw(NewNameW,ASIZE(NewNameW),L"%.*s(%d)%s",int(ExtW-NameW),NameW,FileVer,ExtW);
|
|
if (!FileExist(NewName,NewNameW))
|
|
{
|
|
if (Name!=NULL && *Name!=0)
|
|
strcpy(Name,NewName);
|
|
if (NameW!=NULL && *NameW!=0)
|
|
wcscpy(NameW,NewNameW);
|
|
break;
|
|
}
|
|
if (FileVer>=1000000)
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
|
|
#if defined(_WIN_ALL) && !defined(_WIN_CE)
|
|
// If we find a file, which short name is equal to 'Name', we try to change
|
|
// its short name, while preserving the long name. It helps when unpacking
|
|
// an archived file, which long name is equal to short name of already
|
|
// existing file. Otherwise we would overwrite the already existing file,
|
|
// even though its long name does not match the name of unpacking file.
|
|
bool UpdateExistingShortName(wchar *Name)
|
|
{
|
|
// 'Name' is the name of file which we want to create. Let's check
|
|
// if file with such name is exist. If it does not, we return.
|
|
FindData fd;
|
|
if (!FindFile::FastFind(NULL,Name,&fd))
|
|
return(false);
|
|
|
|
// We continue only if file has a short name, which does not match its
|
|
// long name, and this short name is equal to name of file which we need
|
|
// to create.
|
|
if (*fd.ShortName==0 || wcsicomp(PointToName(fd.NameW),fd.ShortName)==0 ||
|
|
wcsicomp(PointToName(Name),fd.ShortName)!=0)
|
|
return(false);
|
|
|
|
// Generate the temporary new name for existing file.
|
|
wchar NewName[NM];
|
|
*NewName=0;
|
|
for (int I=0;I<10000 && *NewName==0;I+=123)
|
|
{
|
|
// Here we copy the path part of file to create. We'll make the temporary
|
|
// file in the same folder.
|
|
wcsncpyz(NewName,Name,ASIZE(NewName));
|
|
|
|
// Here we set the random name part.
|
|
sprintfw(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
|
|
|
|
// If such file is already exist, try next random name.
|
|
if (FileExist(NULL,NewName))
|
|
*NewName=0;
|
|
}
|
|
|
|
// If we could not generate the name not used by any other file, we return.
|
|
if (*NewName==0)
|
|
return(false);
|
|
|
|
// FastFind returns the name without path, but we need the fully qualified
|
|
// name for renaming, so we use the path from file to create and long name
|
|
// from existing file.
|
|
wchar FullName[NM];
|
|
wcsncpyz(FullName,Name,ASIZE(FullName));
|
|
wcscpy(PointToName(FullName),PointToName(fd.NameW));
|
|
|
|
// Rename the existing file to randomly generated name. Normally it changes
|
|
// the short name too.
|
|
if (!MoveFileW(FullName,NewName))
|
|
return(false);
|
|
|
|
// Now we need to create the temporary empty file with same name as
|
|
// short name of our already existing file. We do it to occupy its previous
|
|
// short name and not allow to use it again when renaming the file back to
|
|
// its original long name.
|
|
File KeepShortFile;
|
|
bool Created=false;
|
|
if (!FileExist(NULL,Name))
|
|
Created=KeepShortFile.Create(NULL,Name);
|
|
|
|
// Now we rename the existing file from temporary name to original long name.
|
|
// Since its previous short name is occupied by another file, it should
|
|
// get another short name.
|
|
MoveFileW(NewName,FullName);
|
|
|
|
if (Created)
|
|
{
|
|
// Delete the temporary zero length file occupying the short name,
|
|
KeepShortFile.Close();
|
|
KeepShortFile.Delete();
|
|
}
|
|
// We successfully changed the short name. Maybe sometimes we'll simplify
|
|
// this function by use of SetFileShortName Windows API call.
|
|
// But SetFileShortName is not available in older Windows.
|
|
return(true);
|
|
}
|
|
#endif
|