delete_lha

raw_lzh_file_sourceとraw_lzh_file_sinkを使って、LZHファイルからエントリを削除するプログラムを作ってみました。

#include <hamigaki/iostreams/device/raw_lzh_file.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/iostreams/copy.hpp>
#include <clocale>
#include <exception>
#include <iostream>

namespace io_ex = hamigaki::iostreams;
namespace fs = boost::filesystem;
namespace io = boost::iostreams;

// パスparentがパスchildの親ディレクトリか?
bool is_parent_of(const fs::path& parent, const fs::path& child)
{
    typedef fs::path::iterator iter_type;

    iter_type p_beg = parent.begin();
    iter_type p_end = parent.end();

    iter_type c_beg = child.begin();
    iter_type c_end = child.end();

    while ((p_beg != p_end) && (c_beg != c_end))
    {
        if (*p_beg != *c_beg)
            return false;

        ++p_beg;
        ++c_beg;
    }

    return (c_beg != c_end);
}

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 3)
        {
            std::cerr << "Usage: delete_lha (LZH file) (filename)" << std::endl;
            return 1;
        }

        std::setlocale(LC_ALL, "");
        fs::path::default_name_check(fs::no_check);

        fs::path lzh_name(argv[1], fs::native);

        // 元ファイルを拡張子「.bak」にリネーム
        const fs::path& bak_name = change_extension(lzh_name, ".bak");
        rename(lzh_name, bak_name);

        fs::path del_name(argv[2], fs::native);

        {
            io_ex::raw_lzh_file_source src(bak_name.native_file_string());
            io_ex::raw_lzh_file_sink sink(lzh_name.native_file_string());

            while (src.next_entry())
            {
                const io_ex::lha::header& head = src.header();

                // パスが一致するか、削除するディレクトリの配下ならスキップ
                if ((head.path == del_name) || is_parent_of(del_name,head.path))
                    continue;

                sink.create_entry(head);

                if (head.compressed_size != 0)
                    io::copy(src, sink);
            }
            sink.close_archive();
        }

        // 成功したのでバックアップを削除
        remove(bak_name);
        return 0;
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 1;
}

結果、Level1/2ヘッダなら問題なく動きました。
一方、Level0ヘッダはファイルのCRCが付いていない場合があり、raw_lzh_file_sink(Level2ヘッダ固定CRC必須)が出力する際にエラーになります。
CRCを計算するためには一旦デコードするほかなく、無駄の多い処理になります。
現実的な解決策としては、Level0/1ヘッダでの出力をサポートして、コピー元のエントリと同じ形式で出力することになります。
Level0/1ヘッダでの出力をTODOリストに追加です。