You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
711 lines
14 KiB
711 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
main.c
|
|
|
|
Abstract:
|
|
|
|
<TODO: fill in abstract>
|
|
|
|
Author:
|
|
|
|
TODO: <full name> (<alias>) <date>
|
|
|
|
Revision History:
|
|
|
|
<full name> (<alias>) <date> <comments>
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "wininet.h"
|
|
#include <lm.h>
|
|
|
|
HANDLE g_hHeap;
|
|
HINSTANCE g_hInst;
|
|
GROWLIST g_WindiffCmds = INIT_GROWLIST;
|
|
|
|
BOOL WINAPI MigUtil_Entry (HINSTANCE, DWORD, PVOID);
|
|
|
|
BOOL
|
|
pCallEntryPoints (
|
|
DWORD Reason
|
|
)
|
|
{
|
|
switch (Reason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
UtInitialize (NULL);
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
UtTerminate ();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
Init (
|
|
VOID
|
|
)
|
|
{
|
|
g_hHeap = GetProcessHeap();
|
|
g_hInst = GetModuleHandle (NULL);
|
|
|
|
return pCallEntryPoints (DLL_PROCESS_ATTACH);
|
|
}
|
|
|
|
|
|
VOID
|
|
Terminate (
|
|
VOID
|
|
)
|
|
{
|
|
pCallEntryPoints (DLL_PROCESS_DETACH);
|
|
}
|
|
|
|
|
|
VOID
|
|
HelpAndExit (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// This routine is called whenever command line args are wrong
|
|
//
|
|
|
|
fprintf (
|
|
stderr,
|
|
"Command Line Syntax:\n\n"
|
|
|
|
//
|
|
// TODO: Describe command line syntax(es), indent 2 spaces
|
|
//
|
|
|
|
" changes <changenumber>\n"
|
|
|
|
"\nDescription:\n\n"
|
|
|
|
//
|
|
// TODO: Describe tool, indent 2 spaces
|
|
//
|
|
|
|
" changes.exe executes sd describe and windiff for a specific change.\n"
|
|
|
|
"\nArguments:\n\n"
|
|
|
|
//
|
|
// TODO: Describe args, indent 2 spaces, say optional if necessary
|
|
//
|
|
|
|
" <changenumber> Specifies the Source Depot change number\n"
|
|
|
|
);
|
|
|
|
exit (1);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pGetNextLine (
|
|
IN PCSTR Start,
|
|
IN PCSTR Eof,
|
|
OUT PCSTR *PrintableStart,
|
|
OUT PCSTR *End,
|
|
OUT PCSTR *NextLine
|
|
)
|
|
{
|
|
PCSTR pos;
|
|
|
|
pos = Start;
|
|
*End = NULL;
|
|
|
|
while (pos < Eof) {
|
|
if (pos[0] != ' ' && pos[0] != '\t') {
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
*PrintableStart = pos;
|
|
|
|
while (pos < Eof) {
|
|
if (pos[0] == '\r' || pos[0] == '\n') {
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
*End = pos;
|
|
|
|
if (pos < Eof && pos[0] == '\r') {
|
|
pos++;
|
|
}
|
|
if (pos < Eof && pos[0] == '\n') {
|
|
pos++;
|
|
}
|
|
*NextLine = pos;
|
|
|
|
return Start != *NextLine;
|
|
}
|
|
|
|
PCSTR
|
|
pFindNextCharAB (
|
|
IN PCSTR Start,
|
|
IN PCSTR End,
|
|
IN CHAR FindChar
|
|
)
|
|
{
|
|
if (!Start) {
|
|
return NULL;
|
|
}
|
|
|
|
while (Start < End) {
|
|
if (*Start == FindChar) {
|
|
return Start;
|
|
}
|
|
|
|
Start++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pParseViewLines (
|
|
IN OUT PCSTR *FilePos,
|
|
IN PCSTR Eof,
|
|
IN PCSTR Root,
|
|
IN PMAPSTRUCT Map
|
|
)
|
|
{
|
|
UINT count = 0;
|
|
PCSTR pos;
|
|
PCSTR nextPos;
|
|
PSTR prefix;
|
|
PCSTR prefixEnd;
|
|
PSTR subDir;
|
|
PCSTR localPath;
|
|
PCSTR clientPathStart;
|
|
PCSTR clientPathEnd;
|
|
PSTR p;
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
|
|
pos = *FilePos;
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &nextPos)) {
|
|
if (pos == lineStart) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find depot prefix
|
|
//
|
|
|
|
prefixEnd = lineStart;
|
|
for (;;) {
|
|
prefixEnd = pFindNextCharAB (prefixEnd, lineEnd, '.');
|
|
if (!prefixEnd) {
|
|
break;
|
|
}
|
|
|
|
if (prefixEnd[1] == '.' && prefixEnd[2] == '.' &&
|
|
isspace(prefixEnd[3]) &&
|
|
prefixEnd[4] == '/' && prefixEnd[5] == '/'
|
|
) {
|
|
break;
|
|
}
|
|
|
|
prefixEnd++;
|
|
}
|
|
|
|
if (!prefixEnd || prefixEnd == lineStart) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find client path
|
|
//
|
|
|
|
clientPathStart = pFindNextCharAB (prefixEnd + 6, lineEnd, '/');
|
|
if (!clientPathStart) {
|
|
break;
|
|
}
|
|
|
|
clientPathStart++;
|
|
|
|
clientPathEnd = clientPathStart;
|
|
for (;;) {
|
|
clientPathEnd = pFindNextCharAB (clientPathEnd, lineEnd, '.');
|
|
if (!clientPathEnd) {
|
|
break;
|
|
}
|
|
|
|
if (clientPathEnd[1] == '.' && clientPathEnd[2] == '.' &&
|
|
clientPathEnd + 3 == lineEnd
|
|
) {
|
|
break;
|
|
}
|
|
|
|
clientPathEnd++;
|
|
}
|
|
|
|
if (!clientPathEnd) {
|
|
break;
|
|
}
|
|
|
|
if (clientPathEnd > clientPathStart) {
|
|
clientPathEnd--; // account for last slash
|
|
}
|
|
|
|
//
|
|
// Clean the strings and add to mapping
|
|
//
|
|
|
|
prefix = AllocText (prefixEnd - lineStart);
|
|
StringCopyAB (prefix, lineStart, prefixEnd);
|
|
|
|
subDir = AllocText ((clientPathEnd - clientPathStart) + 1);
|
|
if (clientPathEnd > clientPathStart) {
|
|
StringCopyAB (subDir, clientPathStart, clientPathEnd);
|
|
}
|
|
|
|
p = strchr (subDir, '/');
|
|
while (p) {
|
|
*p++ = '\\';
|
|
p = strchr (p, '/');
|
|
}
|
|
|
|
AppendWack (subDir);
|
|
|
|
localPath = JoinPaths (Root, subDir);
|
|
|
|
AddStringMappingPair (Map, prefix, localPath);
|
|
|
|
FreeText (prefix);
|
|
FreeText (subDir);
|
|
FreePathString (localPath);
|
|
|
|
count++;
|
|
|
|
pos = nextPos;
|
|
}
|
|
|
|
|
|
*FilePos = pos;
|
|
|
|
return count > 0;
|
|
}
|
|
|
|
BOOL
|
|
pParseClientMapping (
|
|
IN PCSTR SdClientOutput,
|
|
IN PCSTR Eof,
|
|
OUT PSTR RootPath,
|
|
IN PMAPSTRUCT Map
|
|
)
|
|
{
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR pos;
|
|
PSTR dup;
|
|
PCSTR root;
|
|
BOOL viewFound = FALSE;
|
|
|
|
//
|
|
// Find Root: or View:
|
|
//
|
|
|
|
pos = SdClientOutput;
|
|
*RootPath = 0;
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &pos)) {
|
|
if (lineStart == lineEnd) {
|
|
continue;
|
|
}
|
|
|
|
if (*lineStart == '#') {
|
|
continue;
|
|
}
|
|
|
|
dup = AllocText (lineEnd - lineStart);
|
|
StringCopyAB (dup, lineStart, lineEnd);
|
|
|
|
if (StringIPrefix (dup, "Root:")) {
|
|
root = dup + 5;
|
|
while (isspace (*root)) {
|
|
root++;
|
|
}
|
|
|
|
StringCopy (RootPath, root);
|
|
|
|
} else if (StringIPrefix (dup, "View:")) {
|
|
if (!(*RootPath)) {
|
|
break;
|
|
}
|
|
|
|
viewFound = pParseViewLines (&pos, Eof, RootPath, Map);
|
|
}
|
|
|
|
FreeText (dup);
|
|
dup = NULL;
|
|
}
|
|
|
|
FreeText (dup);
|
|
|
|
return *RootPath && viewFound;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pParseChangeList (
|
|
IN PCSTR SdDescribeOutput,
|
|
IN PCSTR Eof,
|
|
IN PMAPSTRUCT Map
|
|
)
|
|
{
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR pos;
|
|
PCSTR nextPos;
|
|
PSTR dup;
|
|
BOOL result = FALSE;
|
|
PSTR p;
|
|
UINT num;
|
|
CHAR bigBuf[2048];
|
|
PSTR change;
|
|
CHAR cmdLine[2048];
|
|
|
|
//
|
|
// Find the line Affected files
|
|
//
|
|
|
|
pos = SdDescribeOutput;
|
|
printf ("\n\n");
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &nextPos)) {
|
|
if (lineEnd > lineStart &&
|
|
StringMatchAB ("Affected files ...", lineStart, lineEnd)
|
|
) {
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
|
|
dup = AllocText ((lineEnd - pos) + 1);
|
|
|
|
if (lineEnd > pos) {
|
|
StringCopyAB (dup, pos, lineEnd);
|
|
}
|
|
|
|
printf ("%s\n", dup);
|
|
FreeText (dup);
|
|
|
|
pos = nextPos;
|
|
}
|
|
|
|
if (result) {
|
|
//
|
|
// Files listed in output
|
|
//
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &nextPos)) {
|
|
pos = nextPos;
|
|
|
|
if (lineStart[0] != '.') {
|
|
continue;
|
|
}
|
|
|
|
lineStart += 4;
|
|
if (lineStart > lineEnd) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Translate depot path into local path
|
|
//
|
|
|
|
StringCopyAB (bigBuf, lineStart, lineEnd);
|
|
|
|
p = strrchr (bigBuf, '#');
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
|
|
*p = 0;
|
|
num = strtoul (p + 1, &change, 10);
|
|
|
|
while (isspace (*change)) {
|
|
change++;
|
|
}
|
|
|
|
MappingSearchAndReplace (Map, bigBuf, ARRAYSIZE(bigBuf));
|
|
|
|
p = strchr (bigBuf, '/');
|
|
while (p) {
|
|
*p++ = '\\';
|
|
p = strchr (p, '/');
|
|
}
|
|
|
|
if (StringIMatch (change, "edit") && num > 1) {
|
|
wsprintf (cmdLine, "windiff.exe %s#%u %s#%u", bigBuf, num - 1, bigBuf, num);
|
|
GlAppendString (&g_WindiffCmds, cmdLine);
|
|
}
|
|
|
|
printf ("%s: %s#%u\n", change, bigBuf, num);
|
|
|
|
}
|
|
}
|
|
|
|
return GlGetSize (&g_WindiffCmds) > 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pLaunchSd (
|
|
IN PSTR CmdLine,
|
|
IN HANDLE TempFile,
|
|
IN PCSTR Msg,
|
|
OUT HANDLE *Mapping,
|
|
OUT PCSTR *FileContent,
|
|
OUT PCSTR *Eof
|
|
)
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LONG rc;
|
|
|
|
SetFilePointer (TempFile, 0, NULL, FILE_BEGIN);
|
|
SetEndOfFile (TempFile);
|
|
|
|
ZeroMemory (&si, sizeof (si));
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
|
|
|
|
if (!DuplicateHandle (
|
|
GetCurrentProcess(),
|
|
TempFile,
|
|
GetCurrentProcess(),
|
|
&si.hStdOutput,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)) {
|
|
printf ("Can't dup temp file handle\n");
|
|
return FALSE;
|
|
}
|
|
|
|
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
|
|
|
|
if (!CreateProcess (
|
|
NULL,
|
|
CmdLine,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi
|
|
)) {
|
|
printf ("Can't launch sd describe\n");
|
|
CloseHandle (si.hStdOutput);
|
|
return FALSE;
|
|
}
|
|
|
|
printf ("%s", Msg);
|
|
rc = WaitForSingleObject (pi.hProcess, INFINITE);
|
|
printf ("\n");
|
|
|
|
CloseHandle (pi.hProcess);
|
|
CloseHandle (pi.hThread);
|
|
CloseHandle (si.hStdOutput);
|
|
|
|
if (rc != WAIT_OBJECT_0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetFileSize (TempFile, NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Mapping = CreateFileMapping (TempFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (!(*Mapping)) {
|
|
printf ("Can't map temp file into memory\n");
|
|
return FALSE;
|
|
}
|
|
|
|
*FileContent = (PCSTR) MapViewOfFile (*Mapping, FILE_MAP_READ, 0, 0, 0);
|
|
if (!*FileContent) {
|
|
printf ("Can't map temp file data into memory\n");
|
|
CloseHandle (*Mapping);
|
|
return FALSE;
|
|
}
|
|
|
|
*Eof = *FileContent + GetFileSize (TempFile, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pLaunchWindiff (
|
|
IN PCSTR CmdLine
|
|
)
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LONG rc;
|
|
PSTR writableCmdLine = DuplicateText (CmdLine);
|
|
|
|
ZeroMemory (&si, sizeof (si));
|
|
|
|
if (!CreateProcess (
|
|
NULL,
|
|
writableCmdLine,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi
|
|
)) {
|
|
FreeText (writableCmdLine);
|
|
printf ("Can't launch %s\n", CmdLine);
|
|
return FALSE;
|
|
}
|
|
|
|
FreeText (writableCmdLine);
|
|
|
|
rc = WaitForSingleObject (pi.hProcess, INFINITE);
|
|
|
|
CloseHandle (pi.hProcess);
|
|
CloseHandle (pi.hThread);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
INT
|
|
__cdecl
|
|
_tmain (
|
|
INT argc,
|
|
PCTSTR argv[]
|
|
)
|
|
{
|
|
INT i;
|
|
UINT change = 0;
|
|
UINT u;
|
|
UINT count;
|
|
|
|
//
|
|
// TODO: Parse command line here
|
|
//
|
|
|
|
for (i = 1 ; i < argc ; i++) {
|
|
if (argv[i][0] == TEXT('/') || argv[i][0] == TEXT('-')) {
|
|
HelpAndExit();
|
|
} else {
|
|
//
|
|
// Parse other args that don't require / or -
|
|
//
|
|
|
|
if (change) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
change = _tcstoul (argv[i], NULL, 10);
|
|
if (!change) {
|
|
HelpAndExit();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!change) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
//
|
|
// Begin processing
|
|
//
|
|
|
|
if (!Init()) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// TODO: Do work here
|
|
//
|
|
{
|
|
TCHAR cmd[MAX_PATH];
|
|
HANDLE tempFile;
|
|
HANDLE mapping;
|
|
PCSTR fileData;
|
|
PCSTR endOfFile;
|
|
BOOL runWinDiff = FALSE;
|
|
CHAR root[MAX_PATH];
|
|
PMAPSTRUCT map;
|
|
|
|
tempFile = BfGetTempFile ();
|
|
map = CreateStringMapping();
|
|
|
|
if (!tempFile) {
|
|
printf ("Can't create temp file\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (!pLaunchSd ("sd client -o", tempFile, "Getting client mapping...", &mapping, &fileData, &endOfFile)) {
|
|
exit (1);
|
|
}
|
|
|
|
pParseClientMapping (fileData, endOfFile, root, map);
|
|
|
|
wsprintf (cmd, TEXT("sd describe -s %u"), change);
|
|
if (!pLaunchSd (cmd, tempFile, "Getting change list...", &mapping, &fileData, &endOfFile)) {
|
|
exit (1);
|
|
}
|
|
|
|
runWinDiff = pParseChangeList (fileData, endOfFile, map);
|
|
|
|
UnmapViewOfFile (fileData);
|
|
CloseHandle (mapping);
|
|
|
|
DestroyStringMapping (map);
|
|
CloseHandle (tempFile);
|
|
|
|
if (runWinDiff) {
|
|
count = GlGetSize (&g_WindiffCmds);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
if (!pLaunchWindiff (GlGetString (&g_WindiffCmds, u))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
GlFree (&g_WindiffCmds);
|
|
|
|
}
|
|
|
|
//
|
|
// End of processing
|
|
//
|
|
|
|
Terminate();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|