アーカイブの更新
ようやく追記の問題が解決しました。
これまで、lzh_file_append_sinkのような追記用のクラスを用意して、ファイルが存在すれば追記モード、なければlzh_file_sinkと同じ動作といったものを想像していました。
ar::lzh_file_append_sink sink("test.lzh"); append_lzh(sink, "abc.txt");
しかし、実際にはほとんどのアーカイバは常に新しいファイルに書き出して、成功したら元のファイルと置き換える、ということをしています。
また、追記では同名のファイルが既に追加されている場合、同じファイル名のエントリが複数存在することになります。
既存エントリを更新することを考慮すると、
- raw_lzh_file_sourceから順にエントリを読み出す
- ファイル名を確認して更新対象のファイルでなければ、圧縮済みのデータをそのまま書き出す
- 全てのエントリを読み終わったら、更新対象ファイルを新規にエントリに圧縮しながら書き出す
という操作が必要です。
新規作成の場合は、3.だけ考えればよいわけですから、そもそも同じクラスで扱う必要もありません。
つまり、
if (fs::exists("test.lzh")) { ar::lzh_file_sink sink("tmp.lzh"); { // 圧縮イメージを展開せずにそのまま読む ar::raw_lzh_file_source src("test.lzh"); while (src.next_entry()) { // 対象ファイルでなければ、そのままコピー if (src.header().path != "abc.txt") copy_lzh_entry(src, sink); } } // 更新ファイルは最後に追加 append_lzh(sink, "abc.txt"); sink.close_archive(); // リネーム fs::remove("test.lzh"); fs::rename("tmp.lzh", "test.lzh"); } else { ar::lzh_file_sink sink("test.lzh"); append_lzh(sink, "abc.txt"); sink.close_archive(); }
とできればよいのです。
あとはcopy_lzh_entry()関数を実装するだけです。
これまでのlzh_file_sinkは圧縮メソッドに従って常に圧縮していたため不可能でしたが、圧縮済みデータを書き出すためのモードを追加しました。
圧縮済みの場合、圧縮後のサイズが分かっているので、これが設定されているかどうかをモード切替のフラグとしました。
このため、copy_lzh_entry()の実装は直感的で単純です。
void copy_lzh_entry(ar::raw_lzh_file_source& src, ar::lzh_file_sink& sink)
{
sink.create_entry(src.header());
io::copy(src, sink);
}
lzh_file_sink_impl.hppの差分
ZIPの場合は暗号化が絡んでくるので多少面倒ですが、基本的には同じ仕組みで実現できています。
raw_zip_file_sink_impl.hppの差分
zip_file_sink_impl.hppの差分
tarに至っては圧縮がないため、修正不要です。