Test to see if auto_props and label_mapper files are readable.
[vss2svn.git] / ssphys / SSPhys / GetCommand.cpp
blob171c1e021bea0eaa4a03c2a51607675ba35c2ce0
1 // GetCommand.cpp: implementation of the GetCommand class.
2 //
3 //////////////////////////////////////////////////////////////////////
5 #include "StdAfx.h"
6 #include "GetCommand.h"
7 #include <SSPhysLib/SSFiles.h>
8 #include <SSPhysLib/SSItemInfoObject.h>
9 #include <SSPhysLib/SSVersionObject.h>
10 #include <SSPhysLib/SSProjectObject.h>
11 #include <boost/integer/static_min_max.hpp>
12 #include <boost/filesystem/operations.hpp>
13 #include <boost/filesystem/convenience.hpp> // create_directories
14 #include <boost/filesystem/exception.hpp>
15 #include <fcntl.h>
16 #include <fstream>
17 #include <strstream>
18 #include <sys/stat.h>
19 #include <errno.h>
20 //using namespace boost::filesystem;
21 namespace fs = boost::filesystem;
23 //////////////////////////////////////////////////////////////////////
24 // Construction/Destruction
25 //////////////////////////////////////////////////////////////////////
27 CGetCommand::CGetCommand ()
28 : CCommand ("get", "Retrieve old versions from a VSS physical file"),
29 m_Version (-1),
30 m_bForceOverwrite (false),
31 m_bBulkGet (false)
35 po::options_description CGetCommand::GetOptionsDescription () const
37 po::options_description descr (CCommand::GetOptionsDescription());
38 descr.add_options ()
39 ("version,v", po::value<int>(), "Get a specific old version")
40 ("force-overwrite", "overwrite target file")
41 ("bulk,b", "bulk operation: get all intermediate files with the same name as the source name")
42 ("projects,p", "Experimental: also try to rebuild project files")
43 ("input", po::value<std::string>(), "input physical file name")
44 ("output", po::value<std::string>(), "target file name.\n"
45 "\n"
46 "You can also specify an oputput directory for the output target."
47 "In that case the name of the input file will be used "
48 "as the output file name. With this option, you can easily "
49 "build a shadow directory of your data, e.g. with the following command\n"
50 "\n"
51 " find data -name ???????? | xargs -n 1 ssphys get -b -v 1 -s 1 --output shadowdir/ \n"
52 "\n"
53 "If you specify a relative or absolute path to the physical file, all non "
54 "directory elements will be appended to the output directory, e.g.\n"
55 "\n"
56 " ssphys get data/b/baaaaaaa shadow/ \n"
57 "\n"
58 "will output all files to \"shadow/data/b/baaaaaaa\". You can control the number "
59 "of directories appended with the --strip option" )
60 ("strip", po::value<int> (), "Strip the smallest prefix containing num leading slashes from the input path "
61 "A sequence of one or more adjacent slashes is counted as a single slash, e.g\n"
62 "\n"
63 "/path/to/soursafe/archive/data/a/abaaaaaa\n"
64 "\n"
65 "setting --strip 0 gives the entire file name unmodified, --strip 1 gives\n"
66 "\n"
67 "path/to/soursafe/archive/data/a/abaaaaaa\n"
68 "\n"
69 "without the leading slash, --strip 6 gives\n"
70 "\n"
71 "a/abaaaaaa\n"
72 "\n"
73 "and not specifying --strip at all just gives you abaaaaaa.")
75 return descr;
78 po::options_description CGetCommand::GetHiddenDescription () const
80 po::options_description descr (CCommand::GetHiddenDescription());
81 return descr;
84 po::positional_options_description CGetCommand::GetPositionalOptionsDescription () const
86 po::positional_options_description positional (CCommand::GetPositionalOptionsDescription());
87 positional.add ("input", 1);
88 positional.add ("output", 2);
89 return positional;
92 class CActionVisitor : public ISSActionVisitor
94 public:
95 virtual ~CActionVisitor () {}
97 virtual bool Apply (const SSLabeledAction& rAction) { return Apply ((const SSAction&) rAction); }
98 // virtual bool Apply (const SSSingleFileAction& rAction) { return Apply ((const SSAction&) rAction); }
99 virtual bool Apply (const SSCreatedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
100 virtual bool Apply (const SSCreatedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
101 virtual bool Apply (const SSAddedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
102 virtual bool Apply (const SSAddedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
103 virtual bool Apply (const SSDeletedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
104 virtual bool Apply (const SSDeletedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
105 virtual bool Apply (const SSRecoveredProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
106 virtual bool Apply (const SSRecoveredFileAction& rAction) { return Apply ((const SSAction&) rAction); }
107 virtual bool Apply (const SSBranchFileAction& rAction) { return Apply ((const SSAction&) rAction); }
108 virtual bool Apply (const SSRollbackAction& rAction) { return Apply ((const SSAction&) rAction); }
109 virtual bool Apply (const SSArchivedVersionsAction& rAction) { return Apply ((const SSAction&) rAction); }
110 virtual bool Apply (const SSArchiveFileAction& rAction) { return Apply ((const SSAction&) rAction); }
111 virtual bool Apply (const SSArchiveProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
112 virtual bool Apply (const SSRestoreFileAction& rAction) { return Apply ((const SSAction&) rAction); }
113 virtual bool Apply (const SSRestoreProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
115 virtual bool Apply (const SSDestroyedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
116 virtual bool Apply (const SSDestroyedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
117 virtual bool Apply (const SSRenamedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
118 virtual bool Apply (const SSRenamedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
119 virtual bool Apply (const SSCheckedInAction& rAction) { return Apply ((const SSAction&) rAction); }
120 virtual bool Apply (const SSSharedAction& rAction) { return Apply ((const SSAction&) rAction); }
122 virtual bool Apply (const SSMovedProjectAction& rAction) { return Apply ((const SSAction&) rAction); }
124 protected:
125 virtual bool Apply (const SSAction& rAction)
127 return true;
131 class CAutoFile
133 public:
134 CAutoFile (std::string name, bool bDelete = true)
135 : m_FileName (name),
136 m_bDeleteFile (bDelete)
140 CAutoFile (CAutoFile const & r)
141 : m_bDeleteFile (r.m_bDeleteFile),
142 m_FileName (r.Detatch ())
146 CAutoFile& operator= (CAutoFile const & r)
148 if (this != &r)
150 if (m_FileName != r.GetPath())
152 if (m_bDeleteFile)
153 Unlink ();
154 m_bDeleteFile = r.m_bDeleteFile;
156 else if (r.m_bDeleteFile)
157 m_bDeleteFile = r.m_bDeleteFile;
158 m_FileName = r.Detatch();
160 return (*this);
163 ~CAutoFile()
165 if (m_bDeleteFile)
166 Unlink ();
169 std::string GetPath () const { return m_FileName; }
171 std::string Detatch() const;
172 int Unlink();
174 protected:
176 std::string m_FileName;
177 mutable bool m_bDeleteFile;
180 std::string CAutoFile::Detatch() const
182 m_bDeleteFile = false;
183 return m_FileName;
186 int CAutoFile::Unlink()
188 if ( m_bDeleteFile )
190 m_bDeleteFile = false;
191 return( unlink( m_FileName.c_str () ) );
193 else
195 return( 0 );
201 class CReverseDelta
203 public:
204 CReverseDelta (const char* buffer, size_t length)
205 : m_pBuffer (buffer), m_length (length)
209 bool operator () (std::istream& input, std::ostream& output) const
211 for (size_t i = 0; i < m_length; )
213 const FD* pfd = (const FD*) (m_pBuffer+i);
214 i += sizeof(FD);
215 // printf ("fd: %d, start %d, len %d\n", pfd->command, pfd->start, pfd->end);
217 switch (pfd->command)
219 case 2:
220 // assert finito
221 break;
222 case 1:
224 char b[256];
225 long size = pfd->end;
226 input.seekg(pfd->start);
227 if (input.fail ())
228 throw SSException ("reverse delta: invalid seek beyond file size");
230 while (size > 0 && !input.fail () && !output.fail ())
232 long s = std::min (size, (long) sizeof (b));
233 input.read (b, s);
234 output.write (b, s);
236 // how many bytes did we really read?
237 s = input.gcount ();
238 size -= s;
241 if (input.fail ())
242 throw SSException ("reverse delta: failed to read necessary amount of data from input file");
243 if (output.fail ())
244 throw SSException ("reverse delta: failed to write necessary amount of data to the output file");
246 break;
247 case 0:
249 long s = std::min (pfd->end, (ulong)(m_length - i));
250 output.write (m_pBuffer+i, s);
252 if (s < pfd->end)
253 throw SSException ("reverse delta: invalid patch length in delta record");
255 i += s;
257 break;
258 default:
259 std::strstream msg;
260 msg << "unknown reverse delta command " << pfd->command;
261 throw SSException (msg.str());
262 break;
266 return true;
268 protected:
269 const char* m_pBuffer;
270 size_t m_length;
274 class CHistoryHandler : public CActionVisitor
276 public:
277 CHistoryHandler (std::string src)
278 : m_File (src, false)
282 std::string GetPath ()
284 return m_File.GetPath();
287 virtual void SaveAs (std::string name, bool overwrite = false) = 0;
289 protected:
290 CAutoFile m_File;
294 class CReverseHistoryHandler : public CHistoryHandler
296 public:
297 CReverseHistoryHandler (std::string src)
298 : CHistoryHandler (src)
302 virtual bool Apply (const SSCreatedFileAction& rAction)
304 // create an empty file
305 CAutoFile targetFile (tmpnam(NULL));
306 m_File = targetFile;
308 return true;
311 virtual bool Apply (const SSArchivedVersionsAction& rAction)
313 // end building the history at this point
314 return false;
317 virtual bool Apply (const SSLabeledAction& rAction)
319 // nothing to do for a file labeling operation
320 return true;
323 virtual bool Apply (const SSRollbackAction& rAction)
325 // nothing special to do for a rollback operation
326 // The rollback action just marks the begin of this file
327 return true;
330 virtual bool Apply (const SSCheckedInAction& rAction)
332 SSRecordPtr pRecord = rAction.GetFileDelta();
333 if (!pRecord)
335 throw SSException ("no file delta record found for check-in action (probably item did not retained old versions of itself)");
338 CAutoFile targetFile (tmpnam(NULL));
340 std::ifstream input (m_File.GetPath().c_str(), std::ios::in|std::ios::binary);
341 if (!input.is_open())
342 return false;
343 std::ofstream output (targetFile.GetPath().c_str(), std::ios::out|std::ios::binary);
344 if (!output.is_open())
345 return false;
347 CReverseDelta revDelta ((const char*)pRecord->GetBuffer(), pRecord->GetLen());
348 bool ret = revDelta (input, output);
350 input.close();
351 output.close ();
353 if (ret)
355 m_File = targetFile;
357 return ret;
360 virtual void SaveAs (std::string name, bool overwrite = false)
362 fs::path fpath (name);
363 if (overwrite && fs::exists (fpath))
364 fs::remove (fpath);
365 else if (!fs::exists (fpath.branch_path ()))
366 fs::create_directories(fpath.branch_path ());
368 fs::copy_file (GetPath (), fpath);
371 virtual bool Apply (const SSAction& rAction)
373 throw SSException (std::string ("unsuported action in CReverseHistoryHandler: ").append (CAction::ActionToString(rAction.GetActionID())));
378 class CProjectHistoryHandler : public CHistoryHandler
380 public:
381 CProjectHistoryHandler (std::string src)
382 : CHistoryHandler (src)
384 SSProjectFile projectFile (GetPath ());
385 BuildList (projectFile);
388 virtual void SaveAs (std::string name, bool overwrite = false)
390 boost::throw_exception (std::logic_error ("get not yet implemented for project status files"));
393 virtual bool Apply (const SSLabeledAction& rAction) { /* nothing to do */ return true; }
394 //// virtual bool Apply (const SSSingleFileAction& rAction) { return Apply ((const SSAction&) rAction); }
395 virtual bool Apply (const SSCreatedProjectAction& rAction) { /* nothing to do */ return true; }
396 virtual bool Apply (const SSCreatedFileAction& rAction) { /* nothing to do */ return true; }
397 virtual bool Apply (const SSAddedProjectAction& rAction);
398 virtual bool Apply (const SSAddedFileAction& rAction);
399 virtual bool Apply (const SSDeletedProjectAction& rAction);
400 virtual bool Apply (const SSDeletedFileAction& rAction);
401 virtual bool Apply (const SSRecoveredProjectAction& rAction);
402 virtual bool Apply (const SSRecoveredFileAction& rAction);
404 virtual bool Apply (const SSDestroyedProjectAction& rAction);
405 virtual bool Apply (const SSDestroyedFileAction& rAction);
406 virtual bool Apply (const SSRenamedProjectAction& rAction);
407 virtual bool Apply (const SSRenamedFileAction& rAction);
408 // virtual bool Apply (const SSCheckedInAction& rAction) { return Apply ((const SSAction&) rAction); }
409 // TODO:
410 // virtual bool Apply (const SSSharedFileAction& rAction) { return Apply ((const SSAction&) rAction); }
411 // virtual bool Apply (const SSMovedProjectAction& rAction);
413 protected:
414 virtual bool Apply (const SSAction& rAction)
416 throw SSException ("unsuported action in CProjectHistoryHandler");
419 typedef std::vector<SSProjectObject>::iterator iterator;
420 std::vector<SSProjectObject> m_Items;
422 void BuildList (SSProjectFile& rFile);
423 iterator FindItem (std::string physFile);
424 iterator InsertItem (SSProjectObject object);
429 void CProjectHistoryHandler::BuildList (SSProjectFile& rFile)
431 // iterate all records and add them to the collection
432 SSRecordPtr recordPtr = rFile.GetFirstRecord ();
433 while (recordPtr)
435 if (recordPtr->GetType() == eProjectEntry)
437 SSProjectObject projectObject (recordPtr);
438 m_Items.push_back (projectObject);
441 recordPtr = rFile.GetNextRecord (recordPtr);
445 CProjectHistoryHandler::iterator CProjectHistoryHandler::FindItem (std::string physFile)
447 iterator itor;
448 iterator end = m_Items.end();
449 iterator found = end;
451 for (itor = m_Items.begin(); itor != end; ++itor)
453 SSProjectObject& po = *itor;
454 // std::cout << itemPtr->GetPhysical () << std::endl;
455 if (physFile == po.GetPhysFile())
457 if (found != end)
458 throw SSException ("duplicate entry");
460 found = itor;
464 return found;
467 CProjectHistoryHandler::iterator CProjectHistoryHandler::InsertItem (SSProjectObject object)
469 m_Items.push_back (object);
470 iterator last = m_Items.end();
471 return --last;
474 bool CProjectHistoryHandler::Apply (const SSAddedFileAction& rAction)
476 iterator itor = FindItem (rAction.GetPhysical());
477 if (itor != m_Items.end())
478 m_Items.erase(itor);
479 else
480 throw SSException ("item not found");
482 return true;
485 bool CProjectHistoryHandler::Apply (const SSAddedProjectAction& rAction)
487 iterator itor = FindItem (rAction.GetPhysical());
488 if (itor != m_Items.end())
489 m_Items.erase(itor);
490 else
491 throw SSException ("item not found");
493 return true;
496 bool CProjectHistoryHandler::Apply (const SSDeletedFileAction& rAction)
498 iterator itor = FindItem (rAction.GetPhysical());
499 if (itor != m_Items.end ())
501 SSProjectObject& po = *itor;
502 po.Recover ();
504 else
505 throw SSException ("item not found");
507 return true;
509 bool CProjectHistoryHandler::Apply (const SSDeletedProjectAction& rAction)
511 iterator itor = FindItem (rAction.GetPhysical());
512 if (itor != m_Items.end ())
514 SSProjectObject& po = *itor;
515 po.Recover ();
517 else
518 throw SSException ("item not found");
520 return true;
522 bool CProjectHistoryHandler::Apply (const SSRecoveredFileAction& rAction)
524 iterator itor = FindItem (rAction.GetPhysical());
525 if (itor != m_Items.end ())
527 SSProjectObject& po = *itor;
528 po.Delete ();
530 else
531 throw SSException ("item not found");
533 return true;
535 bool CProjectHistoryHandler::Apply (const SSRecoveredProjectAction& rAction)
537 iterator itor = FindItem (rAction.GetPhysical());
538 if (itor != m_Items.end ())
540 SSProjectObject& po = *itor;
541 po.Delete ();
543 else
544 throw SSException ("item not found");
546 return true;
549 bool CProjectHistoryHandler::Apply (const SSDestroyedProjectAction& rAction)
551 if (FindItem (rAction.GetPhysical ()) != m_Items.end ())
552 throw SSException ("adding already existing item");
554 PROJECT_ENTRY pe;
555 // pe.flags = ??;
556 pe.name = rAction.GetSSName ();
557 strncpy (pe.phys, rAction.GetPhysical().c_str(), 8);
558 pe.phys[8] = '\0';
559 pe.pinnedToVersion = 0;
560 // pe.type = ??
561 SSProjectObject pr (pe);
562 InsertItem (pr);
564 return true;
567 bool CProjectHistoryHandler::Apply (const SSDestroyedFileAction& rAction)
569 // durch map wahrscheinlich besser zu lösen
570 if (FindItem (rAction.GetPhysical()) != m_Items.end ())
571 throw SSException ("adding already existing item");
573 PROJECT_ENTRY pe;
574 // pe.flags = ??;
575 pe.name = rAction.GetSSName ();
576 strncpy (pe.phys, rAction.GetPhysical().c_str(), 8);
577 pe.phys[8] = '\0';
578 pe.pinnedToVersion = 0;
579 // pe.type = ??
580 SSProjectObject pr (pe);
581 InsertItem (pr);
583 return true;
586 bool CProjectHistoryHandler::Apply (const SSRenamedProjectAction& rAction)
588 iterator itor = FindItem (rAction.GetPhysical());
589 if (itor == m_Items.end ())
590 throw SSException ("item not found");
592 SSProjectObject& po = *itor;
593 po.Rename (rAction.GetNewSSName (), rAction.GetSSName());
594 return true;
597 bool CProjectHistoryHandler::Apply (const SSRenamedFileAction& rAction)
599 iterator itor = FindItem (rAction.GetPhysical());
600 if (itor == m_Items.end ())
601 throw SSException ("item not found");
603 SSProjectObject& po = *itor;
604 po.Rename (rAction.GetNewSSName (), rAction.GetSSName());
605 return true;
613 void CGetCommand::Execute (po::variables_map const & options, std::vector<po::option> const & args)
615 std::string physFile;
616 std::string destFile;
617 fs::path physPath;
618 fs::path destPath;
620 fs::path::default_name_check (fs::native);
622 if (options.count("version"))
623 m_Version = options["version"].as<int>();
624 if (options.count("force-overwrite"))
625 m_bForceOverwrite = true;
626 if (options.count("bulk"))
627 m_bBulkGet = true;
629 if (options.count("input"))
630 physPath = physFile = options["input"].as<std::string>();
631 if (options.count("output"))
632 destPath = destFile = options["output"].as<std::string> ();
636 if (physFile.empty ())
637 throw SSException ("please specify a source file for the get operation");
639 if (destFile.empty ())
640 throw SSException ("please specify a destination file for the get operation");
642 if (fs::exists (destPath) && fs::is_directory(destPath) || *destFile.rbegin() == '\\' || *destFile.rbegin() == '/')
644 fs::path::iterator itor = physPath.begin ();
645 int strip = options.count ("strip") ? options["strip"].as<int> () : 0;
647 while (strip > 0 && itor != physPath.end ())
649 ++itor;
650 --strip;
653 fs::path relPath;
654 if (itor == physPath.end ())
655 relPath = physPath.leaf ();
656 else
658 while (itor != physPath.end ())
660 relPath /= *itor;
661 ++itor;
665 destPath = destFile / relPath;
668 if (fs::exists (destPath) && !m_bForceOverwrite)
669 throw SSException ("destination file exists. Please use overwrite flag");
672 SSHistoryFile file(physPath.string ());
673 std::auto_ptr<SSItemInfoObject> pItem (file.GetItemInfo());
674 if (!pItem.get ())
675 throw SSException ("no information object found");
677 std::auto_ptr<CHistoryHandler> pVisitor;
678 std::string lastDataFileName = pItem->GetDataFileName ();
680 if (pItem->GetType() == SSITEM_FILE)
681 pVisitor = std::auto_ptr<CHistoryHandler> (new CReverseHistoryHandler (lastDataFileName));
682 else if (options.count("projects"))
683 pVisitor = std::auto_ptr<CHistoryHandler> (new CProjectHistoryHandler (lastDataFileName));
684 else
685 return;
687 if (m_Version < 0)
688 m_Version = pItem->GetNumberOfActions () - m_Version + 1;
690 SSFileItem* pFileItem = dynamic_cast<SSFileItem*> (pItem.get());
692 // This check isn't necessary, since the get code will fail, if not delta information is available
693 // The code was only here to give a better error message. But in case, that the flag was added after
694 // the last commit, it is possible, that parts of the file are still recoverable. Therefore I disabled
695 // this code
697 if (pFileItem && pFileItem->GetStoreOnlyLatestRev ())
699 if (m_Version < pItem->GetNumberOfActions ())
701 throw SSException ("item does not retain old versions of itself");
706 // we need to initialize the tmpnam once. The first file generated by tmpnam has a trailing dot, but no
707 // extension, e.g "sesc.". And this is an invalid name for the boost::filesystem library
708 CAutoFile tmpfile (tmpnam(NULL));
711 SSVersionObject version (pItem->GetHistoryLast ());
712 while (version && version.GetVersionNumber() > m_Version)
714 if (version.GetAction())
716 if (m_bBulkGet)
718 std::string bulkFile (destPath.string () + "." + boost::lexical_cast<std::string>(version.GetVersionNumber ()));
720 pVisitor->SaveAs (bulkFile, m_bForceOverwrite);
721 // throw SSException ("failed to create target file " + bulkFile);
724 if (!version.GetAction ()->Accept (*pVisitor.get()))
725 break;
728 version = version.GetPreviousObject ();
731 if (m_bBulkGet)
732 destPath = destPath.string () + "." + boost::lexical_cast<std::string>(version ? version.GetVersionNumber () : 0);
734 pVisitor->SaveAs (destPath.string (), m_bForceOverwrite);
735 // throw SSException (std::string ("failed to create target file ").append (m_DestFile));