define update_subfile

This commit is contained in:
David Rose 2003-08-25 13:09:55 +00:00
parent f474d2a336
commit fbdc3e52c7
3 changed files with 181 additions and 45 deletions

View File

@ -30,6 +30,7 @@
bool create = false; // -c
bool append = false; // -r
bool update = false; // -u
bool list = false; // -t
bool extract = false; // -x
bool verbose = false; // -v
@ -49,7 +50,7 @@ string dont_compress_str = "jpg,mp3";
void
usage() {
cerr <<
"Usage: multify -[c|r|t|x] -f <multifile_name> [options] <subfile_name> ...\n";
"Usage: multify -[c|r|u|t|x] -f <multifile_name> [options] <subfile_name> ...\n";
}
void
@ -82,6 +83,12 @@ help() {
" the Multifile with the same name. The Multifile will be repacked\n"
" after completion, even if no Subfiles were added.\n\n"
" -u\n"
" Update an existing Multifile archive. This is similar to -r, except\n"
" that files are compared byte-for-byte with their corresponding files\n"
" in the archive first. If they have not changed, the multifile is not\n"
" modified (other than to repack it if necessary).\n\n"
" -t\n"
" List the contents of an existing Multifile. With -v, this shows\n"
" the size of each Subfile and its compression ratio, if compressed.\n\n"
@ -127,7 +134,6 @@ help() {
" -O\n"
" With -x, extract subfiles to standard output instead of to disk.\n\n"
" -Z <extension_list>\n"
" Specify a comma-separated list of filename extensions that represent\n"
" files that are not to be compressed. The default if this is omitted is\n"
@ -191,16 +197,23 @@ add_directory(Multifile &multifile, const Filename &directory_name) {
for (fi = files.begin(); fi != files.end(); ++fi) {
Filename subfile_name(directory_name, (*fi));
if (subfile_name.is_directory()) {
okflag = add_directory(multifile, subfile_name);
if (!add_directory(multifile, subfile_name)) {
okflag = false;
}
} else if (!subfile_name.exists()) {
cerr << "Not found: " << subfile_name << "\n";
okflag = false;
} else {
string new_subfile_name =
multifile.add_subfile(subfile_name, subfile_name,
get_compression_level(subfile_name));
string new_subfile_name;
if (update) {
new_subfile_name = multifile.update_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
} else {
new_subfile_name = multifile.add_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
}
if (new_subfile_name.empty()) {
cerr << "Unable to add " << subfile_name << ".\n";
okflag = false;
@ -218,7 +231,7 @@ add_directory(Multifile &multifile, const Filename &directory_name) {
bool
add_files(int argc, char *argv[]) {
Multifile multifile;
if (append) {
if (append || update) {
if (!multifile.open_read_write(multifile_name)) {
cerr << "Unable to open " << multifile_name << " for updating.\n";
return false;
@ -248,9 +261,14 @@ add_files(int argc, char *argv[]) {
okflag = false;
} else {
string new_subfile_name =
multifile.add_subfile(subfile_name, subfile_name,
get_compression_level(subfile_name));
string new_subfile_name;
if (update) {
new_subfile_name = multifile.update_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
} else {
new_subfile_name = multifile.add_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
}
if (new_subfile_name.empty()) {
cerr << "Unable to add " << subfile_name << ".\n";
okflag = false;
@ -406,7 +424,7 @@ main(int argc, char *argv[]) {
extern char *optarg;
extern int optind;
static const char *optflags = "crtxvz123456789Z:f:OC:F:h";
static const char *optflags = "crutxvz123456789Z:f:OC:F:h";
int flag = getopt(argc, argv, optflags);
Filename rel_path;
while (flag != EOF) {
@ -417,6 +435,9 @@ main(int argc, char *argv[]) {
case 'r':
append = true;
break;
case 'u':
update = true;
break;
case 't':
list = true;
break;
@ -512,8 +533,8 @@ main(int argc, char *argv[]) {
argv += (optind - 1);
// We should have exactly one of these options.
if ((create?1:0) + (append?1:0) + (list?1:0) + (extract?1:0) != 1) {
cerr << "Exactly one of -c, -r, -t, -x must be specified.\n";
if ((create?1:0) + (append?1:0) + (update?1:0) + (list?1:0) + (extract?1:0) != 1) {
cerr << "Exactly one of -c, -r, -u, -t, -x must be specified.\n";
usage();
return 1;
}
@ -528,7 +549,7 @@ main(int argc, char *argv[]) {
tokenize_extensions(dont_compress_str, dont_compress);
bool okflag = true;
if (create || append) {
if (create || append || update) {
okflag = add_files(argc, argv);
} else if (extract) {
okflag = extract_files(argc, argv);

View File

@ -294,7 +294,10 @@ set_scale_factor(size_t scale_factor) {
// Access: Published
// Description: Adds a file on disk as a subfile to the Multifile.
// The file named by filename will be read and added to
// the Multifile at the next call to flush().
// the Multifile at the next call to flush(). If there
// already exists a subfile with the indicated name, it
// is replaced without examining its contents (but see
// also update_subfile).
//
// Returns the subfile name on success (it might have
// been modified slightly), or empty string on failure.
@ -307,11 +310,58 @@ add_subfile(const string &subfile_name, const Filename &filename,
if (!filename.exists()) {
return string();
}
Subfile *subfile = new Subfile;
subfile->_source_filename = filename;
subfile->_source_filename.set_binary();
string name = standardize_subfile_name(subfile_name);
if (!name.empty()) {
Subfile *subfile = new Subfile;
subfile->_name = name;
subfile->_source_filename = filename;
subfile->_source_filename.set_binary();
add_new_subfile(subfile, compression_level);
}
return add_new_subfile(subfile_name, subfile, compression_level);
return name;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::update_subfile
// Access: Published
// Description: Adds a file on disk to the subfile. If a subfile
// already exists with the same name, its contents are
// compared to the disk file, and it is replaced only if
// it is different; otherwise, the multifile is left
// unchanged.
////////////////////////////////////////////////////////////////////
string Multifile::
update_subfile(const string &subfile_name, const Filename &filename,
int compression_level) {
nassertr(is_write_valid(), string());
if (!filename.exists()) {
return string();
}
string name = standardize_subfile_name(subfile_name);
if (!name.empty()) {
int index = find_subfile(name);
if (index >= 0) {
// The subfile already exists; compare it to the source file.
if (compare_subfile(index, filename)) {
// The files are identical; do nothing.
return name;
}
}
// The subfile does not already exist or it is different from the
// source file. Add the new source file.
Subfile *subfile = new Subfile;
subfile->_name = name;
subfile->_source_filename = filename;
subfile->_source_filename.set_binary();
add_new_subfile(subfile, compression_level);
}
return name;
}
////////////////////////////////////////////////////////////////////
@ -545,7 +595,7 @@ get_num_subfiles() const {
int Multifile::
find_subfile(const string &subfile_name) const {
Subfile find_subfile;
find_subfile._name = subfile_name;
find_subfile._name = standardize_subfile_name(subfile_name);
Subfiles::const_iterator fi;
fi = _subfiles.find(&find_subfile);
if (fi == _subfiles.end()) {
@ -590,7 +640,7 @@ has_directory(const string &subfile_name) const {
// Access: Published
// Description: Considers subfile_name to be the name of a
// subdirectory within the Multifile, but not a file
// itself; ills the given vector up with the sorted list
// itself; fills the given vector up with the sorted list
// of subdirectories or files within the named
// directory.
//
@ -808,6 +858,57 @@ extract_subfile(int index, const Filename &filename) {
return extract_subfile_to(index, out);
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::compare_subfile
// Access: Published
// Description: Performs a byte-for-byte comparison of the indicated
// file on disk with the nth subfile. Returns true if
// the files are equivalent, or false if they are
// different (or the file is missing).
////////////////////////////////////////////////////////////////////
bool Multifile::
compare_subfile(int index, const Filename &filename) {
nassertr(is_read_valid(), false);
nassertr(index >= 0 && index < (int)_subfiles.size(), false);
if (!filename.exists()) {
express_cat.info()
<< "File is missing: " << filename << "\n";
return false;
}
istream *in1 = open_read_subfile(index);
if (in1 == (istream *)NULL) {
return false;
}
ifstream in2;
Filename bin_filename = Filename::binary_filename(filename);
if (!bin_filename.open_read(in2)) {
express_cat.info()
<< "Cannot read " << filename << "\n";
return false;
}
int byte1 = in1->get();
int byte2 = in2.get();
while (!in1->fail() && !in1->eof() &&
!in2.fail() && !in2.eof()) {
if (byte1 != byte2) {
delete in1;
return false;
}
byte1 = in1->get();
byte2 = in2.get();
}
bool failed = (in1->fail() && !in1->eof()) || (in2.fail() && !in2.eof());
delete in1;
nassertr(!failed, false);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::output
// Access: Published
@ -928,7 +1029,7 @@ open_read_write(iostream *multifile_stream) {
////////////////////////////////////////////////////////////////////
// Function: Multifile::add_subfile
// Access: Public
// Description: Adds a file on disk as a subfile to the Multifile.
// Description: Adds a file from a stream as a subfile to the Multifile.
// The indicated istream will be read and its contents
// added to the Multifile at the next call to flush().
//
@ -940,10 +1041,15 @@ add_subfile(const string &subfile_name, istream *subfile_data,
int compression_level) {
nassertr(is_write_valid(), string());
Subfile *subfile = new Subfile;
subfile->_source = subfile_data;
string name = standardize_subfile_name(subfile_name);
if (!name.empty()) {
Subfile *subfile = new Subfile;
subfile->_name = name;
subfile->_source = subfile_data;
add_new_subfile(subfile, compression_level);
}
return add_new_subfile(subfile_name, subfile, compression_level);
return name;
}
////////////////////////////////////////////////////////////////////
@ -1001,9 +1107,8 @@ pad_to_streampos(streampos fpos) {
// Description: Adds a newly-allocated Subfile pointer to the
// Multifile.
////////////////////////////////////////////////////////////////////
string Multifile::
add_new_subfile(const string &subfile_name, Subfile *subfile,
int compression_level) {
void Multifile::
add_new_subfile(Subfile *subfile, int compression_level) {
if (compression_level != 0) {
#ifndef HAVE_ZLIB
express_cat.warning()
@ -1021,20 +1126,6 @@ add_new_subfile(const string &subfile_name, Subfile *subfile,
_needs_repack = true;
}
// Normalize the Subfile name: eliminate ./, leading slash, etc.
Filename name = subfile_name;
name.standardize();
if (name.empty() || name == "/") {
// Invalid empty name.
return string();
}
if (name[0] == '/') {
subfile->_name = name.get_fullpath().substr(1);
} else {
subfile->_name = name;
}
pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
if (!insert_result.second) {
// Hmm, unable to insert. There must already be a subfile by that
@ -1046,7 +1137,27 @@ add_new_subfile(const string &subfile_name, Subfile *subfile,
}
_new_subfiles.push_back(subfile);
return subfile->_name;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::standardize_subfile_name
// Access: Private
// Description: Returns the standard form of the subfile name.
////////////////////////////////////////////////////////////////////
string Multifile::
standardize_subfile_name(const string &subfile_name) const {
Filename name = subfile_name;
name.standardize();
if (name.empty() || name == "/") {
// Invalid empty name.
return string();
}
if (name[0] == '/') {
return name.get_fullpath().substr(1);
} else {
return name.get_fullpath();
}
}
////////////////////////////////////////////////////////////////////

View File

@ -57,6 +57,8 @@ PUBLISHED:
string add_subfile(const string &subfile_name, const Filename &filename,
int compression_level);
string update_subfile(const string &subfile_name, const Filename &filename,
int compression_level);
bool flush();
bool repack();
@ -74,6 +76,7 @@ PUBLISHED:
INLINE string read_subfile(int index);
istream *open_read_subfile(int index);
bool extract_subfile(int index, const Filename &filename);
bool compare_subfile(int index, const Filename &filename);
void output(ostream &out) const;
void ls(ostream &out = cout) const;
@ -129,8 +132,9 @@ private:
INLINE streampos normalize_streampos(streampos fpos) const;
streampos pad_to_streampos(streampos fpos);
string add_new_subfile(const string &subfile_name, Subfile *subfile,
int compression_level);
void add_new_subfile(Subfile *subfile, int compression_level);
string standardize_subfile_name(const string &subfile_name) const;
void clear_subfiles();
bool read_index();
bool write_header();