ファイルなしでも開ける様に+:oコマンドの追加
諏訪子 <suwako@076.moe>、2026-05-03 20:08
コミット 6415199b8ce3c4d54d0b2b07aaa3f5bbd480db07
・親 b510b28
・ファイルを見る
4個のファイル、+116 -32
変更
CHANGELOG.md
| @@ -1,6 +1,8 @@ | ||
| 1 | 1 | ## 1.2.0 (2026年05月03日) |
| 2 | 2 | * Debianでコンパイル出来る様に |
| 3 | 3 | * マウス対応の追加 |
| 4 | +* ファイルなしでも開ける様に | |
| 5 | +* `:o`コマンドの追加 | |
| 4 | 6 | |
| 5 | 7 | ## 1.1.0 (2025年12月28日) |
| 6 | 8 | * PgUp・PgDownで使ってページを動ける様に |
変更
main.cc
| @@ -39,13 +39,13 @@ | ||
| 39 | 39 | #include "src/hexeditor.hh" |
| 40 | 40 | |
| 41 | 41 | int main(int argc, char *argv[]) { |
| 42 | - if (argc != 2) { | |
| 43 | - std::cerr << "usage: hexagon <filename>\n"; | |
| 44 | - return 1; | |
| 45 | - } | |
| 42 | + std:: string filename; | |
| 46 | 43 | |
| 44 | + if (argc > 1) filename = argv[1]; | |
| 45 | + else filename = ""; | |
| 46 | + | |
| 47 | 47 | try { |
| 48 | - HexEditor editor(argv[1]); | |
| 48 | + HexEditor editor(filename); | |
| 49 | 49 | editor.run(); |
| 50 | 50 | } catch (const std::exception &e) { |
| 51 | 51 | std::cerr << "エラー: " << e.what() << "\n"; |
| @@ -80,7 +80,7 @@ | ||
| 80 | 80 | HexEditor::HexEditor(const std::string &filename) |
| 81 | 81 | : curPos(0), dpOffset(0), bpr(16), |
| 82 | 82 | statusMode(Status_Normal), modified(false), |
| 83 | - running(true), | |
| 83 | + running(true), hasFile(!filename.empty()), | |
| 84 | 84 | lastSearchDir(Direction_Forward), |
| 85 | 85 | headerLen(0), headerType("") { |
| 86 | 86 | // SIGINT (CTRL + C)を無効に |
| @@ -123,39 +123,46 @@ | ||
| 123 | 123 | refresh(); |
| 124 | 124 | |
| 125 | 125 | // ファイルをバッファーに読み込む |
| 126 | - std::ifstream file(filename, std::ios::binary); | |
| 127 | - if (!file) { | |
| 128 | - endwin(); | |
| 129 | - throw std::runtime_error("ファイルを開くに失敗"); | |
| 130 | - } | |
| 126 | + if (hasFile) { | |
| 127 | + std::ifstream file(filename, std::ios::binary); | |
| 128 | + if (!file) { | |
| 129 | + endwin(); | |
| 130 | + throw std::runtime_error("ファイルを開くに失敗"); | |
| 131 | + } | |
| 131 | 132 | |
| 132 | - buf.assign((std::istreambuf_iterator<char>(file)), {}); | |
| 133 | - file.close(); | |
| 133 | + buf.assign((std::istreambuf_iterator<char>(file)), {}); | |
| 134 | + file.close(); | |
| 134 | 135 | |
| 135 | - if (buf.empty()) { | |
| 136 | - endwin(); | |
| 137 | - throw std::runtime_error("ファイルが空です"); | |
| 138 | - } | |
| 136 | + if (buf.empty()) { | |
| 137 | + endwin(); | |
| 138 | + throw std::runtime_error("ファイルが空です"); | |
| 139 | + } | |
| 139 | 140 | |
| 140 | - fname = filename; | |
| 141 | + fname = filename; | |
| 142 | + hasFile = true; | |
| 141 | 143 | |
| 142 | - // ファイルヘッダー | |
| 143 | - for (const auto &sig : signatures) { | |
| 144 | - if (buf.size() >= sig.signature.size()) { | |
| 145 | - bool match = true; | |
| 146 | - for (size_t i = 0; i < sig.signature.size(); ++i) { | |
| 147 | - if (buf[i] != sig.signature[i]) { | |
| 148 | - match = false; | |
| 144 | + // ファイルヘッダー | |
| 145 | + for (const auto &sig : signatures) { | |
| 146 | + if (buf.size() >= sig.signature.size()) { | |
| 147 | + bool match = true; | |
| 148 | + for (size_t i = 0; i < sig.signature.size(); ++i) { | |
| 149 | + if (buf[i] != sig.signature[i]) { | |
| 150 | + match = false; | |
| 151 | + break; | |
| 152 | + } | |
| 153 | + } | |
| 154 | + | |
| 155 | + if (match) { | |
| 156 | + headerLen = sig.signature.size(); | |
| 157 | + headerType = sig.type; | |
| 149 | 158 | break; |
| 150 | 159 | } |
| 151 | 160 | } |
| 152 | - | |
| 153 | - if (match) { | |
| 154 | - headerLen = sig.signature.size(); | |
| 155 | - headerType = sig.type; | |
| 156 | - break; | |
| 157 | - } | |
| 158 | 161 | } |
| 162 | + } else { | |
| 163 | + fname = "[No File]"; | |
| 164 | + buf.clear(); | |
| 165 | + hasFile = false; | |
| 159 | 166 | } |
| 160 | 167 | |
| 161 | 168 | // ウィンドウを作成する |
| @@ -516,9 +523,13 @@ | ||
| 516 | 523 | } else if (statusText == "noh") { |
| 517 | 524 | lastSearch = ""; |
| 518 | 525 | isCommand = true; |
| 526 | + } else if (statusText.rfind("o ", 0) == 0 || statusText.rfind("open ", 0) == 0) { | |
| 527 | + std::string path = statusText.substr(statusText.find(' ') + 1); | |
| 528 | + handleOpen(path); | |
| 529 | + isCommand = true; | |
| 519 | 530 | } else { |
| 520 | 531 | statusMode = Status_Error; |
| 521 | - statusText.clear(); | |
| 532 | + statusText = "不正なコマンド。"; | |
| 522 | 533 | } |
| 523 | 534 | |
| 524 | 535 | if (isCommand) { |
| @@ -538,6 +549,67 @@ | ||
| 538 | 549 | } |
| 539 | 550 | } |
| 540 | 551 | |
| 552 | +void HexEditor::handleOpen(const std::string &path) { | |
| 553 | + if (path.empty()) { | |
| 554 | + statusMode = Status_Error; | |
| 555 | + statusText = "利用方法: :o <filename>"; | |
| 556 | + render(); | |
| 557 | + return; | |
| 558 | + } | |
| 559 | + | |
| 560 | + std::ifstream file(path, std::ios::binary); | |
| 561 | + if (!file) { | |
| 562 | + statusMode = Status_Error; | |
| 563 | + statusText = "ファイルを開くに失敗: " + path; | |
| 564 | + render(); | |
| 565 | + return; | |
| 566 | + } | |
| 567 | + | |
| 568 | + std::vector<uint8_t> newbuf((std::istreambuf_iterator<char>(file)), {}); | |
| 569 | + file.close(); | |
| 570 | + | |
| 571 | + if (newbuf.empty()) { | |
| 572 | + statusMode = Status_Error; | |
| 573 | + statusText = "ファイルは空です。"; | |
| 574 | + render(); | |
| 575 | + return; | |
| 576 | + } | |
| 577 | + | |
| 578 | + buf = std::move(newbuf); | |
| 579 | + fname = path; | |
| 580 | + hasFile = true; | |
| 581 | + curPos = 0; | |
| 582 | + dpOffset = 0; | |
| 583 | + modified = false; | |
| 584 | + undoStack.clear(); | |
| 585 | + redoStack.clear(); | |
| 586 | + lastSearch.clear(); | |
| 587 | + lastHexSearch.clear(); | |
| 588 | + headerLen = 0; | |
| 589 | + headerType = ""; | |
| 590 | + | |
| 591 | + for (const auto &sig : signatures) { | |
| 592 | + if (buf.size() >= sig.signature.size()) { | |
| 593 | + bool match = true; | |
| 594 | + for (size_t i = 0; i < sig.signature.size(); ++i) { | |
| 595 | + if (buf[i] != sig.signature[i]) { | |
| 596 | + match = false; | |
| 597 | + break; | |
| 598 | + } | |
| 599 | + } | |
| 600 | + if (match) { | |
| 601 | + headerLen = sig.signature.size(); | |
| 602 | + headerType = sig.type; | |
| 603 | + break; | |
| 604 | + } | |
| 605 | + } | |
| 606 | + } | |
| 607 | + | |
| 608 | + statusMode = Status_Normal; | |
| 609 | + statusText = path + " を開きました。"; | |
| 610 | + render(); | |
| 611 | +} | |
| 612 | + | |
| 541 | 613 | void HexEditor::handleSave() { |
| 542 | 614 | std::ofstream file(fname, std::ios::binary); |
| 543 | 615 | if (file) { |
| @@ -862,6 +934,14 @@ | ||
| 862 | 934 | if (statusMode != Status_Normal && statusMode != Status_Error) continue; |
| 863 | 935 | |
| 864 | 936 | ch = getch(); |
| 937 | + | |
| 938 | + if (!hasFile && ch != ':' && ch != 'Z') { | |
| 939 | + statusMode = Status_Error; | |
| 940 | + statusText = "ファイルを読み込んでいません。「:o <file>」を御利用下さい。"; | |
| 941 | + render(); | |
| 942 | + return; | |
| 943 | + } | |
| 944 | + | |
| 865 | 945 | if ((ch == 'j' || ch == KEY_DOWN) && curPos + bpr < buf.size()) { |
| 866 | 946 | curPos += bpr; // 下 |
| 867 | 947 | if (statusMode == Status_Error) statusMode = Status_Normal; |
| @@ -79,6 +79,7 @@ | ||
| 79 | 79 | bool running; |
| 80 | 80 | std::string lastSearch; |
| 81 | 81 | bool isMouse = false; |
| 82 | + bool hasFile = false; | |
| 82 | 83 | |
| 83 | 84 | WINDOW *hexPanel; |
| 84 | 85 | WINDOW *asciiPanel; |
| @@ -102,6 +103,7 @@ | ||
| 102 | 103 | void topbar(); |
| 103 | 104 | void statusbar(); |
| 104 | 105 | void handleCommand(); |
| 106 | + void handleOpen(const std::string &path); | |
| 105 | 107 | void handleSave(); |
| 106 | 108 | void handleQuit(bool force); |
| 107 | 109 | void handleSearch(); |