ファイルなしでも開ける様に+:oコマンドの追加

諏訪子 <suwako@076.moe>、2026-05-03 20:08
コミット 6415199b8ce3c4d54d0b2b07aaa3f5bbd480db07 ・親 b510b28 ・ファイルを見る

4個のファイル、+116 -32

変更 CHANGELOG.md
@@ -1,6 +1,8 @@
11 ## 1.2.0 (2026年05月03日)
22 * Debianでコンパイル出来る様に
33 * マウス対応の追加
4+* ファイルなしでも開ける様に
5+* `:o`コマンドの追加
46
57 ## 1.1.0 (2025年12月28日)
68 * PgUp・PgDownで使ってページを動ける様に
変更 main.cc
@@ -39,13 +39,13 @@
3939 #include "src/hexeditor.hh"
4040
4141 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;
4643
44+ if (argc > 1) filename = argv[1];
45+ else filename = "";
46+
4747 try {
48- HexEditor editor(argv[1]);
48+ HexEditor editor(filename);
4949 editor.run();
5050 } catch (const std::exception &e) {
5151 std::cerr << "エラー: " << e.what() << "\n";
@@ -80,7 +80,7 @@
8080 HexEditor::HexEditor(const std::string &filename)
8181 : curPos(0), dpOffset(0), bpr(16),
8282 statusMode(Status_Normal), modified(false),
83- running(true),
83+ running(true), hasFile(!filename.empty()),
8484 lastSearchDir(Direction_Forward),
8585 headerLen(0), headerType("") {
8686 // SIGINT (CTRL + C)を無効に
@@ -123,39 +123,46 @@
123123 refresh();
124124
125125 // ファイルをバッファーに読み込む
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+ }
131132
132- buf.assign((std::istreambuf_iterator<char>(file)), {});
133- file.close();
133+ buf.assign((std::istreambuf_iterator<char>(file)), {});
134+ file.close();
134135
135- if (buf.empty()) {
136- endwin();
137- throw std::runtime_error("ファイルが空です");
138- }
136+ if (buf.empty()) {
137+ endwin();
138+ throw std::runtime_error("ファイルが空です");
139+ }
139140
140- fname = filename;
141+ fname = filename;
142+ hasFile = true;
141143
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;
149158 break;
150159 }
151160 }
152-
153- if (match) {
154- headerLen = sig.signature.size();
155- headerType = sig.type;
156- break;
157- }
158161 }
162+ } else {
163+ fname = "[No File]";
164+ buf.clear();
165+ hasFile = false;
159166 }
160167
161168 // ウィンドウを作成する
@@ -516,9 +523,13 @@
516523 } else if (statusText == "noh") {
517524 lastSearch = "";
518525 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;
519530 } else {
520531 statusMode = Status_Error;
521- statusText.clear();
532+ statusText = "不正なコマンド。";
522533 }
523534
524535 if (isCommand) {
@@ -538,6 +549,67 @@
538549 }
539550 }
540551
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+
541613 void HexEditor::handleSave() {
542614 std::ofstream file(fname, std::ios::binary);
543615 if (file) {
@@ -862,6 +934,14 @@
862934 if (statusMode != Status_Normal && statusMode != Status_Error) continue;
863935
864936 ch = getch();
937+
938+ if (!hasFile && ch != ':' && ch != 'Z') {
939+ statusMode = Status_Error;
940+ statusText = "ファイルを読み込んでいません。「:o <file>」を御利用下さい。";
941+ render();
942+ return;
943+ }
944+
865945 if ((ch == 'j' || ch == KEY_DOWN) && curPos + bpr < buf.size()) {
866946 curPos += bpr; // 下
867947 if (statusMode == Status_Error) statusMode = Status_Normal;
@@ -79,6 +79,7 @@
7979 bool running;
8080 std::string lastSearch;
8181 bool isMouse = false;
82+ bool hasFile = false;
8283
8384 WINDOW *hexPanel;
8485 WINDOW *asciiPanel;
@@ -102,6 +103,7 @@
102103 void topbar();
103104 void statusbar();
104105 void handleCommand();
106+ void handleOpen(const std::string &path);
105107 void handleSave();
106108 void handleQuit(bool force);
107109 void handleSearch();