chore: initial commit (extracted from Launchers monorepo)
Plugin: ns7zip v2.0.0 Architectures: x86-ansi, x86-unicode, amd64-unicode License: LGPL-2.1-or-later
This commit is contained in:
@@ -0,0 +1,402 @@
|
||||
// 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;
|
||||
FillLinkData(data2, attr.GetPath(),
|
||||
!attr.IsMountPoint(), attr.IsSymLink_WSL());
|
||||
if (data2.Size() == 0)
|
||||
{
|
||||
errorMessage = "Cannot reproduce reparse point";
|
||||
return false;
|
||||
}
|
||||
if (data2 != buf)
|
||||
{
|
||||
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())
|
||||
{
|
||||
const DWORD lastError = GetLastError();
|
||||
if (lastError)
|
||||
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;
|
||||
}
|
||||
|
||||
CByteBuffer data;
|
||||
const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION);
|
||||
FillLinkData(data, to, isSymLink, isWSL);
|
||||
if (data.Size() == 0)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
CSelectedState srcSelState;
|
||||
srcPanel.SaveSelectedState(srcSelState);
|
||||
|
||||
CLinkDialog dlg;
|
||||
dlg.CurDirPrefix = fsPrefix;
|
||||
dlg.FilePath = srcPath + itemName;
|
||||
dlg.AnotherPath = path;
|
||||
|
||||
if (dlg.Create(srcPanel.GetParent()) != IDOK)
|
||||
return;
|
||||
|
||||
// we refresh srcPanel to show changes in "Link" (kpidNtReparse) column.
|
||||
// maybe we should refresh another panel also?
|
||||
if (srcPanel._visibleColumns.FindItem_for_PropID(kpidNtReparse) >= 0)
|
||||
srcPanel.RefreshListCtrl(srcSelState);
|
||||
|
||||
RefreshTitleAlways();
|
||||
}
|
||||
Reference in New Issue
Block a user