Cmd_DirStat.cpp. #1

  • //
  • guest/
  • YourUncleBob/
  • p4win/
  • main/
  • gui/
  • p4api/
  • Cmd_DirStat.cpp.
  • View
  • Commits
  • Open Download .zip Download (17 KB)
//
// Copyright 1998 Perforce Software.  All rights reserved.
//
//

// Cmd_DirStat.cpp

#include "stdafx.h"
#include "p4win.h"
#include "MainFrm.h"
#include "Cmd_Dirstat.h"
#include "Cmd_Fstat.h"
#include "Cmd_Dirs.h"
#include "Cmd_Where.h"
#include "strops.h"

#pragma warning (disable:4786)
#include <list>
#include <string>

using namespace std;

#ifdef UNICODE
typedef list<wstring> LISTSTR;
#else
typedef list<string> LISTSTR;
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


IMPLEMENT_DYNCREATE(CCmd_DirStat, CP4Command)


CCmd_DirStat::CCmd_DirStat(CGuiClient *client) : CP4Command(client)
{
	m_ReplyMsg= WM_P4DIRSTAT;
	m_TaskName= _T("DirStat");
	m_ShowOnlyNotInDepot = MainFrame()->m_ShowOnlyNotInDepot;
}

BOOL CCmd_DirStat::Run( CStringList *specs, BOOL entireDepot )
{
	m_EntireDepot= entireDepot;
	m_pSpecList=specs;
	return CP4Command::Run();
}

void CCmd_DirStat::PreProcess(BOOL& done)
{
	BOOL bSpecListHasWild = FALSE;
	BOOL bFileListHasWild = FALSE;
	CStringList files;
    POSITION pos;
	Error e;
	
	if (GET_P4REGPTR( )->ShowEntireDepot( ) <= SDF_DEPOT)
	{
		// Make a copy of the specList, since Cmd_Dirs will 
		// delete the list as it is processed
		CStringList specListCopy;
		for( pos= m_pSpecList->GetHeadPosition(); pos!= NULL; )
			specListCopy.AddHead( m_pSpecList->GetNext(pos) );

		// Set up and run Dirs synchronously
		CCmd_Dirs cmd1(m_pClient);
		cmd1.Init(NULL, RUN_SYNC);
		if(cmd1.Run( &specListCopy, m_EntireDepot ))
		{
			m_FatalError= cmd1.GetError();
			done=TRUE;
		}
		else
		{
			m_ErrorTxt= "Unable to Run Dirs";
			done= m_FatalError=TRUE;
		}
		cmd1.CloseConn(&e);

		CStringList *list= cmd1.GetList();
    
		// Copy the results from dirs
		if(!m_FatalError && list->GetCount() > 0)
		{
			for( pos= list->GetHeadPosition(); pos!= NULL; )
				m_Dirs.AddHead( list->GetNext(pos) );
		}
	}
	else
	{
		CString theroot = TheApp()->m_ClientRoot;
		if (GetFileAttributes(theroot) == -1)
		{
			done = TRUE;
			return;
		}
		TCHAR curdir[MAX_PATH+1];
		if ((theroot.GetAt(0) == _T('\\')) && GetCurrentDirectory(sizeof(curdir)/sizeof(TCHAR), curdir))
		{
			curdir[2] = _T('\0');
			theroot = curdir + theroot;
			TheApp()->m_ClientRoot = theroot;
		}
		if (theroot.GetLength() > 3)
			theroot += _T('\\');
		int therootlgth = theroot.GetLength();

		CFileFind finder;

		// find the directories
		for( pos= m_pSpecList->GetHeadPosition(); pos!= NULL; )
		{
			CString dirname = m_pSpecList->GetNext(pos);
			if (dirname.FindOneOf(_T("@#%")) != -1)
				bSpecListHasWild = TRUE;
			if (dirname.GetAt(0) == _T('/'))
				continue;	// we don't handle depot syntax here
			else
				dirname.Replace(_T('/'), _T('\\'));

			// check to make sure the desired directory
			// is actually under the client's root dir
			if (_tcsnicmp(dirname, theroot, therootlgth))
				continue;

			// now find all the dirs and files in the directory
			BOOL bWorking = finder.FindFile(dirname);

			while (bWorking)
			{
				bWorking = finder.FindNextFile();

				// skip . and .. files
				if (finder.IsDots())
					continue;

				CString str = finder.GetFilePath();

				// we can't deal with files or directories 
				// with * in their name, so skip 'em
				if (str.Find(_T('*')) != -1)
				{
					CString txt;
					txt.FormatMessage(IDS_CANTDEALWITH_ASTERISK, str);
					TheApp()->StatusAdd(txt, SV_WARNING);
					continue;
				}

				// if it's a directory, add it to the command's dirs list
				if (finder.IsDirectory())
				{
					m_Dirs.AddHead( str );
				}
				// if it's a regular file, save it for later processing
				// in the fstat part of this routine below
				else if ((GET_P4REGPTR( )->ShowEntireDepot( ) == SDF_LOCALTREE)
					&& (!finder.IsHidden() || GET_P4REGPTR( )->ShowHiddenFilesNotInDepot())
					&& !finder.IsSystem())
				{
					if (str.FindOneOf(_T("@#%")) != -1)
						bFileListHasWild = TRUE;
					files.AddTail( str );
				}
			}
		}

		finder.Close();

		// if we only want to see Perforce files we now need to eliminate
		// all the directories that don't have Perforce files - so run p4 dirs
		if (m_Dirs.GetCount())
		{
			// Save a copy of the m_Dirs, since Cmd_Dirs will 
			// delete the list as it is processed
			BOOL bWild = FALSE;
			BOOL b = GET_P4REGPTR( )->ShowEntireDepot( ) > SDF_DEPOT;	// Local View?
			CStringList saveDirs;
			for( pos= m_Dirs.GetHeadPosition(); pos!= NULL; )
			{
				CString str = m_Dirs.GetNext(pos);
				if (b && str.FindOneOf(_T("@#%")) != -1)
					bWild = TRUE;
				saveDirs.AddHead( str );
			}
			if (bWild)
			{
				m_Dirs.RemoveAll();
				for( pos= saveDirs.GetHeadPosition(); pos!= NULL; )
				{
					CString str =  saveDirs.GetNext(pos);
					if (b && str.FindOneOf(_T("@#%")) != -1)
					{
						int star = str.Find(_T('*'));
						StrBuf b;
						StrBuf f;
						f << CharFromCString(str);
						StrPtr *p = &f;
						StrOps::WildToStr(*p, b);
						str = CharToCString(b.Value());
						if (star != -1)
						{
							while ((star = str.Find(_T("%2A"))) != -1)
							{
								CString s1 = str.Left(star);
								CString s2 = str.Mid(star + sizeof(_T("%2A")));
								str = s1 + _T('*') + s2;
							}
						}
					}
					m_Dirs.AddHead( str );
				}
			}

			// Set up and run Dirs synchronously
			CCmd_Dirs cmd1(m_pClient);
			cmd1.Init(NULL, RUN_SYNC);
			if(cmd1.Run( &m_Dirs, m_EntireDepot ))
			{
				m_FatalError= cmd1.GetError();
				done=TRUE;
			}
			else
			{
				m_ErrorTxt= "Unable to Run Dirs";
				done= m_FatalError=TRUE;
			}
			cmd1.CloseConn(&e);

			// all we care about is the error listing
			CStringList *list= cmd1.GetErrors();
    
			// Make sure m_Dirs was emptied
			m_Dirs.RemoveAll();

			// Look thru the directories and remove any
			// mentioned in an error message
			if(!m_FatalError && list->GetCount() > 0)
			{
				int lgth = TheApp()->m_ClientRoot.GetLength();
				CStringList fstatlist;
				CStringList fdirslist;
				POSITION pos2;
				for( pos = saveDirs.GetHeadPosition(); pos!= NULL; )
				{
					CString dir = saveDirs.GetNext(pos);
					BOOL bAdd = TRUE;
					for( pos2 = list->GetHeadPosition(); pos2 != NULL; )
					{
						CString errmsg = list->GetNext(pos2);
						if ((tolower(dir.GetAt(lgth)) == tolower(errmsg.GetAt(lgth)))
						  && errmsg.Find(dir) == 0)
						{
							if (errmsg.Find(dir + _T(" - file(s) not in client view")) == 0)
							{
								if (GET_SERVERLEVEL() >= 25)	// 2008.1 or later?
								{
									CString dirdotdotdot = dir + _T("\\...");
									fstatlist.AddTail(dirdotdotdot);
									fdirslist.AddTail(dir);
								}
								bAdd = FALSE;
							}
							else if (errmsg.Find(dir + _T(" - no mappings in client view")) == 0)
								m_NotMapped.AddHead(dir);
							break;
						}
					}
					if (bAdd)
						m_Dirs.AddHead(dir);
				}

				if (fstatlist.GetCount())
				{
					CCmd_Fstat cmd2(m_pClient);
					cmd2.Init(NULL, RUN_SYNC);
					if(cmd2.Run( FALSE, &fstatlist, m_EntireDepot, 0, FALSE, -1, 1 ))
					{
						if (cmd2.GetFileList()->GetCount())
						{
							CObList *fstatList= cmd2.GetFileList();
							for( pos= fstatList->GetHeadPosition(); pos!= NULL; )
							{
								CP4FileStats *stats = ( CP4FileStats * )fstatList->GetNext( pos );
								ASSERT_KINDOF( CP4FileStats, stats );
								CString clientPath = stats->GetFullClientPath( );
								for( pos2= fdirslist.GetHeadPosition(); pos2!= NULL; )
								{
									CString dir = fdirslist.GetNext(pos2);
									CString dirslash = dir + _T("\\");
									CString path = clientPath.Left(dirslash.GetLength());
									if (path == dirslash)
									{
										m_Dirs.AddHead(dir);
										break;
									}
								}
								delete stats;
							}
						}
					}
					else
					{
						m_ErrorTxt= _T("Unable to Run Fstat");
						m_FatalError=TRUE;
					}
				}
			}
			else	// we have to put the saved dirs back in m_Dirs
			{
				for( pos = saveDirs.GetHeadPosition(); pos!= NULL; )
					m_Dirs.AddHead(saveDirs.GetNext(pos));
			}
		}
	}

	if(!m_FatalError)
	{
     	// Set up and run fstat
		// Fisrt convert any wild syntax if there is any
		CStringList *pstrlist = m_pSpecList;
		CStringList strlist;
		if (bSpecListHasWild)
		{
			for( pos= m_pSpecList->GetHeadPosition(); pos!= NULL; )
			{
				CString str = m_pSpecList->GetNext(pos);
				if (str.FindOneOf(_T("@#%")) != -1)
				{
					int star = str.Find(_T('*'));
					StrBuf b;
					StrBuf f;
					f << CharFromCString(str);
					StrPtr *p = &f;
					StrOps::WildToStr(*p, b);
					str = CharToCString(b.Value());
					if (star != -1)
					{
						while ((star = str.Find(_T("%2A"))) != -1)
						{
							CString s1 = str.Left(star);
							CString s2 = str.Mid(star + sizeof(_T("%2A")));
							str = s1 + _T('*') + s2;
						}
					}
				}
				strlist.AddTail( str );
			}
			pstrlist = &strlist;
		}
		// Now run the Fstat
	    CCmd_Fstat cmd2(m_pClient);
		cmd2.Init(NULL, RUN_SYNC);
		if(cmd2.Run( FALSE, pstrlist, m_EntireDepot, 0, FALSE, -1, -1 ))
            m_FatalError= cmd2.GetError();
        else
		{
			m_ErrorTxt= _T("Unable to Run Fstat");
			m_FatalError=TRUE;
		}

        CObList *fstatList= cmd2.GetFileList();
		    
	    // Copy the results from fstat
	    if(!m_FatalError && fstatList->GetCount() > 0)
	    {
			if (GET_P4REGPTR( )->ShowEntireDepot( ) == SDF_LOCALTREE)
			{
				// We need to generate 2 sorted lists:
				LISTSTR filesarray;		// 1) the 'files' names
				LISTSTR fstatarray;		// 2) the pFileStats->GetFullClientPath() names
				LISTSTR::iterator ifi;	// iterator for filesarray
				LISTSTR::iterator ifs;	// iterator for fstatarray

				for (pos = files.GetHeadPosition(); pos != NULL; )
				{
					filesarray.insert(filesarray.end(), 
						(TCHAR *)(files.GetNext( pos ).GetBuffer()));
				}
				filesarray.sort();

			    for( pos= fstatList->GetHeadPosition(); pos!= NULL; )
				{
				    CObject *cobject = fstatList->GetNext(pos);
				    m_Files.AddHead( cobject );
					if (((CP4FileStats *)cobject)->GetHeadAction() != F_DELETE)
						fstatarray.insert(fstatarray.end(), 
							(TCHAR *)(((CP4FileStats *)cobject)->GetFullClientPath()));
				}
				fstatarray.sort();

				// now walk both arrays at the same time
				// add any file not in the fstatarray to the 'files' list
				files.RemoveAll();
				ifi = filesarray.begin();
				ifs = fstatarray.begin();
				while (ifi != filesarray.end())
				{
#ifdef _DEBUG
					if (ifs != fstatarray.end())
					{
						CString ifistr = (*ifi).c_str();
						CString ifsstr = (*ifs).c_str();
						int i = fstatarray.size();
						i--;	// to shutup the compiler
					}
#endif
					if (ifs != fstatarray.end() 
					 && Compare((*ifi).c_str(), (*ifs).c_str())==0)
					{
						ifi++;						// found it in the fstats
						fstatarray.remove((*ifs).c_str());// remove it from the fstat list
						ifs = fstatarray.begin();	// setup for next item
						continue;
					}
					if (ifs != fstatarray.end())	// keep checking if not at end of fstat list
					{
						ifs++;						// next fstat item
						continue;
					}
					files.AddHead( (*ifi++).c_str() );	// not in fstat list; add to not in depot list
					ifs = fstatarray.begin();			// setup for next item
				}
			}
			else
			{
				for( pos= fstatList->GetHeadPosition(); pos!= NULL; )
				{
					CObject *cobject = fstatList->GetNext(pos);
					m_Files.AddHead( cobject );
				}
			}
        }

		if (GET_P4REGPTR( )->ShowEntireDepot( ) > SDF_DEPOT)
		{
			// Now see if there were any " - no mappings in client view" errors
			// if so, add those directories to the 'm_NotMapped' list
			CStringList *list= cmd2.GetErrors();
			if (list->GetCount())
			{
				for(POSITION pos = list->GetHeadPosition(); pos != NULL; )
				{
					int i;
					CString errmsg = list->GetNext(pos);
					if ((i = errmsg.Find(_T('*'))) > 0)
					{
						errmsg = errmsg.Left(i);
						if (errmsg.GetLength() != 3)	// handle C:\ == root
							TrimRightMBCS(errmsg, _T("\\"));
						m_NotMapped.AddHead(errmsg);
					}
				}
			}
		}

		if (GET_P4REGPTR( )->ShowEntireDepot( ) == SDF_LOCALTREE)
		{
			// 'files' contains a list of files that are 
			// under the root but not under Perforce control.
			// 'm_NotMapped' contains a list of directories
			// that are not in the client
			// Now remove any files that cannot be added to the client
			POSITION pos;
			POSITION pos1, pos2;
			if (files.GetCount() && m_NotMapped.GetCount())
			{
				int i;
				for (pos = m_NotMapped.GetHeadPosition(); pos != NULL; )
				{
					CString dir = m_NotMapped.GetNext( pos );
					for (pos1 = files.GetHeadPosition(); ( pos2 = pos1 ) != NULL; )
					{
						CString file = files.GetNext( pos1 );
						if ((i = ReverseFindMBCS(file, _T('\\'))) == -1)
							continue;
						file = file.Left(i);
						if (file == dir)
						{
							// Don't mess with pos1!
							files.RemoveAt( pos2 );
						}
					}
				}
			}
			if (files.GetCount())
			{
				cmd2.CloseConn(&e);

				// if any addable files contain @ # or %, we have to convert them
				if (bFileListHasWild)
				{
					BOOL bWild = FALSE;
					CStringList strlist;
					for (POSITION pos = files.GetHeadPosition(); pos != NULL; )
					{
						CString file = files.GetNext( pos );
						if (file.FindOneOf(_T("@#%")) != -1)
							bWild = TRUE;
						strlist.AddHead( file );
					}
					if (bWild)
					{
						files.RemoveAll();
						for (POSITION pos = strlist.GetHeadPosition(); pos != NULL; )
						{
							CString file = strlist.GetNext( pos );
							if (file.FindOneOf(_T("@#%")) != -1)
							{
								int star = file.Find(_T('*'));
								StrBuf b;
								StrBuf f;
								f << CharFromCString(file);
								StrPtr *p = &f;
								StrOps::WildToStr(*p, b);
								file = CharToCString(b.Value());
								if (star != -1)
								{
									while ((star = file.Find(_T("%2A"))) != -1)
									{
										CString s1 = file.Left(star);
										CString s2 = file.Mid(star + sizeof(_T("%2A")));
										file = s1 + _T('*') + s2;
									}
								}
							}
							files.AddHead( file );
						}
					}
				}

				// Now create FileStats for all the addable files
				// First run 'p4 where' on all the files to
				// get both the local and the depot syntax
				CCmd_Where cmd3(m_pClient);
				cmd3.Init(NULL, RUN_SYNC);
				if ( cmd3.Run(&files) && !cmd3.GetError() )
				{
					CStringList *locals = cmd3.GetLocalFiles();
					CStringList *depots = cmd3.GetDepotFiles();
					ASSERT(locals->GetCount() == depots->GetCount());

					// get the fstat of files opened for add
					CObList *list = (CObList *)::SendMessage(m_ReplyWnd, WM_GETADDFSTATS, 0, 0);
					ASSERT_KINDOF(CObList, list);
					int listcount = list->GetCount();

					// now walk the 'p4 where' output
					CString localprev = _T("");
					for (pos1 = locals->GetHeadPosition(), 
						 pos2 = depots->GetHeadPosition(); pos1 != NULL; )
					{
						CString localsyntax = locals->GetNext( pos1 );
						CString depotsyntax = depots->GetNext( pos2 );

						// check for duplicates (due to a + mapping in clinet's view)
						if (localprev == localsyntax)
							continue;
						localprev = localsyntax;

						// do we have an unmap record?
						if (depotsyntax.GetAt(0) == _T('-'))
						{
							depotsyntax = depotsyntax.Mid(1);
							POSITION pos= m_Files.GetHeadPosition();
							while(pos != NULL)
							{
								// Get the filestats info for files opened for add
								CP4FileStats *stats = (CP4FileStats *) m_Files.GetNext(pos);
								if ( depotsyntax == stats->GetFullDepotPath() )
								{
									// since the obvious m_Files.RemoveAt(pos) doesn't work
									// we just mark the record as deleted 
									// by setting the depotpat to the empty string
									stats->SetDepotPath(_T(""));
									break;
								}
							}
							// clear the 'localprev' since we might
							// subsequently map this same file back in
							localprev = _T("-");
							// we certainly don't want to add
							// a new fstat record for an unmap
							continue;
						}

						CP4FileStats *newStats= new CP4FileStats;
						BOOL b = FALSE;
						// if there are files opened for add, 
						// we need to check that list before creating a
						// whole new fstat record from the bare 'p4 where' data
						if (listcount)
						{
							POSITION pos= list->GetHeadPosition();
							while(pos != NULL)
							{
								// Get the filestats info for files opened for add
								CP4FileStats *stats = (CP4FileStats *) list->GetNext(pos);
								ASSERT_KINDOF(CP4FileStats, stats);

								// is this file which has been added the same as 
								// the file we are examining from the 'p4 where' list?
								if ( depotsyntax == stats->GetFullDepotPath() )
								{
									// Create a copy of the fstat info
									newStats->Create(stats);
									newStats->SetClientPath(localsyntax);
									newStats->SetNotInDepot(TRUE);
									newStats->SetHaveRev(0);
									listcount--;
									b = TRUE;
									break;
								}
							}
						}
						// If we didn't create a new fstat because it wasn't open for add
						// we have to create a sparse fstat record from the 'p4 where' data
						if(!b && !newStats->Create(localsyntax, depotsyntax))
							delete newStats;
						else
						{
							// now add out newly created fstat record to the list
							CObject *cobject = (CP4FileStats *)newStats;
							m_Files.AddHead( cobject );
						}
					}
				}
				else
					TheApp()->StatusAdd( LoadStringResource(IDS_UNABLE_TO_SHOW_LOCAL_FILES), SV_WARNING );  
			}
		}
	}
	
    done=TRUE;
}
# Change User Description Committed
#1 19924 YourUncleBob Populate -o //guest/perforce_software/p4win/...
//guest/YourUncleBob/p4win/.....
//guest/perforce_software/p4win/main/gui/p4api/Cmd_DirStat.cpp
#1 16169 perforce_software Move files to follow new path scheme for branches.
//guest/perforce_software/p4win/gui/p4api/Cmd_DirStat.cpp
#1 8562 Matt Attaway These feet never stop running.

Initial commit of the P4Win source code.  To the best of our knowledge this
compiles and runs using the 2013.3 P4 API and VS 2010. Expect a few changes
as we refine the build process. Please post any build issues to the forums.