Added "known issues" section to main www doc page
[barry/pauldeden.git] / gui / src / tarfile.cc
blobab1b43a90c7b9e23a3ec7285d2ba52b05605c93a
1 ///
2 /// \file tarfile.cc
3 /// API for reading and writing sequentially from compressed
4 /// tar files.
6 /*
7 Copyright (C) 2007-2008, Chris Frey <cdfrey@foursquare.net>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "tarfile.h"
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <string.h>
28 namespace reuse {
30 TarFile::TarFile(const char *filename,
31 bool create,
32 tartype_t *compress_ops,
33 bool always_throw)
34 : m_tar(0),
35 m_throw(always_throw),
36 m_writemode(create)
38 // figure out how to handle the file flags/modes
39 int flags = 0;
40 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
42 if( m_writemode ) {
43 flags = O_WRONLY | O_CREAT | O_EXCL;
45 else {
46 flags = O_RDONLY;
49 // open... throw on error, as we are in the constructor
50 if( tar_open(&m_tar, const_cast<char*>(filename),
51 compress_ops, flags, mode, TAR_VERBOSE | TAR_GNU) == -1 ) {
52 throw TarError(std::string("Unable to open tar file: ") + strerror(errno));
56 TarFile::~TarFile()
58 try {
59 Close();
60 } catch( TarError &te ) {}
63 bool TarFile::False(const char *msg)
65 m_last_error = msg;
66 if( m_throw )
67 throw TarError(msg);
68 else
69 return false;
72 bool TarFile::False(const std::string &msg, int err)
74 std::string str = msg;
75 str += ": ";
76 str += strerror(err);
77 return False(str);
80 bool TarFile::Close()
82 if( m_tar ) {
83 if( m_writemode ) {
84 if( tar_append_eof(m_tar) != 0 )
85 return False("Unable to write eof", errno);
88 if( tar_close(m_tar) != 0 ) {
89 return False("Unable to close file", errno);
91 m_tar = 0;
93 return true;
96 /// Appends a new file to the current tarfile, using tarpath as
97 /// its internal filename, and data as the complete file contents.
98 /// Uses current date and time as file mtime.
99 bool TarFile::AppendFile(const char *tarpath, const std::string &data)
101 // write standard file header
102 th_set_type(m_tar, REGTYPE);
103 th_set_mode(m_tar, 0644);
104 th_set_path(m_tar, const_cast<char*>(tarpath));
105 th_set_user(m_tar, 0);
106 th_set_group(m_tar, 0);
107 th_set_size(m_tar, data.size());
108 th_set_mtime(m_tar, time(NULL));
109 if( th_write(m_tar) != 0 ) {
110 return False("Unable to write tar header", errno);
113 // write the data in blocks until finished
114 char block[T_BLOCKSIZE];
115 for( size_t pos = 0; pos < data.size(); pos += T_BLOCKSIZE ) {
116 memset(block, 0, T_BLOCKSIZE);
118 size_t size = T_BLOCKSIZE;
119 if( data.size() - pos < T_BLOCKSIZE )
120 size = data.size() - pos;
122 memcpy(block, data.data() + pos, size);
124 if( tar_block_write(m_tar, block) != T_BLOCKSIZE ) {
125 return False("Unable to write block", errno);
129 return true;
132 /// Reads next available file into data, filling tarpath with
133 /// internal filename from tarball.
134 bool TarFile::ReadNextFile(std::string &tarpath, std::string &data)
136 // start fresh
137 tarpath.clear();
138 data.clear();
140 // read next tar file header
141 if( th_read(m_tar) != 0 ) {
142 // this is not necessarily an error, as it could just
143 // be the end of file, so a simple false is good here,
144 // don't throw an exception
145 m_last_error = "";
146 return false;
149 // write standard file header
150 if( !TH_ISREG(m_tar) ) {
151 return False("Only regular files are supported inside a tarball.");
154 tarpath = th_get_pathname(m_tar);
155 size_t size = th_get_size(m_tar);
157 // read the data in blocks until finished
158 char block[T_BLOCKSIZE];
159 for( size_t pos = 0; pos < size; pos += T_BLOCKSIZE ) {
160 memset(block, 0, T_BLOCKSIZE);
162 size_t readsize = T_BLOCKSIZE;
163 if( size - pos < T_BLOCKSIZE )
164 readsize = size - pos;
166 if( tar_block_read(m_tar, block) != T_BLOCKSIZE ) {
167 return False("Unable to read block", errno);
170 data.append(block, readsize);
173 return true;
176 /// Read next available filename, skipping the data if it is
177 /// a regular file
178 bool TarFile::ReadNextFilenameOnly(std::string &tarpath)
180 // start fresh
181 tarpath.clear();
183 // read next tar file header
184 if( th_read(m_tar) != 0 ) {
185 // this is not necessarily an error, as it could just
186 // be the end of file, so a simple false is good here,
187 // don't throw an exception
188 m_last_error = "";
189 return false;
192 // write standard file header
193 if( !TH_ISREG(m_tar) ) {
194 return False("Only regular files are supported inside a tarball.");
197 tarpath = th_get_pathname(m_tar);
199 if( tar_skip_regfile(m_tar) != 0 ) {
200 return False("Unable to skip tar file", errno);
203 return true;
207 } // namespace reuse
210 #ifdef __TEST_MODE__
212 #include <iostream>
213 #include <unistd.h>
215 using namespace std;
217 int main()
219 try {
220 cout << "Writing test file..." << endl;
221 reuse::TarFile output("tartest.tar.gz", true, true, true);
222 std::string data;
223 for( int i = 0; i < 60; i++ ) {
224 data.append("0123456789", 10);
227 output.AppendFile("path1/test1.txt", data);
228 output.AppendFile("path2/test2.txt", data);
229 output.Close();
232 cout << "Reading test file..." << endl;
233 reuse::TarFile input("tartest.tar.gz", false, true, true);
234 std::string path, incoming;
236 while( input.ReadNextFile(path, incoming) ) {
237 cout << "Read: " << path
238 << " Data: "
239 << (( data == incoming ) ? "equal" : "different")
240 << endl;
243 input.Close();
245 unlink("tartest.tar.gz");
247 } catch( reuse::TarFile::TarError &te ) {
248 cerr << te.what() << endl;
249 return 1;
253 #endif