2 #include "SVNDirectory.hxx"
7 #include <boost/foreach.hpp>
9 #include <simgear/debug/logstream.hxx>
10 #include <simgear/misc/strutils.hxx>
11 #include <simgear/misc/sg_dir.hxx>
12 #include <simgear/io/HTTPClient.hxx>
13 #include <simgear/io/DAVMultiStatus.hxx>
14 #include <simgear/io/SVNRepository.hxx>
15 #include <simgear/io/sg_file.hxx>
16 #include <simgear/io/SVNReportParser.hxx>
17 #include <simgear/package/md5.h>
18 #include <simgear/structure/exception.hxx>
23 using namespace simgear;
25 typedef std::vector<HTTP::Request_ptr> RequestVector;
26 typedef std::map<std::string, DAVResource*> DAVResourceMap;
29 const char* DAV_CACHE_NAME = ".terrasync_cache";
30 const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
31 const unsigned int MAX_UPDATE_REPORT_DEPTH = 3;
39 SVNDirectory::SVNDirectory(SVNRepository *r, const SGPath& path) :
43 _doingUpdateReport(false),
50 // don't create dir here, repo might not exist at all
53 SVNDirectory::SVNDirectory(SVNDirectory* pr, DAVCollection* col) :
55 repo(pr->repository()),
56 _doingUpdateReport(false),
59 assert(col->container());
60 assert(!col->url().empty());
63 localPath = pr->fsDir().file(col->name());
64 if (!localPath.exists()) {
73 SVNDirectory::~SVNDirectory()
75 // recursive delete our child directories
76 BOOST_FOREACH(SVNDirectory* d, _children) {
81 void SVNDirectory::parseCache()
84 p.append(DAV_CACHE_NAME);
90 char versionName[128];
91 LineState lineState = LINESTATE_HREF;
92 std::ifstream file(p.c_str());
93 bool doneSelf = false;
95 file.getline(href, 1024);
96 if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
97 SG_LOG(SG_IO, SG_WARN, "invalid cache file:" << p.str());
102 file.getline(href, 1024);
105 while (!file.eof()) {
106 if (lineState == LINESTATE_HREF) {
107 file.getline(href, 1024);
108 lineState = LINESTATE_VERSIONNAME;
110 assert(lineState == LINESTATE_VERSIONNAME);
111 file.getline(versionName, 1024);
112 lineState = LINESTATE_HREF;
113 char* hrefPtr = href;
117 dav = new DAVCollection(hrefPtr);
118 dav->setVersionName(versionName);
120 assert(string(hrefPtr) == dav->url());
123 if (!vccUrl.empty()) {
124 dav->setVersionControlledConfiguration(vccUrl);
127 _cachedRevision = versionName;
130 DAVResource* child = addChildDirectory(hrefPtr)->collection();
131 child->setVersionName(versionName);
133 } // of line-state switching
134 } // of file get-line loop
137 void SVNDirectory::writeCache()
145 p.append(DAV_CACHE_NAME);
147 std::ofstream file(p.c_str(), std::ios::trunc);
148 // first, cache file version header
149 file << CACHE_VERSION_4_TOKEN << '\n';
151 // second, the repository VCC url
152 file << dav->versionControlledConfiguration() << '\n';
154 // third, our own URL, and version
155 file << dav->url() << '\n' << _cachedRevision << '\n';
157 BOOST_FOREACH(DAVResource* child, dav->contents()) {
158 if (child->isCollection()) {
159 file << child->name() << '\n' << child->versionName() << "\n";
161 } // of child iteration
164 void SVNDirectory::setBaseUrl(const string& url)
167 SG_LOG(SG_IO, SG_ALERT, "setting base URL on non-root directory " << url);
171 if (dav && (url == dav->url())) {
175 dav = new DAVCollection(url);
178 std::string SVNDirectory::url() const
181 return repo->baseUrl();
184 return _parent->url() + "/" + name();
187 std::string SVNDirectory::name() const
193 SVNDirectory::addChildFile(const std::string& fileName)
195 DAVResource* child = NULL;
196 child = new DAVResource(dav->urlForChildWithName(fileName));
197 dav->addChild(child);
204 SVNDirectory::addChildDirectory(const std::string& dirName)
206 if (dav->childWithName(dirName)) {
207 // existing child, let's remove it
208 deleteChildByName(dirName);
211 DAVCollection* childCol = dav->createChildCollection(dirName);
212 SVNDirectory* child = new SVNDirectory(this, childCol);
213 _children.push_back(child);
218 void SVNDirectory::deleteChildByName(const std::string& nm)
220 DAVResource* child = dav->childWithName(nm);
222 // std::cerr << "ZZZ: deleteChildByName: unknown:" << nm << std::endl;
226 SGPath path = fsDir().file(nm);
227 dav->removeChild(child);
230 if (child->isCollection()) {
234 DirectoryList::iterator it = findChildDir(nm);
235 if (it != _children.end()) {
236 SVNDirectory* c = *it;
237 // std::cout << "YYY: deleting SVNDirectory for:" << nm << std::endl;
248 void SVNDirectory::requestFailed(HTTP::Request *req)
250 SG_LOG(SG_IO, SG_WARN, "Request failed for:" << req->url());
253 bool SVNDirectory::isDoingSync() const
255 if (_doingUpdateReport) {
259 BOOST_FOREACH(SVNDirectory* child, _children) {
260 if (child->isDoingSync()) {
268 void SVNDirectory::beginUpdateReport()
270 _doingUpdateReport = true;
271 _cachedRevision.clear();
275 void SVNDirectory::updateReportComplete()
277 _cachedRevision = dav->versionName();
278 _doingUpdateReport = false;
281 SVNDirectory* pr = parent();
287 SVNRepository* SVNDirectory::repository() const
292 void SVNDirectory::mergeUpdateReportDetails(unsigned int depth,
295 // normal, easy case: we are fully in-sync at a revision
296 if (!_cachedRevision.empty()) {
297 std::ostringstream os;
298 os << "<S:entry rev=\"" << _cachedRevision << "\" depth=\"infinity\">"
299 << repoPath() << "</S:entry>";
300 items.push_back(os.str());
305 if (depth >= MAX_UPDATE_REPORT_DEPTH) {
306 std::cerr << localPath << "exceeded MAX_UPDATE_REPORT_DEPTH, cleaning" << std::endl;
311 PathList cs = d.children(Dir::NO_DOT_OR_DOTDOT | Dir::INCLUDE_HIDDEN | Dir::TYPE_DIR);
312 BOOST_FOREACH(SGPath path, cs) {
313 SVNDirectory* c = child(path.file());
315 // ignore this child, if it's an incomplete download,
316 // it will be over-written on the update anyway
317 //std::cerr << "unknown SVN child" << path << std::endl;
319 // recurse down into children
320 c->mergeUpdateReportDetails(depth+1, items);
322 } // of child dir iteration
325 std::string SVNDirectory::repoPath() const
331 // find the length of the repository base URL, then
332 // trim that off our repo URL - job done!
333 size_t baseUrlLen = repo->baseUrl().size();
334 return dav->url().substr(baseUrlLen + 1);
337 SVNDirectory* SVNDirectory::parent() const
342 SVNDirectory* SVNDirectory::child(const std::string& dirName) const
344 BOOST_FOREACH(SVNDirectory* d, _children) {
345 if (d->name() == dirName) {
353 DirectoryList::iterator
354 SVNDirectory::findChildDir(const std::string& dirName)
356 DirectoryList::iterator it;
357 for (it=_children.begin(); it != _children.end(); ++it) {
358 if ((*it)->name() == dirName) {
365 simgear::Dir SVNDirectory::fsDir() const
367 return Dir(localPath);