1 // DevStudioFileStore.cpp: implementation of the DevStudioFileStore class.
2 //
3 //////////////////////////////////////////////////////////////////////
4 
5 #include "stdafx.h"
6 
7 // Turn off warnings due to long names arising out of STL use
8 #pragma warning ( disable : 4786 4503 )
9 
10 #include "CcccDevStudioAddIn.h"
11 #include "DevStudioFileStore.h"
12 
13 // Use of the COM interface to the Visual Studio workspace is
14 // localised to this file.
15 #include <ObjModel\addguid.h>
16 #include <ObjModel\appguid.h>
17 #include <ObjModel\bldguid.h>
18 #include <ObjModel\textguid.h>
19 #include <ObjModel\dbgguid.h>
20 
21 #include <fstream>
22 
23 #ifdef _DEBUG
24 #undef THIS_FILE
25 static char THIS_FILE[]=__FILE__;
26 #define new DEBUG_NEW
27 #endif
28 
29 // This file contains logic shamelessly copied from Joshua C. Jensen's
30 // Workspace Utilities package.
31 
32 //////////////////////////////////////////////////////////////////////
33 // Construction/Destruction
34 //////////////////////////////////////////////////////////////////////
DevStudioFileStore(IApplication * pApp,const std::string & wsDir,const std::string & wsName)35 DevStudioFileStore::DevStudioFileStore(IApplication *pApp,
36 									   const std::string& wsDir, const std::string& wsName)
37 									   : m_pApplication(pApp)
38 									   , m_WorkspaceDirectory(wsDir)
39 									   , m_WorkspaceName(wsName)
40 {
41 	CComBSTR bszWSPath, bszWSName, bszWSFullName;
42 
43 	VERIFY_OK(m_pApplication->get_Path(&bszWSPath));
44 	VERIFY_OK(m_pApplication->get_Name(&bszWSName));
45 	VERIFY_OK(m_pApplication->get_FullName(&bszWSFullName));
46 
47 	VERIFY_OK(m_pApplication->PrintToOutputWindow(bszWSPath));
48 	VERIFY_OK(m_pApplication->PrintToOutputWindow(bszWSName));
49 	VERIFY_OK(m_pApplication->PrintToOutputWindow(bszWSFullName));
50 
51 	// First, get a pointer to the dispatch for the Projects collection
52 	CComPtr<IDispatch> pDispProjects;
53 	VERIFY_OK(m_pApplication->get_Projects(&pDispProjects));
54 	CComQIPtr<IProjects, &IID_IProjects> pProjects(pDispProjects);
55 
56 	// Get the number of projects in the collection
57 	long workspaceProjectCount;
58 	VERIFY_OK(pProjects->get_Count(&workspaceProjectCount));
59 
60 	// get information on the projects
61 	for (long i = 1; i < workspaceProjectCount + 1; i++)
62 	{
63 		CComVariant Vari = i;
64 
65 		// Get the next project
66 		CComPtr<IGenericProject> pGenProject;
67 		VERIFY_OK(pProjects->Item(Vari, &pGenProject));
68 		CComQIPtr<IGenericProject, &IID_IGenericProject> pProject(pGenProject);
69 
70 		CComBSTR bszStr;
71 		VERIFY_OK(pProject->get_FullName(&bszStr));
72 		CString projectName = bszStr;
73 
74 		ProjectFileStore::value_type pfsPair;
75 		std::string pfsName=projectName.GetBuffer(0);
76 		FilenameList pfsList;
77 		collectFilenames(pfsName,"SOURCE=",pfsList);
78 		m_ProjectFileStore.insert(std::make_pair(pfsName,pfsList));
79 	}  // end for [1..workspaceProjectCount]
80 
81 	// dumpProjectFileList();
82 }
83 
~DevStudioFileStore()84 DevStudioFileStore::~DevStudioFileStore()
85 {
86 }
87 
88 
89 
listWorkspaceFiles(FilenameList & fileList)90 void DevStudioFileStore::listWorkspaceFiles(FilenameList& fileList)
91 {
92 	ProjectFileStore::const_iterator pfsIter;
93 	for(pfsIter=m_ProjectFileStore.begin();
94 	pfsIter!=m_ProjectFileStore.end();
95 	++pfsIter)
96 	{
97 		FilenameList::const_iterator flIter;
98 		for(flIter=pfsIter->second.begin();
99 		flIter!=pfsIter->second.end();
100 		++flIter)
101 		{
102 			fileList.insert(*flIter);
103 		}
104 	}
105 
106 }
107 
listProjectFiles(std::string projectName,FilenameList & fileList)108 void DevStudioFileStore::listProjectFiles(std::string projectName, FilenameList& fileList)
109 {
110 	ProjectFileStore::const_iterator pfsIter;
111 	pfsIter=m_ProjectFileStore.find(projectName);
112 	std::string fileNames;
113 	if(pfsIter!=m_ProjectFileStore.end())
114 	{
115 		FilenameList::const_iterator flIter;
116 		for(flIter=pfsIter->second.begin();
117 		flIter!=pfsIter->second.end();
118 		++flIter)
119 		{
120 			fileList.insert(*flIter);
121 			fileNames+=*flIter+" ";
122 		}
123 		dump(fileNames);
124 	}
125 	else
126 	{
127 		std::string msg = "Project ";
128 		msg.append(projectName);
129 		msg.append(" not found");
130 		AfxMessageBox(msg.c_str());
131 	}
132 }
133 
composeFilename(const std::string & baseDir,const std::string & relName)134 std::string DevStudioFileStore::composeFilename(const std::string& baseDir, const std::string& relName)
135 {
136 	std::string retval;
137 
138 	// baseDir can either be a directory specification ending in a '\' or '/'
139 	// or a file specification.
140 	// Either way
141 	// Now resolve relative paths.
142 
143 	// We want to arrive at a canonical name for the file, that is a
144 	// name which gives the drive letter and full path, with no use of
145 	// ".."  .  We are provided with the name of the referring file
146 	// and a filename (possibly) relative to that location.
147 
148 	// Our algorithm is to compose a non-canonical name by examining
149 	// the potentially relative name and prepending the directory path
150 	// of the base location unless the relative part is seen to be
151 	// absolute.
152 
153 	std::string nonCanonicalName;
154 	// the classes of strings which are absolute in their
155 	// own right are
156 	// 1) those which start with / or \ indicating the root directory
157 	//    on the current drive; and
158 	// 2) those which start with X: or X: indicating the root directory
159 	//    on the specified drive
160 	if( (relName[0]=='\\') || (relName[0] == '/') || (relName[1]==':') )
161 	{
162 		nonCanonicalName=relName;
163 	}
164 	else
165 	{
166 		std::string::size_type endOfPath=baseDir.find_last_of("\\/");
167 		if(endOfPath!=std::string::npos)
168 		{
169 			nonCanonicalName=baseDir.substr(0,endOfPath+1)+relName;
170 		}
171 		else
172 		{
173 			// we needed a path separator and didn't find one,
174 			// so we are basically stuffed.
175 			std::string msg;
176 			msg.append("Couldn't compose name from ");
177 			msg.append(baseDir);
178 			msg.append(" and ");
179 			msg.append(relName);
180 			AfxMessageBox(msg.c_str());
181 		}
182 	}
183 
184 	// Hopefully, nonCanonicalName now contains _one_ path to the
185 	// file.  We use the following logic to convert this to the
186 	// preferred name.
187 	if(nonCanonicalName.size()>0)
188 	{
189 		// The logic below has in the past contained experimentation
190 		// with various ways of accessing a canonical name for the
191 		// file.
192 		// There are still two different APIs covered, anyone who knows
193 		// of a reason why one is better than another or why anything else
194 		// would be better (remember I want to support Win95/98/NT4/2000
195 		// in a single set of source), please mail me.
196 		CFileStatus fileStatus;
197 		CFile::GetStatus(nonCanonicalName.c_str(), fileStatus);
198 
199 		char getFullPathNameBuf[1024],*shortNamePtr;
200 		unsigned long len=1024;
201 		memset(getFullPathNameBuf,0,len);
202 		int gcode=GetFullPathName(const_cast<char*>(nonCanonicalName.c_str()),
203 			len,getFullPathNameBuf,&shortNamePtr);
204 
205 
206 		char msgbuf[1024];
207 		sprintf(msgbuf,"fs=%s, "
208             "GetFull returned %d name 3=%s short=%s",
209 			fileStatus.m_szFullName,gcode,getFullPathNameBuf,shortNamePtr
210 			);
211 
212 		// AfxMessageBox(msgbuf);
213 		dump(msgbuf);
214 
215 		retval = fileStatus.m_szFullName;
216 	}
217 
218 	return retval;
219 }
220 
collectFilenames(const std::string & dsFile,const std::string & pfx,FilenameList & fileList)221 void DevStudioFileStore::collectFilenames(const std::string& dsFile, const std::string& pfx,
222 										  FilenameList& fileList)
223 {
224 	std::ifstream prjStr(dsFile.c_str());
225 
226 	const int pfxWidth=pfx.size();
227 
228 	while(prjStr.good())
229 	{
230 		char linebuf[1024];
231 		prjStr.getline(linebuf,1023);
232 		if(!prjStr.good())
233 		{
234 			// the stream has gone bad (hopefully due to simple exhaustion)
235 			// no action here and the loop should terminate
236 		}
237 		else if(strncmp(linebuf,pfx.c_str(),pfxWidth)==0)
238 		{
239 			std::string relname=linebuf+pfxWidth;
240 			std::string absName=composeFilename(dsFile,relname);
241 			fileList.insert(absName);
242 		}
243 	}
244 }
245 
dumpProjectFileList()246 void DevStudioFileStore::dumpProjectFileList()
247 {
248 	ProjectFileStore::const_iterator pfsIter;
249 	for(pfsIter=m_ProjectFileStore.begin();
250 	pfsIter!=m_ProjectFileStore.end();
251 	++pfsIter)
252 	{
253 		dump(pfsIter->first);
254 		dump(": ");
255 
256 		FilenameList::const_iterator flIter;
257 		for(flIter=pfsIter->second.begin();
258 		flIter!=pfsIter->second.end();
259 		++flIter)
260 		{
261 			dump(flIter->c_str());
262 		}
263 		dump("");
264 	}
265 }
266 
dump(const std::string & str)267 void DevStudioFileStore::dump(const std::string& str)
268 {
269 	VERIFY_OK(m_pApplication->PrintToOutputWindow(CComBSTR(str.c_str())));
270 }
271 
suggestedWorkingDirectory()272 std::string DevStudioFileStore::suggestedWorkingDirectory()
273 {
274 	// It would be nice to be able to make the working directory take on a constant
275 	// relationship to the .dsw file of the workspace, but there is no programmatic
276 	// way of accessing this item.
277 	// If we just use the current working directory of the
278 	// project, we seem to start off in the .dsw file directory,
279 	// until a file is opened, at which point the current working
280 	// directory reflects the directory in which the file open
281 	// dialog most recently displayed.  This is not very satisfactory,
282 	// so for the moment we just try to use a constant directory,
283 	// defined either by the environment variable CCCCTEMP, with fallbacks
284 	// to TEMP and to the hardcoded directory c:\ (the main virtue of the latter
285 	// being that we are reasonably confident it will exist).
286 	std::string retval;
287 
288 	char * envdir = getenv("CCCCTEMP");
289 	if(envdir!=0)
290 	{
291 		retval=envdir;
292 	}
293 	else
294 	{
295 		envdir=getenv("TEMP");
296 		if(envdir!=0)
297 		{
298 			retval=envdir;
299 		}
300 		else
301 		{
302 			retval="c:\\";
303 		}
304 	}
305 	return retval;
306 }
307 
308 
309