/implementation/untar.cpp

https://gitlab.com/jbandela/ccpm · C++ · 183 lines · 143 code · 35 blank · 5 comment · 26 complexity · 6931fb64ebd1908e32ee837c7da28175 MD5 · raw file

  1. #include <iostream>
  2. #include <boost/filesystem.hpp>
  3. #include <boost/filesystem/fstream.hpp>
  4. #include <array>
  5. #include <algorithm>
  6. #include <exception>
  7. #include <vector>
  8. #include <boost/algorithm/string.hpp>
  9. #include <boost/iostreams/filtering_stream.hpp>
  10. #include <boost/iostreams/filter/gzip.hpp>
  11. // See http://stackoverflow.com/questions/2505042/how-to-parse-a-tar-file-in-c for details
  12. // Each header is 512 bytes
  13. typedef std::array< char, 512> header_t;
  14. enum class entry_type{
  15. file, directory
  16. };
  17. namespace detail{
  18. std::string get_magic(const header_t& h){
  19. auto start = h.data() + 257;
  20. std::string ret { start, std::find(start,start + 6, 0) };
  21. boost::algorithm::trim(ret);
  22. return ret;
  23. }
  24. std::string get_filename(const header_t& h){
  25. std::string ret{ h.data(), std::find(h.data(),h.data() + 100,0) };
  26. if (get_magic(h) == "ustar"){
  27. auto start = h.data() + 345;
  28. std::string prefix{ start, std::find(start, start + 155, 0) };
  29. if (prefix.size()){
  30. ret = prefix + "/" + ret;
  31. }
  32. }
  33. return ret;
  34. }
  35. entry_type get_type(const header_t& h){
  36. auto t = h[156];
  37. if (t == 0 || t == '0') return entry_type::file;
  38. if (t == '5') return entry_type::directory;
  39. // Only support files and directories
  40. throw std::runtime_error("Invalid entry type");
  41. }
  42. // From above stack overflow
  43. int octal_string_to_int(const char *current_char, unsigned int size){
  44. std::size_t output = 0;
  45. while (size > 0){
  46. output = output * 8 + *current_char - '0';
  47. current_char++;
  48. size--;
  49. }
  50. return output;
  51. }
  52. std::size_t get_filesize(const header_t& h) {
  53. return octal_string_to_int(&h[124], 11); }
  54. // elsewhere
  55. }
  56. struct tar_archive{
  57. std::istream& fs_;
  58. header_t h_;
  59. bool fdata_read_ = false;
  60. std::vector<char> vec_;
  61. boost::filesystem::path base_path_;
  62. void read_header(){
  63. fs_.read(h_.data(), h_.size());
  64. fdata_read_ = false;
  65. std::string magic;
  66. if ((magic = detail::get_magic(h_)) != "ustar" && magic.size()){
  67. throw std::runtime_error("Only the ustar format is supported, unsupported format");
  68. }
  69. }
  70. tar_archive(std::istream& fs) :fs_{ fs}{
  71. read_header();
  72. }
  73. boost::filesystem::path path(){
  74. return detail::get_filename(h_);
  75. }
  76. entry_type type(){
  77. return detail::get_type(h_);
  78. }
  79. std::size_t file_size(){
  80. return detail::get_filesize(h_);
  81. }
  82. const std::vector<char>& file_data(){
  83. if (fdata_read_) return vec_;
  84. vec_.clear();
  85. fdata_read_ = true;
  86. auto sz = file_size();
  87. header_t buf;
  88. std::size_t bytes_read = 0;
  89. while (sz){
  90. fs_.read(buf.data(), buf.size());
  91. vec_.insert(vec_.end(), buf.data(), buf.data() + std::min(sz, buf.size()));
  92. sz -= std::min(buf.size(), sz);
  93. bytes_read += 512;
  94. }
  95. return vec_;
  96. }
  97. void extract(const boost::filesystem::path& p){
  98. if (type() == entry_type::directory){
  99. boost::filesystem::create_directories(p / path());
  100. }
  101. else{
  102. boost::filesystem::create_directories(p / path().parent_path());
  103. boost::filesystem::ofstream ofs{ p / path() };
  104. if (file_data().size()){
  105. ofs.write(&file_data()[0], file_data().size());
  106. }
  107. ofs.close();
  108. }
  109. }
  110. bool next(){
  111. if (!fdata_read_)file_data();
  112. read_header();
  113. if (detail::get_filename(h_).empty()){
  114. return false;
  115. }
  116. else{
  117. return true;
  118. }
  119. }
  120. };
  121. void extract_all(const boost::filesystem::path& archive_path, const boost::filesystem::path& p){
  122. boost::filesystem::ifstream ifs;
  123. ifs.exceptions(std::ios::failbit);
  124. ifs.open( archive_path,std::ios::binary );
  125. boost::iostreams::filtering_istream fifs;
  126. auto ext = boost::algorithm::to_lower_copy(archive_path.extension().string());
  127. if (ext == ".gz" || ext == ".tgz"){
  128. fifs.push(boost::iostreams::gzip_decompressor{});
  129. }
  130. fifs.push(ifs);
  131. tar_archive ar{ fifs };
  132. do{
  133. ar.extract(p);
  134. } while (ar.next());
  135. }
  136. int main(int argc, char** argv){
  137. try{
  138. auto cp = boost::filesystem::current_path();
  139. if (argc < 2){
  140. std::cout << "Enter tar file\n";
  141. return 0;
  142. }
  143. extract_all(argv[1], cp);
  144. }
  145. catch (std::exception& e){
  146. std::cerr << "Error " << e.what();
  147. }
  148. }