Skip to content

Instantly share code, notes, and snippets.

@kennykerr
Last active June 5, 2021 09:10
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kennykerr/d72b59a7674001f51431cb973df84cdd to your computer and use it in GitHub Desktop.
Save kennykerr/d72b59a7674001f51431cb973df84cdd to your computer and use it in GitHub Desktop.
Curious how SyncTools (https://kennykerr.ca/2013/01/04/synctools-for-sysinternals/) was implemented? Code circa 2012
#include"Precompiled.h"
static char const Constant_LinkExpression[] = ">([^<]+)<A HREF=\"([^\"]+)\">";
static char const Constant_DefaultUrl[] = "http://live.sysinternals.com/";
static char const Constant_DefaultIgnore[] = "*.sys;*.html;*.cnt;*.scr;*.hlp;*.txt;*.asp;*.aspx";
static char const Constant_Banner[] = "SyncTools for Sysinternals. Copyright (c) 2013 Kenny Kerr\n\n";
static char const Constant_SyncStatus[] = ".SyncStatus";
static char const Constant_SyncIgnore[] = ".SyncIgnore";
static UINT const Constant_SyncStatusFormat = 2;
static char const Report_CheckingForUpdates[] = "Checking for updates";
static char const Report_UpToDateSingular[] = "1 file is up to date\n";
static char const Report_UpToDatePlural[] = "%d files are up to date\n";
static char const Report_UpdateSingular[] = "1 update to download\n";
static char const Report_UpdatePlural[] = "%d updates to download\n";
static char const Report_NewSingular[] = "1 new file to download\n";
static char const Report_NewPlural[] = "%d new files to download\n";
static char const Report_UpdateNone[] = "No updates are available\n";
static char const Report_NewFile[] = "* %s";
static char const Report_UpdatedFile[] = "u %s";
static char const Error_UrlExpected[] = "The -u argument expects an URL";
static char const Error_DirectoryExpected[] = "The -d argument expects a directory";
static char const Error_InvalidArgument[] = "Only the -u and -d arguments may be used";
static char const Error_SaveStatusFailed[] = "Failed to save .SyncStatus > ";
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace std;
using namespace msl::utilities;
struct ProgressCallback : RuntimeClass<RuntimeClassFlags<ClassicCom>, IBindStatusCallback>
{
HANDLE m_console;
COORD m_pos;
CONSOLE_CURSOR_INFO m_show;
ProgressCallback() :
m_console(GetStdHandle(STD_OUTPUT_HANDLE))
{
VERIFY(GetConsoleCursorInfo(m_console, &m_show));
m_show.bVisible = false;
VERIFY(SetConsoleCursorInfo(m_console, &m_show));
}
~ProgressCallback()
{
m_show.bVisible = true;
VERIFY(SetConsoleCursorInfo(m_console, &m_show));
}
COORD GetPosition() const
{
CONSOLE_SCREEN_BUFFER_INFO info;
VERIFY(GetConsoleScreenBufferInfo(m_console, &info));
return info.dwCursorPosition;
}
void SetPosition(COORD const & pos) const
{
VERIFY(SetConsoleCursorPosition(m_console, pos));
}
void Before()
{
CONSOLE_SCREEN_BUFFER_INFO info;
VERIFY(GetConsoleScreenBufferInfo(m_console, &info));
m_pos = info.dwCursorPosition;
}
void After()
{
VERIFY(SetConsoleCursorPosition(m_console, m_pos));
printf(" ");
VERIFY(SetConsoleCursorPosition(m_console, m_pos));
}
HRESULT __stdcall OnProgress(ULONG progress, ULONG progressMax, ULONG, LPCWSTR)
{
if (0 < progress && progress <= progressMax)
{
float percentF = progress * 100.0f / progressMax;
UINT percentU = min(100U, static_cast<UINT>(percentF));
VERIFY(SetConsoleCursorPosition(m_console, m_pos));
printf("%3d%%", percentU);
}
return S_OK;
}
HRESULT __stdcall OnStartBinding(DWORD, IBinding *) { return E_NOTIMPL; }
HRESULT __stdcall GetPriority(LONG *) { return E_NOTIMPL; }
HRESULT __stdcall OnLowResource(DWORD) { return E_NOTIMPL; }
HRESULT __stdcall OnStopBinding(HRESULT, LPCWSTR) { return E_NOTIMPL; }
HRESULT __stdcall GetBindInfo(DWORD *, BINDINFO *) { return E_NOTIMPL; }
HRESULT __stdcall OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *) { return E_NOTIMPL; }
HRESULT __stdcall OnObjectAvailable(REFIID, IUnknown *) { return E_NOTIMPL; }
};
struct LessNoCase : public binary_function<CString, CString, bool>
{
bool operator()(CString const & left, CString const & right)
{
return 0 > left.CompareNoCase(right);
}
};
class MapFile
{
FileHandle m_mapping;
void const * m_view;
LARGE_INTEGER m_size;
public:
MapFile() :
m_view(),
m_size()
{
}
~MapFile()
{
if (m_view)
{
VERIFY(UnmapViewOfFile(m_view));
}
}
bool Map(PCSTR filename)
{
ASSERT(!m_mapping.IsValid());
ASSERT(nullptr == m_view);
FileHandle file(CreateFile(filename,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
nullptr));
if (!file.IsValid())
{
return false;
}
if (!GetFileSizeEx(file.Get(), &m_size))
{
return false;
}
if (0 == m_size.QuadPart)
{
SetLastError(ERROR_FILE_INVALID);
return false;
}
m_mapping.Attach(CreateFileMapping(file.Get(),
nullptr,
PAGE_READONLY,
m_size.HighPart,
m_size.LowPart,
nullptr));
if (!m_mapping.IsValid())
{
return false;
}
m_view = MapViewOfFile(m_mapping.Get(),
FILE_MAP_READ,
0, // offset high
0, // offset low
0); // entire file
return nullptr != m_view;
}
void const * View() const
{
ASSERT(m_view);
return m_view;
}
ULONGLONG Size() const
{
return m_size.QuadPart;
}
};
struct Download
{
Download(CString const & fileName,
CString const & signature,
bool newFile) :
FileName(fileName),
Signature(signature),
New(newFile)
{
}
CString FileName;
CString Signature;
bool New;
};
typedef map<CString, CString, LessNoCase> Status;
static void PrepareUrl(CString & url)
{
if (url.IsEmpty())
{
url = Constant_DefaultUrl;
}
else
{
if ('/' != url[url.GetLength() - 1])
{
url += '/';
}
}
}
static CString GetCurrentDirectory()
{
DWORD size = GetCurrentDirectoryA(0, nullptr);
ASSERT(size);
CString directory;
auto buffer = directory.GetBufferSetLength(size - 1);
VERIFY(GetCurrentDirectoryA(size, buffer));
return directory;
}
static bool PrepareDirectory(CString & directory)
{
CPath path;
if (directory.IsEmpty())
{
directory = GetCurrentDirectory();
}
else if (PathIsRelative(directory))
{
path.Combine(GetCurrentDirectory(), directory);
directory = path.m_strPath;
}
if (!PathFileExists(directory))
{
DWORD error = SHCreateDirectoryEx(nullptr, // no window
directory,
nullptr); // security
if (ERROR_SUCCESS != error)
{
SetLastError(error);
return false;
}
}
return true;
}
template <typename T>
bool BinaryRead(BYTE const *& pos,
BYTE const * end,
T & value)
{
return BinaryRead(pos,
end,
&value,
sizeof(T));
}
template <typename T>
bool BinaryWrite(HANDLE file,
T const & value)
{
return BinaryWrite(file,
&value,
sizeof(T));
}
static bool BinaryRead(BYTE const *& pos,
BYTE const * end,
void * value,
UINT valueSize)
{
ASSERT(pos);
ASSERT(end);
ASSERT(pos <= end);
ASSERT(value);
ASSERT(0 < valueSize);
if (SafeInt<UINT>(end - pos) < valueSize)
{
SetLastError(ERROR_HANDLE_EOF);
return false;
}
memcpy(value,
pos,
valueSize);
pos += valueSize;
return true;
}
static bool BinaryRead(BYTE const *& pos,
BYTE const * end,
CString& value)
{
ASSERT(pos);
ASSERT(end);
ASSERT(pos <= end);
UINT length;
if (!BinaryRead(pos, end, length))
{
SetLastError(ERROR_HANDLE_EOF);
return false;
}
if (SafeInt<UINT>(end - pos) < ((length + 1)))
{
SetLastError(ERROR_HANDLE_EOF);
return false;
}
value = CString(reinterpret_cast<char const *>(pos),
length);
pos += (length + 1);
return true;
}
static bool BinaryWrite(HANDLE file,
void const * value,
UINT valueSize)
{
ASSERT(INVALID_HANDLE_VALUE != file);
ASSERT(value);
ASSERT(0 < valueSize);
DWORD bytesCopied;
BOOL result = WriteFile(file,
value,
valueSize,
&bytesCopied,
nullptr); // overlapped
return result && valueSize == bytesCopied;
}
static bool BinaryWrite(HANDLE file,
const CString& value)
{
ASSERT(INVALID_HANDLE_VALUE != file);
if (!BinaryWrite(file, value.GetLength()))
{
return false;
}
DWORD const bytesToCopy = ((value.GetLength() + 1));
DWORD bytesCopied;
BOOL result = WriteFile(file,
value.GetString(),
bytesToCopy,
&bytesCopied,
nullptr); // overlapped
return result && bytesToCopy == bytesCopied;
}
static bool SaveStatus(CString const & directory, Status const & status)
{
CPath path = directory;
path.Append(Constant_SyncStatus);
FileHandle file(CreateFile(path,
GENERIC_WRITE,
0, // no sharing while writin
nullptr, // security
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr)); // template
if (!file.IsValid())
{
return false;
}
if (!BinaryWrite(file.Get(), Constant_SyncStatusFormat))
{
return false;
}
if (!BinaryWrite(file.Get(), status.size())) return false;
for (auto iter = status.begin(); iter != status.end(); ++iter)
{
if (!BinaryWrite(file.Get(), iter->first)) return false;
if (!BinaryWrite(file.Get(), iter->second)) return false;
}
return true;
}
static bool LoadStatus(CString const & directory, Status & status)
{
CPath path = directory;
path.Append(Constant_SyncStatus);
MapFile map;
if (!map.Map(path))
{
return false;
}
auto pos = static_cast<BYTE const *>(map.View());
auto end = pos + map.Size();
UINT version;
if (!BinaryRead(pos, end, version))
{
return false;
}
if (Constant_SyncStatusFormat != version)
{
return false;
}
UINT files;
if (!BinaryRead(pos, end, files))
{
return false;
}
for (UINT i = 0; i < files; ++i)
{
CString name;
if (!BinaryRead(pos, end, name))
{
return false;
}
CString signature;
if (!BinaryRead(pos, end, signature))
{
return false;
}
status[name] = signature;
}
return true;
}
static bool LoadIgnore(CString const & directory, CString & ignore)
{
CPath path = directory;
path.Append(Constant_SyncIgnore);
MapFile map;
if (!map.Map(path))
{
return false;
}
auto chars = static_cast<char const *>(map.View());
int size = SafeInt<int>(map.Size());
if (!size)
{
return false;
}
ignore = CString(chars, size);
ignore.Trim();
ignore.Replace("\r\n", ";");
ignore.Replace("\n", ";");
return !ignore.IsEmpty();
}
static CString Format(DWORD errorCode,
HMODULE module = nullptr)
{
PSTR buffer = nullptr;
CString message;
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
if (module)
{
flags |= FORMAT_MESSAGE_FROM_HMODULE;
}
if (FormatMessage(flags,
module,
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<PSTR>(&buffer),
0, // size
nullptr)) // args
{
message = buffer;
LocalFree(buffer);
message.Trim();
}
return message;
};
static void PrintFileError(DWORD error)
{
printf("file error: %s", Format(error));
}
static void PrintHttpError(DWORD error)
{
printf("web error: %s", Format(error));
}
int main(int argc, char ** argv)
{
printf(Constant_Banner);
// 1. Check for -u <URL> and -d <DIR> arguments
CString url;
CString directory;
if (1 < argc)
{
auto begin = &argv[1];
auto end = begin + argc - 1;
for (auto arg = begin; arg != end; ++arg)
{
if (0 == strcmp("-u", *arg))
{
++arg;
if (arg == end)
{
printf(Error_UrlExpected);
return 0;
}
url = *arg;
}
else if (0 == strcmp("-d", *arg))
{
++arg;
if (arg == end)
{
printf(Error_DirectoryExpected);
return 0;
}
directory = *arg;
}
else
{
printf(Error_InvalidArgument);
return 0;
}
}
}
PrepareUrl(url);
if (!PrepareDirectory(directory))
{
PrintFileError(GetLastError());
return 0;
}
// 2. Load .syncstatus
Status status;
if (!LoadStatus(directory, status))
{
status.clear();
}
// 3. Load .syncignore
CString ignore;
if (!LoadIgnore(directory, ignore))
{
ignore = Constant_DefaultIgnore;
}
// 4. Download index
ProgressCallback progress;
progress.Before();
char cache[1024];
auto hr = URLDownloadToCacheFile(nullptr, // caller
url,
cache,
_countof(cache),
0, // reserved
&progress);
progress.After();
if (S_OK != hr)
{
PrintHttpError(hr);
return 0;
}
MapFile index;
if (!index.Map(cache))
{
PrintFileError(GetLastError());
return 0;
}
regex link(Constant_LinkExpression);
CPath fullName;
auto begin = static_cast<PCSTR>(index.View());
auto end = begin + index.Size();
vector<Download> downloads;
UINT countUpToDate = 0;
UINT countNew = 0;
UINT countUpdates = 0;
for (auto i = cregex_iterator(begin, end, link); i != cregex_iterator(); ++i)
{
auto & match = *i;
auto & signatureMatch = match[1];
auto & pathMatch = match[2];
CString path(pathMatch.first, pathMatch.length());
int const fileNamePos = path.ReverseFind(L'/');
if (path.GetLength() - 1 == fileNamePos) // skip directories identified by trailing slashes
{
continue;
}
auto fileName = path.GetString() + fileNamePos + 1;
if (S_OK == PathMatchSpecEx(fileName,
ignore,
PMSF_MULTIPLE))
{
continue;
}
CString signature(signatureMatch.first, signatureMatch.length());
signature.Trim();
auto fileStatus = status.find(fileName);
fullName.Combine(directory, fileName);
bool newFile = !PathFileExists(fullName);
if (fileStatus == status.end() ||
signature != fileStatus->second ||
newFile)
{
downloads.emplace_back(fileName, signature, newFile);
if (newFile)
{
++countNew;
}
else
{
++countUpdates;
}
}
else
{
++countUpToDate;
}
}
if (0 < countUpToDate)
{
if (1 == countUpToDate)
{
printf(Report_UpToDateSingular);
}
else
{
printf(Report_UpToDatePlural, countUpToDate);
}
}
if (downloads.empty())
{
printf(Report_UpdateNone);
return 0;
}
if (0 < countUpdates)
{
if (1 == countUpdates)
{
printf(Report_UpdateSingular);
}
else if (1 < countUpdates)
{
printf(Report_UpdatePlural, countUpdates);
}
}
if (0 < countNew)
{
if (1 == countNew)
{
printf(Report_NewSingular);
}
else if (1 < countNew)
{
printf(Report_NewPlural, countNew);
}
}
printf("\n");
// 5. Compare each link with the .syncstatus
// 6. Filter out any files from .syncignore
// 7. Download remaining files marking them with *-new u-updated !-error
UINT total = 0;
for (auto i = downloads.begin(); i != downloads.end(); ++i)
{
CString fileUrl = url + i->FileName;
auto pos = progress.GetPosition();
printf(" %s ", i->FileName);
progress.Before();
hr = URLDownloadToCacheFile(nullptr, // caller
fileUrl,
cache,
_countof(cache),
0, // reserved
&progress);
progress.After();
progress.SetPosition(pos);
if (S_OK != hr)
{
printf("! %s > ", i->FileName);
PrintHttpError(hr);
printf("\n");
continue;
}
fullName.Combine(directory, i->FileName);
auto move = [&] () -> bool
{
return 0 != MoveFileEx(cache,
fullName,
MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
};
bool moveSucceeded;
int moveRetry = 0;
for (;;)
{
moveSucceeded = move();
if (!moveSucceeded && ERROR_SHARING_VIOLATION == GetLastError() && ++moveRetry < 4)
{
Sleep(500);
}
else
{
break;
}
}
if (!moveSucceeded)
{
printf("! %s > ", i->FileName);
PrintFileError(GetLastError());
printf("\n");
continue;
}
++total;
if (i->New)
{
printf("* ");
}
else
{
printf("u ");
}
printf(i->FileName);
status[i->FileName] = i->Signature;
DWORD handle = 0;
DWORD size = ::GetFileVersionInfoSize(fullName, &handle);
if (0 != size)
{
CAtlArray<BYTE> buffer;
if (buffer.SetCount(size))
{
if (::GetFileVersionInfo(fullName,
handle,
size,
buffer.GetData()))
{
VS_FIXEDFILEINFO* info = 0;
UINT length = 0;
if (::VerQueryValue(buffer.GetData(),
"\\",
reinterpret_cast<void**>(&info),
&length))
{
CString version;
version.Format("%d.%d.%d.%d",
HIWORD(info->dwFileVersionMS),
LOWORD(info->dwFileVersionMS),
HIWORD(info->dwFileVersionLS),
LOWORD(info->dwFileVersionLS));
int newLength = version.GetLength();
while (0 <= newLength - 2 &&
L'0' == version[newLength - 1] &&
L'.' == version[newLength - 2])
{
newLength -= 2;
}
version.Truncate(newLength);
if (!version.IsEmpty())
{
printf(" v%s", version.GetString());
}
}
}
}
}
printf("\n");
}
if (!SaveStatus(directory, status))
{
printf(Error_SaveStatusFailed);
PrintFileError(GetLastError());
printf("\n");
}
return total;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment