]> git.mxchange.org Git - simgear.git/blob - simgear/io/SVNDirectory.cxx
311fc4f9a7deb34727bff9292f39c321f333b0ed
[simgear.git] / simgear / io / SVNDirectory.cxx
1
2 #include "SVNDirectory.hxx"
3
4 #include <cassert>
5 #include <fstream>
6 #include <iostream>
7 #include <boost/foreach.hpp>
8
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>
19
20 using std::string;
21 using std::cout;
22 using std::endl;
23 using namespace simgear;
24
25 typedef std::vector<HTTP::Request_ptr> RequestVector;
26 typedef std::map<std::string, DAVResource*> DAVResourceMap;
27
28
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;
32
33 enum LineState
34 {
35   LINESTATE_HREF = 0,
36   LINESTATE_VERSIONNAME
37 };
38
39 SVNDirectory::SVNDirectory(SVNRepository *r, const SGPath& path) :
40   localPath(path),
41   dav(NULL),
42   repo(r),
43   _doingUpdateReport(false),
44   _parent(NULL)
45 {
46   if (path.exists()) {
47     parseCache();
48   } 
49   
50   // don't create dir here, repo might not exist at all
51 }
52   
53 SVNDirectory::SVNDirectory(SVNDirectory* pr, DAVCollection* col) :
54   dav(col),
55   repo(pr->repository()),
56   _doingUpdateReport(false),
57   _parent(pr)
58 {
59   assert(col->container());
60   assert(!col->url().empty());
61   assert(_parent);
62
63   localPath = pr->fsDir().file(col->name());
64   if (!localPath.exists()) {
65     Dir d(localPath);
66     d.create(0755);
67     writeCache();
68   } else {
69     parseCache();
70   }
71 }
72
73 SVNDirectory::~SVNDirectory()
74 {
75     // recursive delete our child directories
76     BOOST_FOREACH(SVNDirectory* d, _children) {
77         delete d;
78     }
79 }
80
81 void SVNDirectory::parseCache()
82 {
83   SGPath p(localPath);
84   p.append(DAV_CACHE_NAME);
85   if (!p.exists()) {
86     return;
87   }
88     
89   char href[1024];
90   char versionName[128];
91   LineState lineState = LINESTATE_HREF;
92   std::ifstream file(p.c_str());
93   bool doneSelf = false;
94     
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());
98     return;
99   }
100     
101     std::string vccUrl;
102     file.getline(href, 1024);
103     vccUrl = href;
104     
105   while (!file.eof()) {
106     if (lineState == LINESTATE_HREF) {
107       file.getline(href, 1024);
108       lineState = LINESTATE_VERSIONNAME;
109     } else {
110       assert(lineState == LINESTATE_VERSIONNAME);
111       file.getline(versionName, 1024);
112       lineState = LINESTATE_HREF;
113       char* hrefPtr = href;
114     
115       if (!doneSelf) {
116         if (!dav) {
117           dav = new DAVCollection(hrefPtr);
118           dav->setVersionName(versionName);
119         } else {
120           assert(string(hrefPtr) == dav->url());
121         }
122         
123         if (!vccUrl.empty()) {
124             dav->setVersionControlledConfiguration(vccUrl);
125         }
126         
127         _cachedRevision = versionName;
128         doneSelf = true;
129       } else {
130         DAVResource* child = addChildDirectory(hrefPtr)->collection();
131         child->setVersionName(versionName);
132       }
133     } // of line-state switching 
134   } // of file get-line loop
135 }
136   
137 void SVNDirectory::writeCache()
138 {
139   SGPath p(localPath);
140   if (!p.exists()) {
141       Dir d(localPath);
142       d.create(0755);
143   }
144   
145   p.append(DAV_CACHE_NAME);
146     
147   std::ofstream file(p.c_str(), std::ios::trunc);
148 // first, cache file version header
149   file << CACHE_VERSION_4_TOKEN << '\n';
150  
151 // second, the repository VCC url
152   file << dav->versionControlledConfiguration() << '\n';
153       
154 // third, our own URL, and version
155   file << dav->url() << '\n' << _cachedRevision << '\n';
156   
157   BOOST_FOREACH(DAVResource* child, dav->contents()) {
158     if (child->isCollection()) {
159         file << child->name() << '\n' << child->versionName() << "\n";
160     }
161   } // of child iteration
162 }
163
164 void SVNDirectory::setBaseUrl(const string& url)
165 {
166     if (_parent) {
167         SG_LOG(SG_IO, SG_ALERT, "setting base URL on non-root directory " << url);
168         return;
169     }
170     
171     if (dav && (url == dav->url())) {
172         return;
173     }
174     
175     dav = new DAVCollection(url);
176 }
177
178 std::string SVNDirectory::url() const
179 {
180   if (!_parent) {
181     return repo->baseUrl();
182   }
183   
184   return _parent->url() + "/" + name();
185 }
186
187 std::string SVNDirectory::name() const
188 {
189     return dav->name();
190
191
192 DAVResource*
193 SVNDirectory::addChildFile(const std::string& fileName)
194 {
195     DAVResource* child = NULL;
196     child = new DAVResource(dav->urlForChildWithName(fileName));
197     dav->addChild(child);
198   
199     writeCache();
200     return child;
201 }
202
203 SVNDirectory*
204 SVNDirectory::addChildDirectory(const std::string& dirName)
205 {
206     if (dav->childWithName(dirName)) {
207         // existing child, let's remove it
208         deleteChildByName(dirName);
209     }
210     
211     DAVCollection* childCol = dav->createChildCollection(dirName);
212     SVNDirectory* child = new SVNDirectory(this, childCol);
213     _children.push_back(child);
214     writeCache();
215     return child;
216 }
217
218 void SVNDirectory::deleteChildByName(const std::string& nm)
219 {
220     DAVResource* child = dav->childWithName(nm);
221     if (!child) {
222 //        std::cerr << "ZZZ: deleteChildByName: unknown:" << nm << std::endl;
223         return;
224     }
225
226     SGPath path = fsDir().file(nm);
227     dav->removeChild(child);
228     delete child;
229     
230     if (child->isCollection()) {
231         Dir d(path);
232         d.remove(true);
233     
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;
238             delete c;
239             _children.erase(it);
240         }
241     } else {
242         path.remove();
243     }
244     
245     writeCache();
246 }
247   
248 void SVNDirectory::requestFailed(HTTP::Request *req)
249 {
250     SG_LOG(SG_IO, SG_WARN, "Request failed for:" << req->url());
251 }
252   
253 bool SVNDirectory::isDoingSync() const
254 {
255   if (_doingUpdateReport) {
256       return true;
257   } 
258
259   BOOST_FOREACH(SVNDirectory* child, _children) {
260       if (child->isDoingSync()) {
261           return true;
262       } // of children
263   }
264     
265   return false;
266 }
267
268 void SVNDirectory::beginUpdateReport()
269 {
270     _doingUpdateReport = true;
271     _cachedRevision.clear();
272     writeCache();
273 }
274
275 void SVNDirectory::updateReportComplete()
276 {
277     _cachedRevision = dav->versionName();
278     _doingUpdateReport = false;
279     writeCache();
280     
281     SVNDirectory* pr = parent();
282     if (pr) {
283         pr->writeCache();
284     }    
285 }
286
287 SVNRepository* SVNDirectory::repository() const
288 {
289     return repo;
290 }
291
292 void SVNDirectory::mergeUpdateReportDetails(unsigned int depth, 
293     string_list& items)
294 {
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());
301         return;
302     }
303     
304     Dir d(localPath);
305     if (depth >= MAX_UPDATE_REPORT_DEPTH) {
306         std::cerr << localPath << "exceeded MAX_UPDATE_REPORT_DEPTH, cleaning" << std::endl;
307         d.removeChildren();
308         return;
309     }
310     
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());
314         if (!c) {
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;
318         } else {
319             // recurse down into children
320             c->mergeUpdateReportDetails(depth+1, items);
321         }
322     } // of child dir iteration
323 }
324
325 std::string SVNDirectory::repoPath() const
326 {
327     if (!_parent) {
328         return "/";
329     }
330     
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);
335 }
336
337 SVNDirectory* SVNDirectory::parent() const
338 {
339     return _parent;
340 }
341
342 SVNDirectory* SVNDirectory::child(const std::string& dirName) const
343 {
344     BOOST_FOREACH(SVNDirectory* d, _children) {
345         if (d->name() == dirName) {
346             return d;
347         }
348     }
349     
350     return NULL;
351 }
352
353 DirectoryList::iterator
354 SVNDirectory::findChildDir(const std::string& dirName)
355 {
356     DirectoryList::iterator it;
357     for (it=_children.begin(); it != _children.end(); ++it) {
358         if ((*it)->name() == dirName) {
359             return it;
360         }
361     }
362     return it;
363 }
364
365 simgear::Dir SVNDirectory::fsDir() const
366 {
367     return Dir(localPath);
368 }