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