アーカイブの更新

ようやく追記の問題が解決しました。
これまで、lzh_file_append_sinkのような追記用のクラスを用意して、ファイルが存在すれば追記モード、なければlzh_file_sinkと同じ動作といったものを想像していました。

ar::lzh_file_append_sink sink("test.lzh");
append_lzh(sink, "abc.txt");

しかし、実際にはほとんどのアーカイバは常に新しいファイルに書き出して、成功したら元のファイルと置き換える、ということをしています。
また、追記では同名のファイルが既に追加されている場合、同じファイル名のエントリが複数存在することになります。
既存エントリを更新することを考慮すると、

  1. raw_lzh_file_sourceから順にエントリを読み出す
  2. ファイル名を確認して更新対象のファイルでなければ、圧縮済みのデータをそのまま書き出す
  3. 全てのエントリを読み終わったら、更新対象ファイルを新規にエントリに圧縮しながら書き出す

という操作が必要です。
新規作成の場合は、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に至っては圧縮がないため、修正不要です。