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.
1334 lines
32 KiB
1334 lines
32 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"
|
|
|
|
#define MAX_BRANCHES 16
|
|
|
|
HANDLE g_hHeap;
|
|
HINSTANCE g_hInst;
|
|
BOOL g_Commit;
|
|
|
|
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,
|
|
"\nDescription:\n\n"
|
|
|
|
" SDPB.EXE executes sd branch and sd client for a specific branch.\n"
|
|
" It maps in the branch into view, or removes the branch from view if\n"
|
|
" the -d argument is specified.\n\n"
|
|
|
|
" This utility assumes a specific format of the branch layout:\n\n"
|
|
|
|
" //depot/private/{branch}/{project}/{path}\n\n"
|
|
|
|
" {branch} specifies the private branch, and the last path member must\n"
|
|
" match the <branchname> arg.\n\n"
|
|
|
|
" Examples: (assume <branchname> is \"foo\")\n"
|
|
" //depot/private/foo/root\n"
|
|
" //depot/private/foo/bar\n"
|
|
" //depot/private/cat/foo/bar\n\n"
|
|
|
|
" {project} specifies the depot, such as root or base\n\n"
|
|
|
|
" {path} specifies the rest of the path\n\n"
|
|
|
|
" The local path is computed by %%_NTBINDIR%%\\{project}\\{path}, unless\n"
|
|
" {project} == \"root\" (%%_NTBINDIR%%\\{path} is used for root).\n\n"
|
|
|
|
" The -p switch overrides local path computation.\n\n"
|
|
|
|
" Example: (a private branch of winnt32)\n\n"
|
|
|
|
" //depot/private/migration/base/ntsetup/winnt32\n\n"
|
|
|
|
" NOTE: SDPB.EXE changes have no affect until sd sync is executed.\n\n"
|
|
|
|
"Command Line Syntax:\n\n"
|
|
|
|
" sdpb <branchname(s)> [-p:<local_proj_root>] [-d] [-o] [-c]\n"
|
|
|
|
"\nArguments: (order-insensitive)\n\n"
|
|
|
|
" <branchname> specifies the Source Depot branch name to map in. A max of\n"
|
|
" 16 branches can be specified.\n\n"
|
|
|
|
" -p:<local_proj_root> specifies an alternative local subdirectory to\n"
|
|
" use as the path base. If specified, //depot/private/{branch}/{project}\n"
|
|
" is replaced by <local_proj_root>.\n\n"
|
|
|
|
" -d enables delete mode, to remove a branch from the client view\n\n"
|
|
|
|
" -o outputs various interesting data about the branch views (no changes)\n\n"
|
|
|
|
" -c commits changes to sd client\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 OUT PGROWLIST LeftSide, OPTIONAL
|
|
IN OUT PGROWLIST RightSide OPTIONAL
|
|
)
|
|
{
|
|
UINT count = 0;
|
|
PCSTR pos;
|
|
PCSTR nextPos;
|
|
PSTR midString;
|
|
PCSTR rightSideStart;
|
|
PCSTR rightSideEnd;
|
|
PSTR p;
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR leftSideStart;
|
|
PCSTR leftSideEnd;
|
|
PCSTR dash;
|
|
BOOL b;
|
|
|
|
pos = *FilePos;
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &nextPos)) {
|
|
if (pos == lineStart) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Extract the left side string
|
|
//
|
|
|
|
leftSideStart = pFindNextCharAB (lineStart, lineEnd, '/');
|
|
if (!leftSideStart || (leftSideStart + 1 >= lineEnd) || leftSideStart[1] != '/') {
|
|
break;
|
|
}
|
|
|
|
leftSideEnd = leftSideStart + 2;
|
|
|
|
dash = pFindNextCharAB (lineStart, lineEnd, '-');
|
|
if (dash == leftSideStart - 1) {
|
|
leftSideStart = dash;
|
|
}
|
|
|
|
for (;;) {
|
|
leftSideEnd = pFindNextCharAB (leftSideEnd, lineEnd, '/');
|
|
if (!leftSideEnd || (leftSideEnd + 1 >= lineEnd)) {
|
|
leftSideEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
if (leftSideEnd[1] == '/') {
|
|
leftSideEnd--;
|
|
break;
|
|
}
|
|
|
|
leftSideEnd++;
|
|
}
|
|
|
|
if (!leftSideEnd) {
|
|
break;
|
|
}
|
|
|
|
rightSideStart = pFindNextCharAB (leftSideEnd, lineEnd, '/');
|
|
if (!rightSideStart || (rightSideStart + 1 >= lineEnd) || rightSideStart[1] != '/') {
|
|
break;
|
|
}
|
|
|
|
while (leftSideEnd > leftSideStart) {
|
|
leftSideEnd--;
|
|
if (!isspace (*leftSideEnd)) {
|
|
leftSideEnd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftSideEnd == leftSideStart) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Extract the right side string
|
|
//
|
|
|
|
rightSideEnd = lineEnd;
|
|
while (rightSideEnd > rightSideStart) {
|
|
rightSideEnd--;
|
|
if (!isspace (*rightSideEnd)) {
|
|
rightSideEnd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rightSideEnd == rightSideStart) {
|
|
break;
|
|
}
|
|
|
|
if (LeftSide) {
|
|
if (!GlAppendStringAB (LeftSide, leftSideStart, leftSideEnd)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RightSide) {
|
|
if (!GlAppendStringAB (RightSide, rightSideStart, rightSideEnd)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
count++;
|
|
|
|
pos = nextPos;
|
|
}
|
|
|
|
*FilePos = pos;
|
|
|
|
return count > 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
pDumpOutput (
|
|
IN PCSTR SdClientOutput,
|
|
IN PCSTR Eof
|
|
)
|
|
{
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR pos;
|
|
PSTR dup;
|
|
PCSTR root;
|
|
BOOL viewFound = FALSE;
|
|
|
|
pos = SdClientOutput;
|
|
|
|
while (pGetNextLine (pos, Eof, &lineStart, &lineEnd, &pos)) {
|
|
if (lineEnd == lineStart) {
|
|
printf ("\n");
|
|
continue;
|
|
}
|
|
|
|
dup = AllocText (lineEnd - lineStart);
|
|
StringCopyAB (dup, lineStart, lineEnd);
|
|
|
|
_tprintf ("%s\n", dup);
|
|
|
|
FreeText (dup);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pParseClientMapping (
|
|
IN PCSTR SdClientOutput,
|
|
IN PCSTR Eof,
|
|
OUT PSTR Client,
|
|
OUT PSTR RootPath,
|
|
IN OUT PGROWLIST LeftSide, OPTIONAL
|
|
IN OUT PGROWLIST RightSide OPTIONAL
|
|
)
|
|
{
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR pos;
|
|
PSTR dup;
|
|
PCSTR data;
|
|
BOOL viewFound = FALSE;
|
|
|
|
//
|
|
// Find Client:, 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, "Client:")) {
|
|
data = dup + 7;
|
|
while (isspace (*data)) {
|
|
data++;
|
|
}
|
|
|
|
StringCopy (Client, data);
|
|
|
|
} else if (StringIPrefix (dup, "Root:")) {
|
|
data = dup + 5;
|
|
while (isspace (*data)) {
|
|
data++;
|
|
}
|
|
|
|
StringCopy (RootPath, data);
|
|
|
|
} else if (StringIPrefix (dup, "View:")) {
|
|
if (!(*RootPath)) {
|
|
break;
|
|
}
|
|
|
|
viewFound = pParseViewLines (&pos, Eof, LeftSide, RightSide);
|
|
}
|
|
|
|
FreeText (dup);
|
|
dup = NULL;
|
|
}
|
|
|
|
FreeText (dup);
|
|
|
|
return *RootPath && viewFound;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pParseBranchMapping (
|
|
IN PCSTR SdClientOutput,
|
|
IN PCSTR Eof,
|
|
IN OUT PGROWLIST LeftSide, OPTIONAL
|
|
IN OUT PGROWLIST RightSide OPTIONAL
|
|
)
|
|
{
|
|
PCSTR lineStart;
|
|
PCSTR lineEnd;
|
|
PCSTR pos;
|
|
PSTR dup;
|
|
PCSTR root;
|
|
BOOL viewFound = FALSE;
|
|
|
|
//
|
|
// Find View:
|
|
//
|
|
|
|
pos = SdClientOutput;
|
|
|
|
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, "View:")) {
|
|
viewFound = pParseViewLines (&pos, Eof, LeftSide, RightSide);
|
|
}
|
|
|
|
FreeText (dup);
|
|
dup = NULL;
|
|
}
|
|
|
|
FreeText (dup);
|
|
|
|
return viewFound;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pVerifyBranch (
|
|
IN PCSTR BranchName,
|
|
IN PGROWLIST BranchStorage,
|
|
OUT PGROWLIST BranchProject, OPTIONAL
|
|
OUT PGROWLIST BranchPath, OPTIONAL
|
|
IN PCSTR LocalRoot, OPTIONAL
|
|
IN PCSTR ClientViewRoot OPTIONAL
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
BOOL result = TRUE;
|
|
PCSTR projectStart;
|
|
PCSTR projectEnd;
|
|
UINT branchNameTchars;
|
|
PCSTR localPathBase = NULL;
|
|
PCSTR p;
|
|
PSTR localSubPath = NULL;
|
|
PCSTR restOfPath = NULL;
|
|
PSTR q;
|
|
PCSTR fullSubPath = NULL;
|
|
|
|
if (LocalRoot) {
|
|
p = LocalRoot + TcharCountA (ClientViewRoot);
|
|
if (*p == '\\') {
|
|
p++;
|
|
}
|
|
|
|
localSubPath = DuplicatePathString (p, 0);
|
|
q = localSubPath;
|
|
while (*q) {
|
|
if (*q == '\\') {
|
|
*q = '/';
|
|
}
|
|
|
|
q++;
|
|
}
|
|
}
|
|
|
|
branchNameTchars = TcharCountA (BranchName);
|
|
|
|
count = GlGetSize (BranchStorage);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
projectStart = GlGetString (BranchStorage, u);
|
|
if (!projectStart || !StringIPrefix (projectStart, "//depot/private/")) {
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
|
|
projectStart += sizeof ("//depot/private/") - 1; // minus one for nul
|
|
|
|
while (*projectStart) {
|
|
if (StringIPrefix (projectStart, BranchName)) {
|
|
|
|
if (projectStart[branchNameTchars] == '/') {
|
|
//
|
|
// Recall syntax is:
|
|
//
|
|
// //depot/private/[subdir/]{branch}/{project}/{path}
|
|
//
|
|
// If -p switch is specified, we don't have {project}.
|
|
//
|
|
|
|
//
|
|
// We just found {branch}, locate start and end ptrs of {project},
|
|
// leave them equal if there is no {project}. projectEnd must
|
|
// point to /{path}.
|
|
//
|
|
|
|
projectStart += branchNameTchars;
|
|
projectEnd = projectStart;
|
|
|
|
if (!LocalRoot) {
|
|
projectStart++;
|
|
projectEnd = strchr (projectStart, '/');
|
|
if (!projectEnd) {
|
|
//
|
|
// Assumption failure -- break now
|
|
//
|
|
|
|
projectStart = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// prepare the base path from -p switch
|
|
//
|
|
|
|
if (localSubPath) {
|
|
localPathBase = localSubPath;
|
|
} else {
|
|
localPathBase = "";
|
|
}
|
|
|
|
//
|
|
// After projectEnd comes optional {path}, find {path}
|
|
//
|
|
|
|
restOfPath = projectEnd;
|
|
if (*restOfPath && !(*localPathBase)) {
|
|
restOfPath++;
|
|
}
|
|
|
|
// done
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// {project} not found yet, keep searching
|
|
//
|
|
|
|
projectStart = strchr (projectStart, '/');
|
|
if (projectStart) {
|
|
projectStart++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!projectStart || !restOfPath || !localPathBase) {
|
|
result = FALSE;
|
|
fprintf (
|
|
stderr,
|
|
"\nThe branch spec below does not fit assumptions. See help (/? switch).\n\n%s\n\n",
|
|
GlGetString (BranchStorage, u)
|
|
);
|
|
break;
|
|
}
|
|
|
|
fullSubPath = JoinText (localPathBase, restOfPath);
|
|
if (!fullSubPath) {
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
|
|
localPathBase = NULL;
|
|
restOfPath = NULL;
|
|
|
|
if (BranchProject) {
|
|
if (!GlAppendStringAB (BranchProject, projectStart, projectEnd)) {
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (BranchPath) {
|
|
if (!GlAppendString (BranchPath, fullSubPath)) {
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreeText (fullSubPath);
|
|
fullSubPath = NULL;
|
|
}
|
|
|
|
FreeText (fullSubPath);
|
|
FreePathString (localSubPath);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pLaunchSd (
|
|
IN PSTR CmdLine,
|
|
IN HANDLE TempInput,
|
|
IN HANDLE TempOutput,
|
|
IN PCSTR Msg,
|
|
OUT HANDLE *Mapping,
|
|
OUT PCSTR *FileContent,
|
|
OUT PCSTR *Eof
|
|
)
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LONG rc;
|
|
|
|
if (TempInput != INVALID_HANDLE_VALUE) {
|
|
SetFilePointer (TempInput, 0, NULL, FILE_BEGIN);
|
|
}
|
|
|
|
if (TempOutput != INVALID_HANDLE_VALUE) {
|
|
SetFilePointer (TempOutput, 0, NULL, FILE_BEGIN);
|
|
SetEndOfFile (TempOutput);
|
|
}
|
|
|
|
ZeroMemory (&si, sizeof (si));
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
if (TempInput == INVALID_HANDLE_VALUE) {
|
|
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
|
|
} else {
|
|
if (!DuplicateHandle (
|
|
GetCurrentProcess(),
|
|
TempInput,
|
|
GetCurrentProcess(),
|
|
&si.hStdInput,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)) {
|
|
printf ("Can't dup temp input file handle\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!DuplicateHandle (
|
|
GetCurrentProcess(),
|
|
TempOutput,
|
|
GetCurrentProcess(),
|
|
&si.hStdOutput,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)) {
|
|
printf ("Can't dup temp output 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 (TempOutput, NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Mapping = CreateFileMapping (TempOutput, 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 (TempOutput, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCSTR
|
|
pSkipMachineName (
|
|
IN PCSTR LocalView
|
|
)
|
|
{
|
|
if (LocalView[0] == '-' && LocalView[1] == '/' && LocalView[2] == '/') {
|
|
return strchr (LocalView + 3, '/');
|
|
}
|
|
|
|
if (LocalView[0] == '/' && LocalView[1] == '/') {
|
|
return strchr (LocalView + 2, '/');
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsBranchInView (
|
|
IN PCSTR ViewInLocalPath,
|
|
IN PGROWLIST StoredSpec,
|
|
OUT UINT *Index OPTIONAL
|
|
)
|
|
{
|
|
UINT subPathTchars;
|
|
UINT u;
|
|
UINT count;
|
|
PCSTR localSpecPath;
|
|
PCSTR p1;
|
|
PCSTR p2;
|
|
|
|
p2 = pSkipMachineName (ViewInLocalPath);
|
|
if (!p2) {
|
|
return FALSE;
|
|
}
|
|
|
|
subPathTchars = TcharCount (p2);
|
|
|
|
count = GlGetSize (StoredSpec);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
p1 = pSkipMachineName (GlGetString (StoredSpec, u));
|
|
if (!p1) {
|
|
continue;
|
|
}
|
|
|
|
if (StringIPrefix (p1, p2)) {
|
|
if (p1[subPathTchars] == 0 || p1[subPathTchars] == '/') {
|
|
if (Index) {
|
|
*Index = u;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pDumpBranchStatus (
|
|
IN PGROWLIST BranchParent,
|
|
IN PGROWLIST BranchStorage,
|
|
IN PGROWLIST BranchProject,
|
|
IN PGROWLIST BranchPath,
|
|
IN PGROWLIST StoredSpec,
|
|
IN PGROWLIST LocalSpec,
|
|
IN PCSTR LocalRoot,
|
|
IN PCSTR NtBinDir,
|
|
IN PCSTR Root,
|
|
IN PCSTR ComputerName
|
|
)
|
|
{
|
|
UINT count;
|
|
UINT u;
|
|
UINT rootTchars;
|
|
PCSTR p;
|
|
PCSTR localBase;
|
|
PSTR q;
|
|
PCSTR baseOfPath;
|
|
PCSTR fullPath;
|
|
CHAR fullSpec[MAX_PATH * 2];
|
|
BOOL outOfView = FALSE;
|
|
|
|
count = GlGetSize (BranchParent);
|
|
if (count != GlGetSize (BranchStorage)) {
|
|
exit (1);
|
|
}
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
wsprintf (fullSpec, "//%s/%s", ComputerName, GlGetString (BranchPath, u));
|
|
|
|
if (!pIsBranchInView (GlGetString (BranchStorage, u), StoredSpec, NULL)) {
|
|
outOfView = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (outOfView) {
|
|
printf ("All Branch View Mappings:\n");
|
|
} else {
|
|
printf ("Branch View Mappings: (all are in client view)\n");
|
|
}
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
_tprintf (
|
|
" %s //%s/%s\n",
|
|
GlGetString (BranchStorage, u),
|
|
ComputerName,
|
|
GlGetString (BranchPath, u)
|
|
);
|
|
}
|
|
|
|
if (outOfView) {
|
|
printf ("\nOut-of-View Mapping:\n");
|
|
for (u = 0 ; u < count ; u++) {
|
|
wsprintf (fullSpec, "//%s/%s", ComputerName, GlGetString (BranchPath, u));
|
|
|
|
if (!pIsBranchInView (GlGetString (BranchStorage, u), StoredSpec, NULL)) {
|
|
_tprintf (
|
|
" %s %s\n",
|
|
GlGetString (BranchStorage, u),
|
|
fullSpec
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
rootTchars = TcharCount (Root);
|
|
|
|
printf ("\nLocal View: (%s)\n", Root);
|
|
for (u = 0 ; u < count ; u++) {
|
|
p = GlGetString (BranchProject, u);
|
|
|
|
if (!LocalRoot) {
|
|
if (!p || StringIMatch (p, "root")) {
|
|
baseOfPath = DuplicatePathString (NtBinDir, 0);
|
|
} else {
|
|
baseOfPath = JoinPaths (NtBinDir, p);
|
|
}
|
|
} else {
|
|
baseOfPath = DuplicatePathString (LocalRoot, 0);
|
|
}
|
|
|
|
if (!baseOfPath) {
|
|
exit (1);
|
|
}
|
|
|
|
fullPath = JoinPaths (baseOfPath, GlGetString (BranchPath, u));
|
|
if (!fullPath) {
|
|
exit (1);
|
|
}
|
|
|
|
FreePathString (baseOfPath);
|
|
|
|
q = (PSTR) fullPath;
|
|
while (*q) {
|
|
if (*q == '/') {
|
|
*q = '\\';
|
|
}
|
|
|
|
q++;
|
|
}
|
|
|
|
if (StringIPrefix (fullPath, Root) && (fullPath[rootTchars] == 0 || fullPath[rootTchars] == '\\')) {
|
|
printf (" %s\n", fullPath);
|
|
}
|
|
|
|
FreePathString (fullPath);
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddAllMappings (
|
|
IN PGROWLIST BranchStorage,
|
|
IN PGROWLIST BranchPath,
|
|
IN OUT PGROWLIST StoredSpec,
|
|
IN OUT PGROWLIST LocalSpec,
|
|
IN PCSTR ComputerName
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
CHAR fullSpec[MAX_PATH * 2];
|
|
BOOL heading = TRUE;
|
|
|
|
count = GlGetSize (BranchPath);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
wsprintf (fullSpec, "//%s/%s", ComputerName, GlGetString (BranchPath, u));
|
|
|
|
if (!pIsBranchInView (GlGetString (BranchStorage, u), StoredSpec, NULL)) {
|
|
if (heading) {
|
|
heading = FALSE;
|
|
printf ("Add to client view:\n");
|
|
}
|
|
|
|
printf (" %s %s\n", GlGetString (BranchStorage, u), fullSpec);
|
|
|
|
GlAppendString (StoredSpec, GlGetString (BranchStorage, u));
|
|
GlAppendString (LocalSpec, fullSpec);
|
|
}
|
|
}
|
|
|
|
return heading == FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pDeleteAllMappings (
|
|
IN PGROWLIST BranchStorage,
|
|
IN PGROWLIST BranchPath,
|
|
IN OUT PGROWLIST StoredSpec,
|
|
IN OUT PGROWLIST LocalSpec,
|
|
IN PCSTR ComputerName
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
CHAR fullSpec[MAX_PATH * 2];
|
|
BOOL heading = TRUE;
|
|
BOOL restart;
|
|
UINT delIndex;
|
|
|
|
do {
|
|
restart = FALSE;
|
|
|
|
count = GlGetSize (BranchPath);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
wsprintf (fullSpec, "//%s/%s", ComputerName, GlGetString (BranchPath, u));
|
|
|
|
if (pIsBranchInView (GlGetString (BranchStorage, u), StoredSpec, &delIndex)) {
|
|
if (heading) {
|
|
heading = FALSE;
|
|
printf ("Remove from client view:\n");
|
|
}
|
|
|
|
printf (" %s %s\n", GlGetString (BranchStorage, u), fullSpec);
|
|
|
|
GlDeleteItem (StoredSpec, delIndex);
|
|
GlDeleteItem (LocalSpec, delIndex);
|
|
|
|
restart = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} while (restart);
|
|
|
|
return heading == FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pDumpClientView (
|
|
IN HANDLE Output,
|
|
IN PGROWLIST StoredSpec,
|
|
IN PGROWLIST LocalSpec,
|
|
IN PCSTR Client,
|
|
IN PCSTR Root
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
CHAR buffer[1024];
|
|
|
|
wsprintf (buffer, "Client: %s\n\n", Client);
|
|
if (!WriteFileString (Output, buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
wsprintf (buffer, "Root: %s\n\n", Root);
|
|
if (!WriteFileString (Output, buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
wsprintf (buffer, "View:\n");
|
|
if (!WriteFileString (Output, buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
count = GlGetSize (StoredSpec);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
wsprintf (buffer, " %s %s\n", GlGetString (StoredSpec, u), GlGetString (LocalSpec, u));
|
|
if (!WriteFileString (Output, buffer)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
INT
|
|
__cdecl
|
|
_tmain (
|
|
INT argc,
|
|
PCTSTR argv[]
|
|
)
|
|
{
|
|
INT i;
|
|
PCSTR branchName[MAX_BRANCHES];
|
|
UINT branches = 0;
|
|
UINT u;
|
|
UINT count;
|
|
PCSTR localRoot = NULL;
|
|
CHAR fullRoot[MAX_PATH];
|
|
BOOL dumpStatus = FALSE;
|
|
BOOL deleteMode = FALSE;
|
|
PSTR dontCare;
|
|
DWORD rc;
|
|
|
|
//
|
|
// TODO: Parse command line here
|
|
//
|
|
|
|
for (i = 1 ; i < argc ; i++) {
|
|
if (argv[i][0] == TEXT('/') || argv[i][0] == TEXT('-')) {
|
|
switch (tolower (argv[i][1])) {
|
|
|
|
case 'o':
|
|
if (dumpStatus) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
dumpStatus = TRUE;
|
|
break;
|
|
|
|
case 'd':
|
|
if (deleteMode) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
deleteMode = TRUE;
|
|
break;
|
|
|
|
case 'p':
|
|
if (localRoot) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
if (argv[i][2] == ':') {
|
|
localRoot = &(argv[i][3]);
|
|
} else {
|
|
i++;
|
|
if (i == argc) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
localRoot = argv[i];
|
|
}
|
|
|
|
rc = GetFullPathName (localRoot, ARRAYSIZE(fullRoot), fullRoot, &dontCare);
|
|
if (rc == 0 || rc >= ARRAYSIZE(fullRoot)) {
|
|
fprintf (stderr, "Can't get full path of %s\n", localRoot);
|
|
exit (1);
|
|
}
|
|
|
|
localRoot = fullRoot;
|
|
break;
|
|
|
|
case 'c':
|
|
if (g_Commit) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
g_Commit = TRUE;
|
|
break;
|
|
|
|
default:
|
|
HelpAndExit();
|
|
}
|
|
} else {
|
|
//
|
|
// Parse other args that don't require / or -
|
|
//
|
|
|
|
if (branches == MAX_BRANCHES) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
branchName[branches++] = argv[i];
|
|
}
|
|
}
|
|
|
|
if (!branches) {
|
|
HelpAndExit();
|
|
}
|
|
|
|
//
|
|
// Begin processing
|
|
//
|
|
|
|
if (!Init()) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// TODO: Do work here
|
|
//
|
|
{
|
|
TCHAR cmd[MAX_PATH];
|
|
HANDLE tempInput;
|
|
HANDLE tempOut;
|
|
HANDLE mapping;
|
|
PCSTR fileData;
|
|
PCSTR endOfFile;
|
|
CHAR root[MAX_PATH];
|
|
CHAR client[MAX_PATH];
|
|
GROWLIST parentBranch = INIT_GROWLIST;
|
|
GROWLIST branchStorage = INIT_GROWLIST;
|
|
GROWLIST branchProject = INIT_GROWLIST;
|
|
GROWLIST branchPath = INIT_GROWLIST;
|
|
GROWLIST storedSpec = INIT_GROWLIST;
|
|
GROWLIST localSpec = INIT_GROWLIST;
|
|
PCTSTR ntBinDir;
|
|
BOOL changed = FALSE;
|
|
UINT currentBranch;
|
|
|
|
ntBinDir = getenv ("_NTBINDIR");
|
|
if (!ntBinDir && !localRoot) {
|
|
fprintf (stderr, "%%_NTBINDIR%% required to be set\n");
|
|
exit (1);
|
|
}
|
|
|
|
tempInput = BfGetTempFile (); // handle & file cleans up with process termination
|
|
|
|
if (!tempInput) {
|
|
printf ("Can't create temp input file\n");
|
|
exit (1);
|
|
}
|
|
|
|
tempOut = BfGetTempFile (); // handle & file cleans up with process termination
|
|
|
|
if (!tempOut) {
|
|
printf ("Can't create temp output file\n");
|
|
exit (1);
|
|
}
|
|
|
|
for (currentBranch = 0 ; currentBranch < branches ; currentBranch++) {
|
|
|
|
printf ("Branch: %s\n\n", branchName[currentBranch]);
|
|
|
|
wsprintf (cmd, TEXT("sd branch -o %s"), branchName[currentBranch]);
|
|
if (!pLaunchSd (
|
|
cmd,
|
|
INVALID_HANDLE_VALUE,
|
|
tempOut,
|
|
"Getting branch mapping...",
|
|
&mapping,
|
|
&fileData,
|
|
&endOfFile
|
|
)) {
|
|
exit (1);
|
|
}
|
|
|
|
if (!pParseBranchMapping (fileData, endOfFile, &parentBranch, &branchStorage)) {
|
|
exit (1);
|
|
}
|
|
|
|
if (!pLaunchSd (
|
|
TEXT("sd client -o"),
|
|
INVALID_HANDLE_VALUE,
|
|
tempOut,
|
|
"Getting client mapping...",
|
|
&mapping,
|
|
&fileData,
|
|
&endOfFile
|
|
)) {
|
|
exit (1);
|
|
}
|
|
|
|
if (!pParseClientMapping (fileData, endOfFile, client, root, &storedSpec, &localSpec)) {
|
|
exit (1);
|
|
}
|
|
|
|
if (localRoot) {
|
|
if (!StringIPrefix (localRoot, root)) {
|
|
fprintf (stderr, "Local root %s must be in client root of %s\n", localRoot, root);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
if (!pVerifyBranch (branchName[currentBranch], &branchStorage, &branchProject, &branchPath, localRoot, root)) {
|
|
exit (1);
|
|
}
|
|
|
|
printf ("\n\n");
|
|
|
|
if (dumpStatus) {
|
|
|
|
pDumpBranchStatus (
|
|
&parentBranch,
|
|
&branchStorage,
|
|
&branchProject,
|
|
&branchPath,
|
|
&storedSpec,
|
|
&localSpec,
|
|
localRoot,
|
|
ntBinDir,
|
|
root,
|
|
client
|
|
);
|
|
|
|
} else if (deleteMode) {
|
|
changed = pDeleteAllMappings (
|
|
&branchStorage,
|
|
&branchPath,
|
|
&storedSpec,
|
|
&localSpec,
|
|
client
|
|
);
|
|
} else {
|
|
changed = pAddAllMappings (
|
|
&branchStorage,
|
|
&branchPath,
|
|
&storedSpec,
|
|
&localSpec,
|
|
client
|
|
);
|
|
}
|
|
|
|
if (changed) {
|
|
if (!g_Commit) {
|
|
printf ("\nChanges not committed -- specify -c to commit\n");
|
|
} else {
|
|
|
|
SetFilePointer (tempInput, 0, NULL, FILE_BEGIN);
|
|
if (!pDumpClientView (tempInput, &storedSpec, &localSpec, client, root)) {
|
|
fprintf (stderr, "Error writing to temp file\n");
|
|
exit (1);
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
if (!pLaunchSd (
|
|
TEXT("sd client -i"),
|
|
tempInput,
|
|
tempOut,
|
|
"Setting client mapping...",
|
|
&mapping,
|
|
&fileData,
|
|
&endOfFile
|
|
)) {
|
|
exit (1);
|
|
}
|
|
|
|
printf ("\nChanges committed. Run sd sync to update your enlistment.\n\n");
|
|
}
|
|
} else {
|
|
printf ("No changes made.\n\n");
|
|
}
|
|
|
|
GlFree (&parentBranch);
|
|
GlFree (&branchStorage);
|
|
GlFree (&branchProject);
|
|
GlFree (&branchPath);
|
|
GlFree (&storedSpec);
|
|
GlFree (&localSpec);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// End of processing
|
|
//
|
|
|
|
Terminate();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|