/*++ Copyright (c) 1998 Microsoft Corporation Module Name: main.c Abstract: Author: TODO: () Revision History: () --*/ #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 arg.\n\n" " Examples: (assume 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 [-p:] [-d] [-o] [-c]\n" "\nArguments: (order-insensitive)\n\n" " specifies the Source Depot branch name to map in. A max of\n" " 16 branches can be specified.\n\n" " -p: specifies an alternative local subdirectory to\n" " use as the path base. If specified, //depot/private/{branch}/{project}\n" " is replaced by .\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; }