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リストに追加です。