Skip to content

Commit

Permalink
#3505; exec: improvements for name w/o extension & search logic
Browse files Browse the repository at this point in the history
  • Loading branch information
alabuzhev committed Sep 24, 2017
1 parent feaf0b6 commit ad97c98
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 95 deletions.
10 changes: 8 additions & 2 deletions far/changelog
@@ -1,4 +1,10 @@
zg 23.09.2017 15:25:28 FLE Daylight Time - build 5037
drkns 24.09.2017 16:59:37 +0100 - build 5038

1. 0003505: FarMenu.ini может оказаться папкой - не пытаемся в этом случае его выводить.

2. Некоторые уточнения в запускателе на тему файлов без расширений и логики поиска запускаемого файла.

zg 23.09.2017 15:25:28 +0300 - build 5037

1. при сохранении файла в редакторе текущей устанавливалась папка, в которой редактор был запущен.
убрано.
Expand Down Expand Up @@ -66,7 +72,7 @@ drkns 09.09.2017 17:32:31 +0100 - build 5025

1. 0003485: Exception при попытке просмотра некорректного? hlf

zg 03.09.2017 14:24:00 FLE Daylight Time - build 5024
zg 03.09.2017 14:24:00 +0300 - build 5024

1. при удалении выделения в неактивном редакторе, он перерисовывался поверх всех окон.

Expand Down
219 changes: 129 additions & 90 deletions far/execute.cpp
Expand Up @@ -179,123 +179,154 @@ static bool FindObject(const string& Module, string &strDest, bool &Internal)
if (Module.empty())
return false;

// нулевой проход - смотрим исключения
// Берем "исключения" из реестра, которые должны исполняться директом,
// например, некоторые внутренние команды ком. процессора.
const auto ExcludeCmdsList = split<std::vector<string>>(os::env::expand(Global->Opt->Exec.strExcludeCmds), STLF_UNIQUE);
const auto ModuleExt = PointToExt(Module);
const auto strPathExt = os::env::get_pathext();
const auto PathExtList = enum_tokens(strPathExt, L";");

if (std::any_of(CONST_RANGE(ExcludeCmdsList, i) { return equal_icase(i, Module); }))
const auto& TryWithExtOrPathExt = [&](const string& Name, const auto& Predicate)
{
Internal = true;
return true;
}
if (*ModuleExt)
{
// Extension has been specified, we don't have to do anything here.
return Predicate(Name);
}

auto strFullName = Module;
const auto ModuleExt = wcsrchr(PointToName(Module), L'.');
const auto strPathExt = os::env::get_pathext() + L";;"; // ";;" to also try no extension if nothing else matches
const auto PathExtList = enum_tokens(strPathExt, L";");
for (const auto& i: PathExtList) // первый проход - в текущем каталоге
{
string strTmpName=strFullName;
{
// Try "as is" first.
// This is rather controversial, as, even though it's the best possible match,
// picking a name without extension might be unexpected on the current target platform.
// TODO: Consider doing this AFTER trying the %PATHEXT%.
const auto Result = Predicate(Name);
if (Result.first)
{
return Result;
}
}

if (!ModuleExt)
// Now try all the %PATHEXT%:
for (const auto& Ext: PathExtList)
{
append(strTmpName, i);
const auto NameWithExt = Name + Ext;
const auto Result = Predicate(NameWithExt);
if (Result.first)
return Result;
}

if (os::fs::is_file(strTmpName))
// Nothing has been found:
return std::make_pair(false, L""s);
};

if (IsAbsolutePath(Module))
{
// If absolute path has been specified it makes no sense to walk through the %PATH%, App Paths etc.
// Just try all the extensions and we are done here:
const auto Result = TryWithExtOrPathExt(Module, [](const string& NameWithExt)
{
strDest = ConvertNameToFull(strTmpName);
return std::make_pair(os::fs::is_file(NameWithExt), NameWithExt);
});

if (Result.first)
{
strDest = Result.second;
return true;
}
}

if (!*ModuleExt)
{
// Neither path nor extension has been specified, it could be some internal %COMSPEC% command:
const auto ExcludeCmdsList = split<std::vector<string>>(os::env::expand(Global->Opt->Exec.strExcludeCmds), STLF_UNIQUE);

if (ModuleExt)
if (std::any_of(CONST_RANGE(ExcludeCmdsList, i) { return equal_icase(i, Module); }))
{
break;
Internal = true;
return true;
}
}

// второй проход - по правилам SearchPath
const auto strPathEnv(os::env::get(L"PATH"));
if (!strPathEnv.empty())
{
for (const auto& Path: split<std::vector<string>>(strPathEnv, STLF_UNIQUE))
// Look in the current directory:
const auto FullName = ConvertNameToFull(Module);
const auto Result = TryWithExtOrPathExt(FullName, [](const string& NameWithExt)
{
for (const auto& Ext: PathExtList)
{
string Dest;
if (os::SearchPath(Path.data(), strFullName, make_string(Ext).data(), Dest))
{
if (os::fs::is_file(Dest))
{
strDest = Dest;
return true;
}
}
}
return std::make_pair(os::fs::is_file(NameWithExt), NameWithExt);
});

if (Result.first)
{
strDest = Result.second;
return true;
}
}

for (const auto& Ext: PathExtList)
{
string Dest;
if (os::SearchPath(nullptr, strFullName, make_string(Ext).data(), Dest))
// Look in the %PATH%:
const auto PathEnv = os::env::get(L"PATH");
if (!PathEnv.empty())
{
if (os::fs::is_file(Dest))
for (const auto& Path : split<std::vector<string>>(PathEnv, STLF_UNIQUE))
{
strDest = Dest;
return true;
auto FullName = Path;
AddEndSlash(FullName);
FullName += Module;

const auto Result = TryWithExtOrPathExt(FullName, [](const string& NameWithExt)
{
return std::make_pair(os::fs::is_file(NameWithExt), NameWithExt);
});

if (Result.first)
{
strDest = Result.second;
return true;
}
}
}
}

// третий проход - лезем в реестр в "App Paths"
if (Global->Opt->Exec.ExecuteUseAppPath && !contains(strFullName, L'\\'))
{
static const wchar_t RegPath[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
// В строке Module заменить исполняемый модуль на полный путь, который
// берется из SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
// Сначала смотрим в HKCU, затем - в HKLM
static const HKEY RootFindKey[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE };
strFullName = RegPath;
strFullName += Module;
// Look in the App Paths registry keys:
if (Global->Opt->Exec.ExecuteUseAppPath && !contains(Module, L'\\'))
{
static const wchar_t RegPath[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
static const HKEY RootFindKey[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE };
const auto FullName = RegPath + Module;

DWORD samDesired = KEY_QUERY_VALUE;
DWORD samDesired = KEY_QUERY_VALUE;

for (size_t i = 0; i < std::size(RootFindKey); i++)
{
if (i == std::size(RootFindKey) - 1)
for (size_t i = 0; i < std::size(RootFindKey); i++)
{
if (const auto RedirectionFlag = os::GetAppPathsRedirectionFlag())
if (i == std::size(RootFindKey) - 1)
{
samDesired |= RedirectionFlag;
if (const auto RedirectionFlag = os::GetAppPathsRedirectionFlag())
{
samDesired |= RedirectionFlag;
}
else
{
break;
}
}
else

const auto Result = TryWithExtOrPathExt(FullName, [&](const string& NameWithExt)
{
break;
}
}
string RealName;
if (os::reg::GetValue(RootFindKey[i], NameWithExt, L"", RealName, samDesired))
{
RealName = unquote(os::env::expand(RealName));
return std::make_pair(os::fs::is_file(RealName), RealName);
}

if (os::reg::GetValue(RootFindKey[i], strFullName, L"", strFullName, samDesired))
{
strDest = unquote(os::env::expand(strFullName));
return true;
return std::make_pair(false, L""s);
});

if (Result.first)
{
strDest = Result.second;
return true;
}
}
}

return std::any_of(CONST_RANGE(PathExtList, Ext)
{
strFullName = concat(RegPath, Module, Ext);

return std::any_of(CONST_RANGE(RootFindKey, i)
{
if (!os::reg::GetValue(i, strFullName, L"", strFullName))
return false;

strDest = unquote(os::env::expand(strFullName));
return true;
});
});
}

return false;
Expand Down Expand Up @@ -398,12 +429,18 @@ static const wchar_t* GetShellActionAndAssociatedApplicationImpl(const string& F
const wchar_t command_action[]=L"\\command";
Error = ERROR_SUCCESS;

const auto ExtPtr = wcsrchr(FileName.data(), L'.');
if (!ExtPtr)
return nullptr;
auto Ext = PointToExt(FileName);
if (!*Ext)
{
// Yes, no matter how mad it looks - it is possible to specify actions for empty extension too
Ext = L".";
}

if (!GetShellType(ExtPtr, strValue))
return nullptr;
if (!GetShellType(Ext, strValue))
{
// Type is absent, however, verbs could be specified right in the extension key
strValue = Ext;
}

if (const auto Key = os::reg::open_key(HKEY_CLASSES_ROOT, strValue.data(), KEY_QUERY_VALUE))
{
Expand Down Expand Up @@ -1290,15 +1327,17 @@ bool ExtractIfExistCommand(string &strCommandText)

bool IsExecutable(const string& Filename)
{
const auto DotPos = Filename.find_last_of('.');
if (DotPos == string::npos || DotPos == Filename.size() - 1)
const auto Ext = PointToExt(Filename);
if (!*Ext)
return false;

const auto Extension = lower(Filename.substr(DotPos + 1));

// these guys have specific association in Windows Registry: "%1" %*
// That means we can't find the associated program etc., so they shall be hard-coded.
return Extension == L"exe" || Extension == L"com" || Extension == L"bat" || Extension == L"cmd";
static const string_view Executables[] = { L"exe"_sv, L"cmd"_sv, L"com"_sv, L"bat"_sv };
return std::any_of(ALL_CONST_RANGE(Executables), [&](const string_view& Extension)
{
return equal_icase(Extension, Ext);
});
}

bool ExpandOSAliases(string &strStr)
Expand Down
5 changes: 3 additions & 2 deletions far/usermenu.cpp
Expand Up @@ -376,9 +376,10 @@ void UserMenu::ProcessUserMenu(bool ChooseMenuType, const string& MenuFileName)
m_Menu.clear();

// Пытаемся открыть файл на локальном диске
if (const auto MenuFile = os::fs::file(strMenuFileFullPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING))
if (os::fs::is_file(strMenuFileFullPath))
{
DeserializeMenu(m_Menu, MenuFile, m_MenuCP);
if (const auto MenuFile = os::fs::file(strMenuFileFullPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING))
DeserializeMenu(m_Menu, MenuFile, m_MenuCP);
}
else if (m_MenuMode != menu_mode::user)
{
Expand Down
2 changes: 1 addition & 1 deletion far/vbuild.m4
@@ -1 +1 @@
m4_define(BUILD,5037)m4_dnl
m4_define(BUILD,5038)m4_dnl

0 comments on commit ad97c98

Please sign in to comment.