Files
easy7zip/CPP/7zip/UI/FileManager/LinkDialog.cpp
Igor Pavlov a36c48cece 23.01
2023-12-22 17:17:05 +00:00

401 lines
9.1 KiB
C++
Executable File

// LinkDialog.cpp
#include "StdAfx.h"
#include "../../../Windows/ErrorMsg.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileFind.h"
#include "../../../Windows/FileIO.h"
#include "../../../Windows/FileName.h"
#include "LangUtils.h"
#include "BrowseDialog.h"
#include "CopyDialogRes.h"
#include "LinkDialog.h"
#include "resourceGui.h"
#include "App.h"
#include "resource.h"
extern bool g_SymLink_Supported;
using namespace NWindows;
using namespace NFile;
#ifdef Z7_LANG
static const UInt32 kLangIDs[] =
{
IDB_LINK_LINK,
IDT_LINK_PATH_FROM,
IDT_LINK_PATH_TO,
IDG_LINK_TYPE,
IDR_LINK_TYPE_HARD,
IDR_LINK_TYPE_SYM_FILE,
IDR_LINK_TYPE_SYM_DIR,
IDR_LINK_TYPE_JUNCTION,
IDR_LINK_TYPE_WSL
};
#endif
static bool GetSymLink(CFSTR path, CReparseAttr &attr, UString &errorMessage)
{
CByteBuffer buf;
if (!NIO::GetReparseData(path, buf, NULL))
return false;
if (!attr.Parse(buf, buf.Size()))
{
SetLastError(attr.ErrorCode);
return false;
}
CByteBuffer data2;
if (!FillLinkData(data2, attr.GetPath(),
!attr.IsMountPoint(), attr.IsSymLink_WSL()))
{
errorMessage = "Cannot reproduce reparse point";
return false;
}
if (data2.Size() != buf.Size() ||
memcmp(data2, buf, buf.Size()) != 0)
{
errorMessage = "mismatch for reproduced reparse point";
return false;
}
return true;
}
static const unsigned k_LinkType_Buttons[] =
{
IDR_LINK_TYPE_HARD,
IDR_LINK_TYPE_SYM_FILE,
IDR_LINK_TYPE_SYM_DIR,
IDR_LINK_TYPE_JUNCTION,
IDR_LINK_TYPE_WSL
};
void CLinkDialog::Set_LinkType_Radio(unsigned idb)
{
CheckRadioButton(
k_LinkType_Buttons[0],
k_LinkType_Buttons[Z7_ARRAY_SIZE(k_LinkType_Buttons) - 1],
idb);
}
bool CLinkDialog::OnInit()
{
#ifdef Z7_LANG
LangSetWindowText(*this, IDD_LINK);
LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
#endif
_pathFromCombo.Attach(GetItem(IDC_LINK_PATH_FROM));
_pathToCombo.Attach(GetItem(IDC_LINK_PATH_TO));
if (!FilePath.IsEmpty())
{
NFind::CFileInfo fi;
unsigned linkType = 0;
if (!fi.Find(us2fs(FilePath)))
linkType = IDR_LINK_TYPE_SYM_FILE;
else
{
if (fi.HasReparsePoint())
{
CReparseAttr attr;
UString error;
const bool res = GetSymLink(us2fs(FilePath), attr, error);
if (!res && error.IsEmpty())
{
DWORD lastError = GetLastError();
if (lastError != 0)
error = NError::MyFormatMessage(lastError);
}
UString s = attr.GetPath();
if (!attr.IsSymLink_WSL())
if (!attr.IsOkNamePair())
{
s += " : ";
s += attr.PrintName;
}
if (!res)
{
s.Insert(0, L"ERROR: ");
if (!error.IsEmpty())
{
s += " : ";
s += error;
}
}
SetItemText(IDT_LINK_PATH_TO_CUR, s);
const UString destPath = attr.GetPath();
_pathFromCombo.SetText(FilePath);
_pathToCombo.SetText(destPath);
// if (res)
{
if (attr.IsMountPoint())
linkType = IDR_LINK_TYPE_JUNCTION;
else if (attr.IsSymLink_WSL())
linkType = IDR_LINK_TYPE_WSL;
else if (attr.IsSymLink_Win())
{
linkType =
fi.IsDir() ?
IDR_LINK_TYPE_SYM_DIR :
IDR_LINK_TYPE_SYM_FILE;
// if (attr.IsRelative()) linkType = IDR_LINK_TYPE_SYM_RELATIVE;
}
if (linkType != 0)
Set_LinkType_Radio(linkType);
}
}
else
{
// no ReparsePoint
_pathFromCombo.SetText(AnotherPath);
_pathToCombo.SetText(FilePath);
if (fi.IsDir())
linkType = g_SymLink_Supported ?
IDR_LINK_TYPE_SYM_DIR :
IDR_LINK_TYPE_JUNCTION;
else
linkType = IDR_LINK_TYPE_HARD;
}
}
if (linkType != 0)
Set_LinkType_Radio(linkType);
}
NormalizeSize();
return CModalDialog::OnInit();
}
bool CLinkDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
{
int mx, my;
GetMargins(8, mx, my);
int bx1, bx2, by;
GetItemSizes(IDCANCEL, bx1, by);
GetItemSizes(IDB_LINK_LINK, bx2, by);
int yPos = ySize - my - by;
int xPos = xSize - mx - bx1;
InvalidateRect(NULL);
{
RECT r, r2;
GetClientRectOfItem(IDB_LINK_PATH_FROM, r);
GetClientRectOfItem(IDB_LINK_PATH_TO, r2);
int bx = RECT_SIZE_X(r);
int newButtonXpos = xSize - mx - bx;
MoveItem(IDB_LINK_PATH_FROM, newButtonXpos, r.top, bx, RECT_SIZE_Y(r));
MoveItem(IDB_LINK_PATH_TO, newButtonXpos, r2.top, bx, RECT_SIZE_Y(r2));
int newComboXsize = newButtonXpos - mx - mx;
ChangeSubWindowSizeX(_pathFromCombo, newComboXsize);
ChangeSubWindowSizeX(_pathToCombo, newComboXsize);
}
MoveItem(IDCANCEL, xPos, yPos, bx1, by);
MoveItem(IDB_LINK_LINK, xPos - mx - bx2, yPos, bx2, by);
return false;
}
bool CLinkDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
{
switch (buttonID)
{
case IDB_LINK_PATH_FROM:
OnButton_SetPath(false);
return true;
case IDB_LINK_PATH_TO:
OnButton_SetPath(true);
return true;
case IDB_LINK_LINK:
OnButton_Link();
return true;
}
return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
}
void CLinkDialog::OnButton_SetPath(bool to)
{
UString currentPath;
NWindows::NControl::CComboBox &combo = to ?
_pathToCombo :
_pathFromCombo;
combo.GetText(currentPath);
// UString title = "Specify a location for output folder";
const UString title = LangString(IDS_SET_FOLDER);
UString resultPath;
if (!MyBrowseForFolder(*this, title, currentPath, resultPath))
return;
NName::NormalizeDirPathPrefix(resultPath);
combo.SetCurSel(-1);
combo.SetText(resultPath);
}
void CLinkDialog::ShowError(const wchar_t *s)
{
::MessageBoxW(*this, s, L"7-Zip", MB_ICONERROR);
}
void CLinkDialog::ShowLastErrorMessage()
{
ShowError(NError::MyFormatMessage(GetLastError()));
}
void CLinkDialog::OnButton_Link()
{
UString from, to;
_pathFromCombo.GetText(from);
_pathToCombo.GetText(to);
if (from.IsEmpty())
return;
if (!NName::IsAbsolutePath(from))
from.Insert(0, CurDirPrefix);
unsigned idb = 0;
for (unsigned i = 0;; i++)
{
if (i >= Z7_ARRAY_SIZE(k_LinkType_Buttons))
return;
idb = k_LinkType_Buttons[i];
if (IsButtonCheckedBool(idb))
break;
}
NFind::CFileInfo info1, info2;
const bool finded1 = info1.Find(us2fs(from));
const bool finded2 = info2.Find(us2fs(to));
const bool isDirLink = (
idb == IDR_LINK_TYPE_SYM_DIR ||
idb == IDR_LINK_TYPE_JUNCTION);
const bool isWSL = (idb == IDR_LINK_TYPE_WSL);
if (!isWSL)
if ((finded1 && info1.IsDir() != isDirLink) ||
(finded2 && info2.IsDir() != isDirLink))
{
ShowError(L"Incorrect link type");
return;
}
if (idb == IDR_LINK_TYPE_HARD)
{
if (!NDir::MyCreateHardLink(us2fs(from), us2fs(to)))
{
ShowLastErrorMessage();
return;
}
}
else
{
if (finded1 && !info1.IsDir() && !info1.HasReparsePoint() && info1.Size != 0)
{
UString s ("WARNING: reparse point will hide the data of existing file");
s.Add_LF();
s += from;
ShowError(s);
return;
}
const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION);
CByteBuffer data;
if (!FillLinkData(data, to, isSymLink, isWSL))
{
ShowError(L"Incorrect link");
return;
}
CReparseAttr attr;
if (!attr.Parse(data, data.Size()))
{
ShowError(L"Internal conversion error");
return;
}
bool res;
if (to.IsEmpty())
{
// res = NIO::SetReparseData(us2fs(from), isDirLink, NULL, 0);
res = NIO::DeleteReparseData(us2fs(from));
}
else
res = NIO::SetReparseData(us2fs(from), isDirLink, data, (DWORD)data.Size());
if (!res)
{
ShowLastErrorMessage();
return;
}
}
End(IDOK);
}
void CApp::Link()
{
const unsigned srcPanelIndex = GetFocusedPanelIndex();
CPanel &srcPanel = Panels[srcPanelIndex];
if (!srcPanel.IsFSFolder())
{
srcPanel.MessageBox_Error_UnsupportOperation();
return;
}
CRecordVector<UInt32> indices;
srcPanel.Get_ItemIndices_Operated(indices);
if (indices.IsEmpty())
return;
if (indices.Size() != 1)
{
srcPanel.MessageBox_Error_LangID(IDS_SELECT_ONE_FILE);
return;
}
const UInt32 index = indices[0];
const UString itemName = srcPanel.GetItemName(index);
const UString fsPrefix = srcPanel.GetFsPath();
const UString srcPath = fsPrefix + srcPanel.GetItemPrefix(index);
UString path = srcPath;
{
const unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex);
CPanel &destPanel = Panels[destPanelIndex];
if (NumPanels > 1)
if (destPanel.IsFSFolder())
path = destPanel.GetFsPath();
}
CLinkDialog dlg;
dlg.CurDirPrefix = fsPrefix;
dlg.FilePath = srcPath + itemName;
dlg.AnotherPath = path;
if (dlg.Create(srcPanel.GetParent()) != IDOK)
return;
// fix it: we should refresh panel with changed link
RefreshTitleAlways();
}